Kaynağa Gözat

feat: 添加大队级别检查功能并支持Electron桌面应用

refactor(utils): 新增大队级别选项构建函数
feat(api): 添加查询部门指定角色人员接口
feat(pages): 在问题整改和检查单页面支持大队级别检查
feat(electron): 新增Electron桌面应用支持
build: 添加electron-builder配置和打包脚本
style(login): 调整验证码输入框布局
chore: 更新.gitignore忽略electron打包目录
huoyi 4 ay önce
ebeveyn
işleme
53b422724e

+ 1 - 0
.gitignore

@@ -3,6 +3,7 @@ node_modules/
3
 unpackage/
3
 unpackage/
4
 dist/
4
 dist/
5
 dist.zip
5
 dist.zip
6
+dist_electron
6
 CLAUDE.md
7
 CLAUDE.md
7
 # local env files
8
 # local env files
8
 .env.local
9
 .env.local

+ 44 - 0
electron-builder.json

@@ -0,0 +1,44 @@
1
+{
2
+  "appId": "com.meilan.app",
3
+  "productName": "美兰机场应用",
4
+  "directories": {
5
+    "output": "dist_electron",
6
+    "buildResources": "build"
7
+  },
8
+  "files": [
9
+    "dist/build/h5/**/*",
10
+    "electron/**/*",
11
+    "!**/node_modules/**/*"
12
+  ],
13
+  "extraResources": [
14
+    {
15
+      "from": "dist/build/h5",
16
+      "to": "app/dist/build/h5",
17
+      "filter": ["**/*"]
18
+    }
19
+  ],
20
+  "mac": {
21
+    "category": "public.app-category.productivity",
22
+    "icon": "dist/build/h5/static/pc.png"
23
+  },
24
+  "win": {
25
+    "target": [
26
+      {
27
+        "target": "nsis",
28
+        "arch": ["x64"]
29
+      }
30
+    ],
31
+    "icon": "dist/build/h5/static/pc.png"
32
+  },
33
+  "nsis": {
34
+    "oneClick": false,
35
+    "allowToChangeInstallationDirectory": true,
36
+    "createDesktopShortcut": true,
37
+    "createStartMenuShortcut": true,
38
+    "shortcutName": "美兰机场应用"
39
+  },
40
+  "publish": {
41
+    "provider": "generic",
42
+    "url": "http://your-update-server.com/updates"
43
+  }
44
+}

+ 176 - 0
electron/main.js

@@ -0,0 +1,176 @@
1
+const { app, BrowserWindow, Menu } = require('electron')
2
+const path = require('path')
3
+const isDev = process.env.NODE_ENV === 'development'
4
+
5
+// 保持对窗口对象的全局引用,避免被垃圾回收
6
+let mainWindow
7
+
8
+function createWindow() {
9
+  // 创建浏览器窗口
10
+  mainWindow = new BrowserWindow({
11
+    width: 600,
12
+    height: 800,
13
+    minWidth: 100,
14
+    minHeight: 300,
15
+    webPreferences: {
16
+      nodeIntegration: false,
17
+      contextIsolation: true,
18
+      webSecurity: false, // 禁用webSecurity以允许跨域请求
19
+      allowRunningInsecureContent: true, // 允许不安全内容
20
+      enableRemoteModule: false, // 禁用远程模块,提高安全性
21
+      webgl: true,
22
+      images: true
23
+    },
24
+    icon: path.join(__dirname, '../../dist/build/h5/static/pc.png'), // 应用图标
25
+    show: false // 先隐藏窗口,等加载完成再显示
26
+  })
27
+
28
+  // 设置Content Security Policy
29
+  mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
30
+    callback({
31
+      responseHeaders: {
32
+        ...details.responseHeaders,
33
+        'Content-Security-Policy': [
34
+          "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:; " +
35
+          "script-src * 'unsafe-inline' 'unsafe-eval'; " +
36
+          "connect-src * 'unsafe-inline'; " +
37
+          "img-src * data: blob: 'unsafe-inline'; " +
38
+          "frame-src *; " +
39
+          "style-src * 'unsafe-inline'; " +
40
+          "font-src * data:; " +
41
+          "media-src *"
42
+        ]
43
+      }
44
+    })
45
+  })
46
+
47
+  // 加载应用
48
+  if (isDev) {
49
+    // 开发环境:加载本地H5开发服务器
50
+    mainWindow.loadURL('http://localhost:9090')
51
+    // 打开开发者工具
52
+    mainWindow.webContents.openDevTools()
53
+  } else {
54
+    // 生产环境:加载打包后的文件
55
+    const fs = require('fs')
56
+    let indexPath
57
+    
58
+    // 尝试多种可能的路径
59
+    const possiblePaths = [
60
+      // 打包后路径1:resources/app/dist/build/h5
61
+      process.resourcesPath ? path.join(process.resourcesPath, 'app', 'dist', 'build', 'h5', 'index.html') : null,
62
+      // 打包后路径2:resources/app
63
+      process.resourcesPath ? path.join(process.resourcesPath, 'app', 'index.html') : null,
64
+      // 打包后路径3:直接位于应用目录
65
+      path.join(__dirname, '..', 'dist', 'build', 'h5', 'index.html'),
66
+      // 开发环境路径
67
+      path.join(__dirname, '..', '..', 'dist', 'build', 'h5', 'index.html'),
68
+      // 备用路径
69
+      path.join(__dirname, 'dist', 'build', 'h5', 'index.html')
70
+    ].filter(Boolean)
71
+    
72
+    // 查找存在的文件
73
+    for (const testPath of possiblePaths) {
74
+      console.log('Testing path:', testPath)
75
+      if (fs.existsSync(testPath)) {
76
+        indexPath = testPath
77
+        console.log('Found index file at:', indexPath)
78
+        break
79
+      }
80
+    }
81
+    
82
+    if (indexPath && fs.existsSync(indexPath)) {
83
+      // 使用 loadFile 方法加载文件
84
+      mainWindow.loadFile(indexPath)
85
+      console.log('Successfully loaded index file from:', indexPath)
86
+    } else {
87
+      console.error('Index file not found in any of the following paths:')
88
+      possiblePaths.forEach(p => console.error('  -', p))
89
+      
90
+      // 显示详细的错误信息
91
+      const errorHtml = `
92
+        <html>
93
+          <head><title>应用加载失败</title></head>
94
+          <body style="font-family: Arial, sans-serif; padding: 20px;">
95
+            <h1>应用文件加载失败</h1>
96
+            <p>无法找到 index.html 文件,请检查以下路径:</p>
97
+            <ul>
98
+              ${possiblePaths.map(p => `<li>${p}</li>`).join('')}
99
+            </ul>
100
+            <p>请重新安装应用或联系技术支持。</p>
101
+          </body>
102
+        </html>
103
+      `
104
+      mainWindow.loadURL(`data:text/html,${encodeURIComponent(errorHtml)}`)
105
+    }
106
+  }
107
+
108
+  // 窗口准备好后显示
109
+  mainWindow.once('ready-to-show', () => {
110
+    mainWindow.show()
111
+  })
112
+
113
+  // 窗口关闭时触发
114
+  mainWindow.on('closed', () => {
115
+    mainWindow = null
116
+  })
117
+
118
+  // 设置菜单(可选)
119
+  const template = [
120
+    {
121
+      label: '文件',
122
+      submenu: [
123
+        {
124
+          label: '退出',
125
+          accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q',
126
+          click() {
127
+            app.quit()
128
+          }
129
+        }
130
+      ]
131
+    },
132
+    {
133
+      label: '视图',
134
+      submenu: [
135
+        { role: 'reload', label: '重新加载' },
136
+        { role: 'forceReload', label: '强制重新加载' },
137
+        { role: 'toggleDevTools', label: '开发者工具' },
138
+        { type: 'separator' },
139
+        { role: 'resetZoom', label: '实际大小' },
140
+        { role: 'zoomIn', label: '放大' },
141
+        { role: 'zoomOut', label: '缩小' },
142
+        { type: 'separator' },
143
+        { role: 'togglefullscreen', label: '切换全屏' }
144
+      ]
145
+    }
146
+  ]
147
+
148
+  const menu = Menu.buildFromTemplate(template)
149
+  Menu.setApplicationMenu(menu)
150
+}
151
+
152
+// Electron 初始化完成时触发
153
+app.whenReady().then(createWindow)
154
+
155
+// 所有窗口关闭时退出应用(macOS除外)
156
+app.on('window-all-closed', () => {
157
+  if (process.platform !== 'darwin') {
158
+    app.quit()
159
+  }
160
+})
161
+
162
+// macOS 应用激活时重新创建窗口
163
+app.on('activate', () => {
164
+  if (BrowserWindow.getAllWindows().length === 0) {
165
+    createWindow()
166
+  }
167
+})
168
+
169
+// 安全设置:阻止新窗口创建
170
+app.on('web-contents-created', (event, contents) => {
171
+  contents.on('new-window', (event, navigationUrl) => {
172
+    event.preventDefault()
173
+    // 在默认浏览器中打开外部链接
174
+    require('electron').shell.openExternal(navigationUrl)
175
+  })
176
+})

+ 42 - 0
package.json

@@ -1,6 +1,8 @@
1
 {
1
 {
2
   "name": "ruoyi-app-project",
2
   "name": "ruoyi-app-project",
3
   "version": "1.0.0",
3
   "version": "1.0.0",
4
+  "description": "美兰机场移动应用 - 基于uni-app开发的跨平台应用",
5
+  "author": "美兰机场技术团队",
4
   "private": true,
6
   "private": true,
5
   "scripts": {
7
   "scripts": {
6
     "dev": "npm run dev:h5",
8
     "dev": "npm run dev:h5",
@@ -8,6 +10,10 @@
8
     "build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
10
     "build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
9
     "build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
11
     "build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
10
     "build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build",
12
     "build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build",
13
+    "electron:dev": "cross-env NODE_ENV=development electron electron/main.js",
14
+    "electron:build": "npm run build:h5 && electron-builder",
15
+    "electron:pack": "npm run build:h5 && electron-builder --dir",
16
+    "electron:dist": "npm run build:h5 && electron-builder --win --x64",
11
     "build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
17
     "build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
12
     "build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
18
     "build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
13
     "build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
19
     "build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
@@ -95,6 +101,8 @@
95
     "autoprefixer": "^8.0.0",
101
     "autoprefixer": "^8.0.0",
96
     "babel-plugin-import": "^1.11.0",
102
     "babel-plugin-import": "^1.11.0",
97
     "cross-env": "^7.0.2",
103
     "cross-env": "^7.0.2",
104
+    "electron": "^19.0.0",
105
+    "electron-builder": "^23.0.0",
98
     "jest": "^25.4.0",
106
     "jest": "^25.4.0",
99
     "postcss-comment": "^2.0.0",
107
     "postcss-comment": "^2.0.0",
100
     "postcss-loader": "^8.1.1",
108
     "postcss-loader": "^8.1.1",
@@ -108,5 +116,39 @@
108
   ],
116
   ],
109
   "uni-app": {
117
   "uni-app": {
110
     "scripts": {}
118
     "scripts": {}
119
+  },
120
+  "main": "electron/main.js",
121
+  "homepage": "./",
122
+  "build": {
123
+    "appId": "com.meilan.app",
124
+    "productName": "美兰机场应用",
125
+    "directories": {
126
+      "output": "dist_electron"
127
+    },
128
+    "electronDownload": {
129
+      "mirror": "https://npmmirror.com/mirrors/electron/"
130
+    },
131
+    "files": [
132
+      "dist/build/h5/**/*",
133
+      "electron/**/*"
134
+    ],
135
+    "win": {
136
+      "target": [
137
+        {
138
+          "target": "nsis",
139
+          "arch": [
140
+            "x64"
141
+          ]
142
+        }
143
+      ],
144
+      "icon": "dist/build/h5/static/pc.png"
145
+    },
146
+    "nsis": {
147
+      "oneClick": false,
148
+      "allowToChangeInstallationDirectory": true,
149
+      "createDesktopShortcut": true,
150
+      "createStartMenuShortcut": true,
151
+      "shortcutName": "美兰机场应用"
152
+    }
111
   }
153
   }
112
 }
154
 }

+ 8 - 0
src/api/check/checklist.js

@@ -67,4 +67,12 @@ export function getChecklistStatistics(params) {
67
     method: 'get',
67
     method: 'get',
68
     params: params
68
     params: params
69
   })
69
   })
70
+}
71
+//查询部门指定角色人员
72
+export function getDeptRoleUser(deptId,data) {
73
+  return request({
74
+    url: `/system/dept/deptRole/${deptId}`,
75
+    method: 'post',
76
+    data: data
77
+  })
70
 }
78
 }

+ 3 - 3
src/config.js

@@ -1,11 +1,11 @@
1
 // 应用全局配置
1
 // 应用全局配置
2
 module.exports = {
2
 module.exports = {
3
    // 接口地址  本地调试使用 http://192.168.3.222:38080  打包使用 /prod-api
3
    // 接口地址  本地调试使用 http://192.168.3.222:38080  打包使用 /prod-api
4
-  //  baseUrl: process.env.NODE_ENV === 'development' ? 'http://192.168.3.221:82/prod-api' : '/prod-api', //生产
4
+ //  baseUrl: process.env.NODE_ENV === 'development' ? 'http://192.168.3.221:82/prod-api' : '/prod-api', //生产
5
   //  baseUrl: process.env.NODE_ENV === 'development' ? 'http://192.168.3.221:8080' : '/prod-api',
5
   //  baseUrl: process.env.NODE_ENV === 'development' ? 'http://192.168.3.221:8080' : '/prod-api',
6
 // baseUrl: process.env.NODE_ENV === 'development' ? 'http://guangxi.qinghe.sundot.cn:8088' : 'http://guangxi.qinghe.sundot.cn:8088/prod-api',
6
 // baseUrl: process.env.NODE_ENV === 'development' ? 'http://guangxi.qinghe.sundot.cn:8088' : 'http://guangxi.qinghe.sundot.cn:8088/prod-api',
7
-   baseUrl: 'http://guangxi.qinghe.sundot.cn:8088',
8
-
7
+// baseUrl: 'http://guangxi.qinghe.sundot.cn:8088',
8
+    baseUrl:'http://airport.samsundot.com:9021/prod-api',
9
   // 应用信息
9
   // 应用信息
10
   appInfo: {
10
   appInfo: {
11
     // 应用名称
11
     // 应用名称

+ 32 - 3
src/pages/checklist/index.vue

@@ -27,9 +27,13 @@
27
                                 <uni-data-picker v-if="getCheckLabel == '被检查科'" :localdata="departments"
27
                                 <uni-data-picker v-if="getCheckLabel == '被检查科'" :localdata="departments"
28
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedDepartmentId"
28
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedDepartmentId"
29
                                     @change="handlecheckedDepartmentIdChange" :readonly="formDisabled" />
29
                                     @change="handlecheckedDepartmentIdChange" :readonly="formDisabled" />
30
+                                <uni-data-picker v-if="getCheckLabel == '被检查大队'" :localdata="brigades"
31
+                                    :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedBrigadeId"
32
+                                    @change="handlecheckedBrigadeIdChange" :readonly="formDisabled" />
30
                                 <uni-data-picker v-if="getCheckLabel == '被检查班组'" :localdata="teams"
33
                                 <uni-data-picker v-if="getCheckLabel == '被检查班组'" :localdata="teams"
31
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedTeamId"
34
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedTeamId"
32
                                     @change="handlecheckedTeamIdChange" :readonly="formDisabled" />
35
                                     @change="handlecheckedTeamIdChange" :readonly="formDisabled" />
36
+
33
                                 <fuzzy-select v-if="getCheckLabel == '被检查人'" v-model="formData.checkedPersonnelId"
37
                                 <fuzzy-select v-if="getCheckLabel == '被检查人'" v-model="formData.checkedPersonnelId"
34
                                     :options="userOptions" placeholder="请输入被检查人姓名搜索" data-value="userId"
38
                                     :options="userOptions" placeholder="请输入被检查人姓名搜索" data-value="userId"
35
                                     data-text="nickName" @change="handleCheckedSelect" :disabled="formDisabled" />
39
                                     data-text="nickName" @change="handleCheckedSelect" :disabled="formDisabled" />
@@ -138,8 +142,9 @@ import TextSwitch from "@/components/text-switch/text-switch.vue"
138
 import { checkedLevelEnums } from "@/utils/enums.js"
142
 import { checkedLevelEnums } from "@/utils/enums.js"
139
 import UnqualifiedPersonnel from "./components/unqualified.vue"
143
 import UnqualifiedPersonnel from "./components/unqualified.vue"
140
 import { treeSelectByType } from "@/api/system/common"
144
 import { treeSelectByType } from "@/api/system/common"
141
-import { buildTeamOptions, uploadFile, buildDepartmentOptions } from '@/utils/common'
145
+import { buildTeamOptions, uploadFile, buildDepartmentOptions, buildBrigadeOptions } from '@/utils/common'
142
 import useDictMixin from '@/utils/dict'
146
 import useDictMixin from '@/utils/dict'
147
+import { getDeptRoleUser } from "@/api/check/checklist.js"
143
 import { addDraftInspection, submitInspection, getInspectionListById } from '@/api/check/checkReward.js'
148
 import { addDraftInspection, submitInspection, getInspectionListById } from '@/api/check/checkReward.js'
144
 import SubmitResult from './components/SubmitResult.vue'
149
 import SubmitResult from './components/SubmitResult.vue'
145
 import { selectDeptLeaderByUserId } from '@/api/approve/approve.js'
150
 import { selectDeptLeaderByUserId } from '@/api/approve/approve.js'
@@ -196,6 +201,9 @@ export default {
196
             if (this.formData.checkedLevel == checkedLevelEnums.DEPARTMENT_LEVEL) {
201
             if (this.formData.checkedLevel == checkedLevelEnums.DEPARTMENT_LEVEL) {
197
                 return '被检查科'
202
                 return '被检查科'
198
             }
203
             }
204
+            if (this.formData.checkedLevel == checkedLevelEnums.BRIGADE_LEVEL) {
205
+                return '被检查大队'
206
+            }
199
         },
207
         },
200
         // 验证规则
208
         // 验证规则
201
         rules() {
209
         rules() {
@@ -254,6 +262,7 @@ export default {
254
             teams: [],//被检查班组选项
262
             teams: [],//被检查班组选项
255
             departments: [],//被检查科选项
263
             departments: [],//被检查科选项
256
             userOptions: [],//全部的人
264
             userOptions: [],//全部的人
265
+            brigades: [],
257
             checkcheckedTeamIdOptions: [],
266
             checkcheckedTeamIdOptions: [],
258
             // 表单数据
267
             // 表单数据
259
             formData: {
268
             formData: {
@@ -376,10 +385,12 @@ export default {
376
             this.formData.channelName = res[2]?.text;
385
             this.formData.channelName = res[2]?.text;
377
             this.formData.channelCode = res[2]?.value;
386
             this.formData.channelCode = res[2]?.value;
378
         },
387
         },
379
-        // 检查科/班组选择
388
+        // 检查班组选择
380
         handlecheckedTeamIdChange(e) {
389
         handlecheckedTeamIdChange(e) {
381
             const { text, value } = e.detail.value[0];
390
             const { text, value } = e.detail.value[0];
382
             let newText = text.split('/')
391
             let newText = text.split('/')
392
+            this.$set(this.formData, 'checkedDeptId', value);
393
+            this.$set(this.formData, 'checkedDeptName', newText[newText.length - 1].trim());
383
             this.$set(this.formData, 'checkedTeamName', newText[newText.length - 1]);
394
             this.$set(this.formData, 'checkedTeamName', newText[newText.length - 1]);
384
 
395
 
385
             // 在树状结构中查找指定id的上一级id
396
             // 在树状结构中查找指定id的上一级id
@@ -430,6 +441,8 @@ export default {
430
             const { text, value } = e.detail.value[0];
441
             const { text, value } = e.detail.value[0];
431
 
442
 
432
             let newText = text.split('/')
443
             let newText = text.split('/')
444
+            this.$set(this.formData, 'checkedDeptId', value);
445
+            this.$set(this.formData, 'checkedDeptName', newText[newText.length - 1].trim());
433
             this.$set(this.formData, 'checkedDepartmentName', newText[newText.length - 1].trim());
446
             this.$set(this.formData, 'checkedDepartmentName', newText[newText.length - 1].trim());
434
             getDeptManager(value).then(res => {
447
             getDeptManager(value).then(res => {
435
                 console.log(res?.data?.userId, "111res")
448
                 console.log(res?.data?.userId, "111res")
@@ -437,6 +450,20 @@ export default {
437
                 this.$set(this.formData, 'responsibleUserName', res?.data?.nickName || '');
450
                 this.$set(this.formData, 'responsibleUserName', res?.data?.nickName || '');
438
             })
451
             })
439
         },
452
         },
453
+        handlecheckedBrigadeIdChange(e) {
454
+            const { text, value } = e.detail.value[0];
455
+            let newText = text.split('/')
456
+            this.$set(this.formData, 'checkedBrigadeName', newText[newText.length - 1].trim());
457
+            this.$set(this.formData, 'checkedDeptId', value);
458
+            this.$set(this.formData, 'checkedDeptName', newText[newText.length - 1].trim());
459
+            this.$set(this.formData, 'checkedDepartmentId', value);
460
+            this.$set(this.formData, 'checkedDepartmentName', newText[newText.length - 1].trim());
461
+            getDeptRoleUser(value, ['xingzheng']).then(res => {
462
+
463
+                this.$set(this.formData, 'responsibleUserId', res?.data[0].userId || '');
464
+                this.$set(this.formData, 'responsibleUserName', res?.data[0].nickName || '');
465
+            })
466
+        },
440
         // 加载字典数据
467
         // 加载字典数据
441
         async loadDictData() {
468
         async loadDictData() {
442
             const user = await listAllUser();
469
             const user = await listAllUser();
@@ -448,6 +475,7 @@ export default {
448
             this.deptTree = deptTree.data || [];
475
             this.deptTree = deptTree.data || [];
449
             this.teams = buildTeamOptions(deptTree.data || []);
476
             this.teams = buildTeamOptions(deptTree.data || []);
450
             this.departments = buildDepartmentOptions(deptTree.data || []);
477
             this.departments = buildDepartmentOptions(deptTree.data || []);
478
+            this.brigades = buildBrigadeOptions(deptTree.data || []);
451
             console.log(this.departments, "this.departments")
479
             console.log(this.departments, "this.departments")
452
             const [positionRes] = await Promise.all([
480
             const [positionRes] = await Promise.all([
453
                 treeSelectByType("POSITION", 3),
481
                 treeSelectByType("POSITION", 3),
@@ -654,7 +682,7 @@ export default {
654
 
682
 
655
             let payload = {
683
             let payload = {
656
                 ...this.formData,
684
                 ...this.formData,
657
-                checkedTeamName: (this.formData.checkedTeamName|| '').trim() ,
685
+                checkedTeamName: (this.formData.checkedTeamName || '').trim(),
658
                 checkProjectItemList: originalCheckProjectItemList
686
                 checkProjectItemList: originalCheckProjectItemList
659
             };
687
             };
660
 
688
 
@@ -724,6 +752,7 @@ export default {
724
                 uni.showLoading({ title: '提交中...', mask: true });
752
                 uni.showLoading({ title: '提交中...', mask: true });
725
 
753
 
726
                 const payload = this.formateData();
754
                 const payload = this.formateData();
755
+                debugger
727
 
756
 
728
                 submitInspection({ ...payload, ...(this.type == 'task' ? {} : { id: this.taskId }) })
757
                 submitInspection({ ...payload, ...(this.type == 'task' ? {} : { id: this.taskId }) })
729
                     .then(() => {
758
                     .then(() => {

+ 16 - 7
src/pages/login.vue

@@ -17,13 +17,17 @@
17
           <view class="iconfont icon-password icon"></view>
17
           <view class="iconfont icon-password icon"></view>
18
           <input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
18
           <input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
19
         </view>
19
         </view>
20
-        <view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
21
-          <view class="iconfont icon-code icon"></view>
22
-          <input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
20
+        <view style="display: flex; align-items: center;">
21
+          <view class="input-item flex align-center" style="flex:1;margin: 0px;" v-if="captchaEnabled">
22
+            <view class="iconfont icon-code icon"></view>
23
+            <input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
24
+
25
+          </view>
23
           <view class="login-code">
26
           <view class="login-code">
24
             <image :src="codeUrl" @click="getCode" class="login-code-img"></image>
27
             <image :src="codeUrl" @click="getCode" class="login-code-img"></image>
25
           </view>
28
           </view>
26
         </view>
29
         </view>
30
+
27
         <view class="action-btn">
31
         <view class="action-btn">
28
           <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
32
           <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
29
         </view>
33
         </view>
@@ -210,7 +214,7 @@ page {
210
       }
214
       }
211
 
215
 
212
       .input {
216
       .input {
213
-        width: 100%;
217
+    
214
         font-size: 14px;
218
         font-size: 14px;
215
         line-height: 20px;
219
         line-height: 20px;
216
         text-align: left;
220
         text-align: left;
@@ -235,12 +239,17 @@ page {
235
 
239
 
236
     .login-code {
240
     .login-code {
237
       height: 38px;
241
       height: 38px;
238
-      float: right;
242
+      margin-left: 10rpx;
243
+      // position: absolute;
244
+
245
+      // left: 470rpx;
246
+
247
+
239
 
248
 
240
       .login-code-img {
249
       .login-code-img {
241
         height: 38px;
250
         height: 38px;
242
-        position: absolute;
243
-        margin-left: 10px;
251
+
252
+
244
         width: 200rpx;
253
         width: 200rpx;
245
       }
254
       }
246
     }
255
     }

+ 8 - 1
src/pages/myToDoList/index.vue

@@ -212,6 +212,9 @@ export default {
212
             if (this.getLabel(item.instance && item.instance.businessType) == '人') {
212
             if (this.getLabel(item.instance && item.instance.businessType) == '人') {
213
                 return this.getFormData(item, 'checkedPersonnelName');
213
                 return this.getFormData(item, 'checkedPersonnelName');
214
             }
214
             }
215
+            if (this.getLabel(item.instance && item.instance.businessType) == '大队') {
216
+                return this.getFormData(item, 'checkedBrigadeName');
217
+            }
215
             return '';
218
             return '';
216
         },
219
         },
217
         getLabel(key) {
220
         getLabel(key) {
@@ -224,9 +227,13 @@ export default {
224
             if (key === 'SECTION_CHECK') {
227
             if (key === 'SECTION_CHECK') {
225
                 return '科室'
228
                 return '科室'
226
             }
229
             }
230
+            if (key === 'BRIGADE_CHECK') {
231
+                return '大队'
232
+            }
227
             return ''
233
             return ''
228
         },
234
         },
229
         navigateToDetail(row) {
235
         navigateToDetail(row) {
236
+            
230
             let type = 'view'; // 默认查看模式
237
             let type = 'view'; // 默认查看模式
231
             if (this.currentTab == 'msg') {
238
             if (this.currentTab == 'msg') {
232
                 const { businessType, businessId, instanceId } = row;
239
                 const { businessType, businessId, instanceId } = row;
@@ -271,7 +278,7 @@ export default {
271
                 url = `/pages/seizedReported/index?params=${encodeURIComponent(JSON.stringify(obj))}`;
278
                 url = `/pages/seizedReported/index?params=${encodeURIComponent(JSON.stringify(obj))}`;
272
             }
279
             }
273
             // 巡检对应个人 班组  科室
280
             // 巡检对应个人 班组  科室
274
-            if (['PERSONAL_CHECK', 'GROUP_CHECK', 'SECTION_CHECK'].includes(businessType) || this.currentTab == 'msg') {
281
+            if (['PERSONAL_CHECK', 'GROUP_CHECK', 'SECTION_CHECK', 'BRIGADE_CHECK'].includes(businessType) || this.currentTab == 'msg') {
275
                 url = `/pages/problemRect/index?params=${encodeURIComponent(JSON.stringify(obj))}`;
282
                 url = `/pages/problemRect/index?params=${encodeURIComponent(JSON.stringify(obj))}`;
276
             }
283
             }
277
             if (url) {
284
             if (url) {

+ 45 - 2
src/pages/problemRect/index.vue

@@ -23,6 +23,9 @@
23
                                 <uni-data-picker v-if="getCheckLabel == '被检查科'" :localdata="departments"
23
                                 <uni-data-picker v-if="getCheckLabel == '被检查科'" :localdata="departments"
24
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedDepartmentId"
24
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedDepartmentId"
25
                                     :readonly="true" />
25
                                     :readonly="true" />
26
+                                <uni-data-picker v-if="getCheckLabel == '被检查大队'" :localdata="brigades"
27
+                                    :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedBrigadeId"
28
+                                    :readonly="true" />
26
                                 <uni-data-picker v-if="getCheckLabel == '被检查班组'" :localdata="teams"
29
                                 <uni-data-picker v-if="getCheckLabel == '被检查班组'" :localdata="teams"
27
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedTeamId"
30
                                     :popup-title="`请选择${getCheckLabel}`" v-model="formData.checkedTeamId"
28
                                     :readonly="true" />
31
                                     :readonly="true" />
@@ -206,7 +209,7 @@ import { listAllUser } from "@/api/system/user.js"
206
 import { getProblemRectDetail, approvalAgree, approvalReject } from '@/api/problemRect/problemRect.js'
209
 import { getProblemRectDetail, approvalAgree, approvalReject } from '@/api/problemRect/problemRect.js'
207
 import { getApprovelHistory } from '@/api/approve/approve.js'
210
 import { getApprovelHistory } from '@/api/approve/approve.js'
208
 import { getDeptManager, getDeptDetail } from "@/api/system/dept/dept.js"
211
 import { getDeptManager, getDeptDetail } from "@/api/system/dept/dept.js"
209
-import { buildTeamOptions, buildDepartmentOptions } from "@/utils/common.js"
212
+import { buildTeamOptions, buildDepartmentOptions,buildBrigadeOptions } from "@/utils/common.js"
210
 export default {
213
 export default {
211
     components: { HomeContainer, RejectModal },
214
     components: { HomeContainer, RejectModal },
212
     mixins: [useDictMixin],
215
     mixins: [useDictMixin],
@@ -214,6 +217,13 @@ export default {
214
         currentUser() {
217
         currentUser() {
215
             return this.$store.state.user;
218
             return this.$store.state.user;
216
         },
219
         },
220
+        currentUserId() {
221
+            return this.currentUser && this.currentUser.id;
222
+        },
223
+        //表单责任人是当前登陆人
224
+        isResponsiblePerson() {
225
+            return this.currentUserId == this.formData.responsibleUserId;
226
+        },
217
         userInfo() {
227
         userInfo() {
218
             return (this.$store.state.user && this.$store.state.user.userInfo) ? this.$store.state.user.userInfo : {}
228
             return (this.$store.state.user && this.$store.state.user.userInfo) ? this.$store.state.user.userInfo : {}
219
         },
229
         },
@@ -243,6 +253,9 @@ export default {
243
             if (this.formData.checkedLevel == checkedLevelEnums.DEPARTMENT_LEVEL) {
253
             if (this.formData.checkedLevel == checkedLevelEnums.DEPARTMENT_LEVEL) {
244
                 return '被检查科'
254
                 return '被检查科'
245
             }
255
             }
256
+            if (this.formData.checkedLevel == checkedLevelEnums.BRIGADE_LEVEL) {
257
+                return '被检查大队'
258
+            }
246
         },
259
         },
247
         // 动态验证规则
260
         // 动态验证规则
248
         rules() {
261
         rules() {
@@ -282,6 +295,7 @@ export default {
282
             userOptions: [],
295
             userOptions: [],
283
             teams: [], // 班组选项数据
296
             teams: [], // 班组选项数据
284
             departments: [], // 部门选项数据
297
             departments: [], // 部门选项数据
298
+            brigades: [], // 大队选项数据
285
             // 表单数据
299
             // 表单数据
286
             formData: {
300
             formData: {
287
                 // 基本信息
301
                 // 基本信息
@@ -301,6 +315,10 @@ export default {
301
                 selectTeamName: '', // 整改班组名称
315
                 selectTeamName: '', // 整改班组名称
302
                 selectTeamLeaderId: '', // 班组长ID
316
                 selectTeamLeaderId: '', // 班组长ID
303
                 selectTeamLeaderName: '', // 班组长姓名
317
                 selectTeamLeaderName: '', // 班组长姓名
318
+                
319
+                // 被检查对象相关字段
320
+                checkedBrigadeId: '', // 被检查大队ID
321
+                checkedBrigadeName: '', // 被检查大队名称
304
 
322
 
305
                 // 整改信息
323
                 // 整改信息
306
                 rectificationDetails: '',
324
                 rectificationDetails: '',
@@ -349,6 +367,14 @@ export default {
349
         handlecheckedDepartmentIdChange(e) {
367
         handlecheckedDepartmentIdChange(e) {
350
 
368
 
351
         },
369
         },
370
+        
371
+        // 被检查大队选择
372
+        handlecheckedBrigadeIdChange(e) {
373
+            const { text, value } = e.detail.value[0];
374
+            let newText = text.split('/')
375
+            this.$set(this.formData, 'checkedBrigadeName', newText[newText.length - 1].trim());
376
+            this.$set(this.formData, 'checkedBrigadeId', value);
377
+        },
352
         async loadDictData() {
378
         async loadDictData() {
353
             const user = await listAllUser();
379
             const user = await listAllUser();
354
             this.userOptions = user.data.map(item => ({
380
             this.userOptions = user.data.map(item => ({
@@ -385,6 +411,7 @@ export default {
385
                 const deptTree = await getDeptList();
411
                 const deptTree = await getDeptList();
386
                 this.teams = buildTeamOptions(deptTree.data || []);
412
                 this.teams = buildTeamOptions(deptTree.data || []);
387
                 this.departments = buildDepartmentOptions(deptTree.data || []);
413
                 this.departments = buildDepartmentOptions(deptTree.data || []);
414
+                this.brigades = buildBrigadeOptions(deptTree.data || []);
388
                 uni.hideLoading();
415
                 uni.hideLoading();
389
             } catch (err) {
416
             } catch (err) {
390
                 uni.hideLoading();
417
                 uni.hideLoading();
@@ -528,7 +555,7 @@ export default {
528
 
555
 
529
             // 还原checkProjectItemList结构到后端原始格式
556
             // 还原checkProjectItemList结构到后端原始格式
530
             const restoredCheckProjectItemList = this.restoreOriginalCheckProjectItemList();
557
             const restoredCheckProjectItemList = this.restoreOriginalCheckProjectItemList();
531
-
558
+            
532
             const payload = {
559
             const payload = {
533
                 ...this.formData,
560
                 ...this.formData,
534
                 checkProjectItemList: restoredCheckProjectItemList,
561
                 checkProjectItemList: restoredCheckProjectItemList,
@@ -553,6 +580,22 @@ export default {
553
 
580
 
554
         // 提交表单
581
         // 提交表单
555
         submitForm() {
582
         submitForm() {
583
+            // 如果当前用户是责任人,校验问题人字段必填
584
+            if (this.isResponsiblePerson) {
585
+                const hasEmptyProblemUser = this.formData.checkProjectItemList?.some(problem => 
586
+                    !problem.checkUserList || !problem.checkUserList.userId
587
+                );
588
+                
589
+                if (hasEmptyProblemUser) {
590
+                    uni.showToast({ 
591
+                        title: '请填写所有问题的责任人', 
592
+                        icon: 'none',
593
+                        duration: 3000
594
+                    });
595
+                    return;
596
+                }
597
+            }
598
+            
556
             this.$refs.form.validate().then(res => {
599
             this.$refs.form.validate().then(res => {
557
                 uni.showLoading({ title: '提交中...', mask: true });
600
                 uni.showLoading({ title: '提交中...', mask: true });
558
 
601
 

BIN
src/static/pc.png


+ 27 - 0
src/utils/common.js

@@ -141,6 +141,33 @@ export function buildDepartmentOptions(tree = []) {
141
   return result;
141
   return result;
142
 }
142
 }
143
 
143
 
144
+/**
145
+ * 找出deptType为BRIGADE的节点
146
+ * @param {Array} tree - 树形结构数据
147
+ * @returns {Array} 包含所有BRIGADE节点的数组
148
+ */
149
+export function buildBrigadeOptions(tree = []) {
150
+  const result = [];
151
+
152
+  function dfs(node, path = []) {
153
+    const currentPath = [...path, node.label];
154
+    // 如果是 DEPARTMENT 节点
155
+    if (node.deptType === 'BRIGADE') {
156
+      result.push({
157
+        text: currentPath.join(' / '),
158
+        value: node.id
159
+      });
160
+    }
161
+    // 继续递归子节点
162
+    if (node.children && Array.isArray(node.children)) {
163
+      node.children.forEach(child => dfs(child, currentPath));
164
+    }
165
+  }
166
+
167
+  tree.forEach(root => dfs(root));
168
+  return result;
169
+}
170
+
144
 //将数组格式的按照航站楼分成对象格式
171
 //将数组格式的按照航站楼分成对象格式
145
 export function getHandleAreaData(data, notkezhang) {
172
 export function getHandleAreaData(data, notkezhang) {
146
   let louObj = {};
173
   let louObj = {};

+ 3 - 2
src/utils/enums.js

@@ -1,8 +1,9 @@
1
 export const checkedLevelEnums = {
1
 export const checkedLevelEnums = {
2
     TEAM_LEVEL: 'TEAM_LEVEL',
2
     TEAM_LEVEL: 'TEAM_LEVEL',
3
     PERSONNEL_LEVEL: 'PERSONNEL_LEVEL',
3
     PERSONNEL_LEVEL: 'PERSONNEL_LEVEL',
4
-    DEPARTMENT_LEVEL: 'DEPARTMENT_LEVEL',
5
-    STATION_LEVEL: 'STATION_LEVEL'
4
+    DEPARTMENT_LEVEL: 'DEPARTMENT_LEVEL',//主管级别
5
+    STATION_LEVEL: 'STATION_LEVEL',
6
+    BRIGADE_LEVEL: 'BRIGADE_LEVEL',//大队级别
6
 }
7
 }
7
 
8
 
8
 
9
 

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1056 - 26
yarn.lock