添加后台启动脚本和修改域名

This commit is contained in:
xuqiuyun
2025-11-17 09:18:31 +08:00
parent eca2040e5b
commit 8615549a6f
45 changed files with 15564 additions and 384 deletions

View File

@@ -19,6 +19,7 @@
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"qrcode": "^1.5.4",
"vue": "^3.4.15",
"vue-router": "^4.2.5",
"xlsx": "^0.18.5"
@@ -1464,7 +1465,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -1473,7 +1473,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -1672,6 +1671,15 @@
"node": ">=6"
}
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
@@ -1730,6 +1738,51 @@
"node": "*"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/cliui/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/cliui/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
@@ -1742,7 +1795,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -1753,8 +1805,7 @@
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/combined-stream": {
"version": "1.0.8",
@@ -1876,6 +1927,15 @@
}
}
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/deep-eql": {
"version": "4.1.4",
"resolved": "https://registry.npmmirror.com/deep-eql/-/deep-eql-4.1.4.tgz",
@@ -1957,6 +2017,12 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
"license": "MIT"
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -2614,6 +2680,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-func-name": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/get-func-name/-/get-func-name-2.0.2.tgz",
@@ -2926,7 +3001,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -3548,6 +3622,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@@ -3576,7 +3659,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -3694,6 +3776,15 @@
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true
},
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"license": "MIT",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
@@ -3783,6 +3874,23 @@
"node": ">=6"
}
},
"node_modules/qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"license": "MIT",
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
},
"bin": {
"qrcode": "bin/qrcode"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -3809,6 +3917,21 @@
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"dev": true
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"license": "ISC"
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
@@ -4017,6 +4140,12 @@
"node": ">=10"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC"
},
"node_modules/shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/shallow-equal/-/shallow-equal-1.2.1.tgz",
@@ -4193,7 +4322,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -4855,6 +4983,12 @@
"node": ">= 8"
}
},
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"license": "ISC"
},
"node_modules/why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
@@ -5025,6 +5159,119 @@
"node": ">=12"
}
},
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"license": "ISC"
},
"node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"license": "MIT",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"license": "ISC",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/yargs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/yargs/node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yargs/node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -5987,14 +6234,12 @@
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -6147,6 +6392,11 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
@@ -6190,6 +6440,43 @@
"get-func-name": "^2.0.2"
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
},
"dependencies": {
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
}
}
},
"codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
@@ -6199,7 +6486,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -6207,8 +6493,7 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"combined-stream": {
"version": "1.0.8",
@@ -6299,6 +6584,11 @@
"ms": "^2.1.3"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
},
"deep-eql": {
"version": "4.1.4",
"resolved": "https://registry.npmmirror.com/deep-eql/-/deep-eql-4.1.4.tgz",
@@ -6353,6 +6643,11 @@
"integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
"dev": true
},
"dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
},
"dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -6847,6 +7142,11 @@
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-func-name": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/get-func-name/-/get-func-name-2.0.2.tgz",
@@ -7060,8 +7360,7 @@
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-glob": {
"version": "4.0.3",
@@ -7516,6 +7815,11 @@
"p-limit": "^3.0.2"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@@ -7540,8 +7844,7 @@
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"path-is-absolute": {
"version": "1.0.1",
@@ -7622,6 +7925,11 @@
}
}
},
"pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="
},
"postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
@@ -7678,6 +7986,16 @@
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true
},
"qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"requires": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
}
},
"queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -7690,6 +8008,16 @@
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"dev": true
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
@@ -7824,6 +8152,11 @@
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/shallow-equal/-/shallow-equal-1.2.1.tgz",
@@ -7955,7 +8288,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
@@ -8365,6 +8697,11 @@
"isexe": "^2.0.0"
}
},
"which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
},
"why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
@@ -8481,6 +8818,88 @@
"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
"dev": true
},
"y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"dependencies": {
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"requires": {
"p-locate": "^4.1.0"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"requires": {
"p-limit": "^2.2.0"
}
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
}
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
},
"yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@@ -33,37 +33,38 @@
"deploy": "npm run build && npm run preview"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"ant-design-vue": "^4.0.6",
"axios": "^1.6.2",
"dayjs": "^1.11.10",
"echarts": "^5.4.3",
"file-saver": "^2.0.5",
"lodash-es": "^4.17.21",
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"qrcode": "^1.5.4",
"vue": "^3.4.15",
"vue-router": "^4.2.5",
"xlsx": "^0.18.5",
"@ant-design/icons-vue": "^7.0.1",
"dayjs": "^1.11.10",
"lodash-es": "^4.17.21",
"nprogress": "^0.2.0"
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.6.2",
"vite": "^4.5.3",
"@types/node": "^16.18.68",
"@types/lodash-es": "^4.17.12",
"@types/node": "^16.18.68",
"@types/nprogress": "^0.2.3",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-vue": "^4.6.2",
"@vitest/coverage-v8": "^0.34.6",
"@vitest/ui": "^0.34.6",
"@vue/eslint-config-typescript": "^11.0.3",
"eslint": "^8.55.0",
"eslint-plugin-vue": "^9.19.2",
"rimraf": "^5.0.5",
"typescript": "^4.9.5",
"vite": "^4.5.3",
"vite-bundle-analyzer": "^0.7.0",
"vitest": "^0.34.6",
"@vitest/ui": "^0.34.6",
"@vitest/coverage-v8": "^0.34.6",
"vue-tsc": "^1.8.25"
}
}

3426
admin-system/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,13 @@ const createHeaders = (headers = {}) => {
throw new Error('未认证,请先登录');
}
return { ...defaultHeaders, ...headers };
// 如果headers中明确设置了Content-Type为undefined则移除它
const mergedHeaders = { ...defaultHeaders, ...headers };
if (mergedHeaders['Content-Type'] === undefined) {
delete mergedHeaders['Content-Type'];
}
return mergedHeaders;
};
/**
@@ -342,16 +348,26 @@ export const api = {
/**
* POST请求
* @param {string} endpoint - API端点
* @param {Object} data - 请求数据
* @param {Object|FormData} data - 请求数据
* @param {Object} options - 请求选项
* @returns {Promise} 响应数据
*/
async post(endpoint, data, options = {}) {
const url = `${API_BASE_URL}${endpoint}`;
// 如果是FormData不设置Content-Type让浏览器自动处理
const isFormData = data instanceof FormData;
const headers = isFormData
? createHeaders({ ...options.headers, 'Content-Type': undefined })
: createHeaders(options.headers);
// 如果是FormData直接使用data否则使用JSON.stringify
const body = isFormData ? data : JSON.stringify(data);
const response = await fetch(url, {
method: 'POST',
headers: createHeaders(options.headers),
body: JSON.stringify(data),
headers: headers,
body: body,
...options,
});
return handleResponse(response);

View File

@@ -104,11 +104,12 @@ const generateRequestId = () => {
* @returns {boolean} 是否为标准格式
*/
export const isValidApiResponse = (response) => {
// 基本格式检查:必须有 success 和 message 字段
// timestamp 为可选字段(兼容旧接口)
return response &&
typeof response === 'object' &&
typeof response.success === 'boolean' &&
typeof response.message === 'string' &&
typeof response.timestamp === 'string';
(typeof response.message === 'string' || response.message === undefined);
};
/**

View File

@@ -128,10 +128,10 @@
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="品" name="strain">
<a-form-item label="品" name="strain">
<a-select
v-model:value="formData.strain"
placeholder="请选择品"
placeholder="请选择品"
:loading="cattleUsersLoading"
@change="handleFieldChange('strain', $event)"
show-search
@@ -157,6 +157,7 @@
show-search
:filter-option="filterOption"
>
<!-- 动态选项 -->
<a-select-option
v-for="type in cattleTypes"
:key="type.id"
@@ -168,10 +169,10 @@
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="类别" name="cate">
<a-form-item label="生理阶段" name="cate">
<a-select
v-model:value="formData.cate"
placeholder="请选择类别"
placeholder="请选择生理阶段"
@change="handleFieldChange('cate', $event)"
show-search
:filter-option="filterCateOption"
@@ -240,16 +241,7 @@
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="生理阶段" name="physiologicalStage">
<a-input
v-model:value="formData.physiologicalStage"
placeholder="请输入生理阶段"
@input="handleFieldChange('physiologicalStage', $event.target.value)"
@change="handleFieldChange('physiologicalStage', $event.target.value)"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="胎次" name="parity">
<a-input-number
@@ -323,13 +315,19 @@
</a-col>
<a-col :span="12">
<a-form-item label="来源" name="source">
<a-input-number
<a-select
v-model:value="formData.source"
placeholder="请输入来源"
:min="0"
style="width: 100%"
placeholder="请选择来源"
@change="handleFieldChange('source', $event)"
/>
show-search
:filter-option="filterOption"
>
<a-select-option :value="1">购买</a-select-option>
<a-select-option :value="2">自繁</a-select-option>
<a-select-option :value="3">放生</a-select-option>
<a-select-option :value="4">合作社</a-select-option>
<a-select-option :value="5">入股</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
@@ -459,7 +457,7 @@ const formData = reactive({
penId: null, // 映射iot_cattle.pen_id
intoTime: null,
parity: 0,
source: 0,
source: '', // 映射iot_cattle.source改为空字符串以便验证
sourceDay: 0,
sourceWeight: 0,
ageInMonths: 0, // 从iot_cattle.birthday计算得出
@@ -484,7 +482,8 @@ const rules = {
cate: [{ required: true, message: '请输入类别', trigger: 'blur' }], // iot_cattle.cate
birthWeight: [{ required: true, message: '请输入出生体重', trigger: 'blur' }], // iot_cattle.birth_weight
birthday: [{ required: true, message: '请选择出生日期', trigger: 'change' }], // iot_cattle.birthday
orgId: [{ required: true, message: '请选择所属农场', trigger: 'change' }] // iot_cattle.org_id
orgId: [{ required: true, message: '请选择所属农场', trigger: 'change' }], // iot_cattle.org_id
source: [{ required: true, message: '请选择来源', trigger: 'change' }] // iot_cattle.source
}
// 表格列配置基于iot_cattle表字段映射
@@ -637,16 +636,34 @@ const fetchFarms = async () => {
const fetchPens = async (farmId = null) => {
try {
pensLoading.value = true
const token = localStorage.getItem('token')
const params = farmId ? { farmId } : {}
const response = await api.get('/iot-cattle/pens/list', {
params
})
if (response.success) {
pens.value = response.data
console.log('🔍 [牛只档案] 开始获取栏舍列表')
// 调用 /cattle-pens 接口
const params = {
page: 1,
pageSize: 10
}
// 如果有 farmId可以添加到参数中如果后端支持
if (farmId) {
params.farmId = farmId
}
console.log('📤 [牛只档案] 栏舍列表请求参数:', params)
const response = await api.cattlePens.getList(params)
console.log('📥 [牛只档案] 栏舍列表响应:', response)
if (response.success && response.data) {
// 从 response.data.list 中提取栏舍列表
const pensList = response.data.list || []
pens.value = pensList
console.log('✅ [牛只档案] 获取栏舍列表成功,共', pensList.length, '条')
return pensList
}
return []
} catch (error) {
console.error('获取栏舍列表失败:', error)
console.error('❌ [牛只档案] 获取栏舍列表失败:', error)
return []
} finally {
pensLoading.value = false
}
@@ -656,16 +673,37 @@ const fetchPens = async (farmId = null) => {
const fetchBatches = async (farmId = null) => {
try {
batchesLoading.value = true
const token = localStorage.getItem('token')
const params = farmId ? { farmId } : {}
const response = await api.get('/iot-cattle/batches/list', {
params
})
if (response.success) {
batches.value = response.data
console.log('🔍 [牛只档案] 开始获取批次列表')
// 调用 /cattle-batches 接口
const params = {
page: 1,
pageSize: 10,
search: '',
exactMatch: true,
strictMatch: true
}
// 如果有 farmId可以添加到参数中如果后端支持
if (farmId) {
params.farmId = farmId
}
console.log('📤 [牛只档案] 批次列表请求参数:', params)
const response = await api.cattleBatches.getList(params)
console.log('📥 [牛只档案] 批次列表响应:', response)
if (response.success && response.data) {
// 从 response.data.list 中提取批次列表
const batchesList = response.data.list || []
batches.value = batchesList
console.log('✅ [牛只档案] 获取批次列表成功,共', batchesList.length, '条')
return batchesList
}
return []
} catch (error) {
console.error('获取批次列表失败:', error)
console.error('❌ [牛只档案] 获取批次列表失败:', error)
return []
} finally {
batchesLoading.value = false
}
@@ -801,12 +839,27 @@ const initializeAddMode = () => {
// ==================== 编辑牛只档案功能 ====================
// 编辑牛只档案
const editAnimal = (record) => {
console.log('=== 点击编辑按钮 ===')
console.log('编辑记录:', record)
initializeEditMode(record)
openModal()
loadRequiredData()
// 编辑牛只档案
const editAnimal = async (record) => {
try {
console.log('=== 点击编辑按钮 ===')
console.log('编辑记录ID:', record.id)
// 调用后端接口获取详情
const response = await api.get(`/iot-cattle/${record.id}`)
if (response.success && response.data) {
console.log('获取到的详情数据:', response.data)
initializeEditMode(response.data)
openModal()
loadRequiredData()
} else {
message.error('获取牛只档案详情失败')
}
} catch (error) {
console.error('获取牛只档案详情失败:', error)
message.error(error.message || '获取牛只档案详情失败')
}
}
// 初始化编辑模式
@@ -842,27 +895,99 @@ const populateFormWithRecord = (record) => {
formData.id = record.id
formData.earNumber = record.earNumber || '' // iot_cattle.ear_number
formData.sex = record.sex || 1 // iot_cattle.sex
formData.strain = record.strain || '' // iot_cattle.strain
formData.varieties = record.varieties || '' // iot_cattle.varieties
formData.cate = record.cate || '' // iot_cattle.cate
// 使用原始IDstrainId如果没有则使用strain兼容旧数据
formData.strain = record.strainId !== undefined ? record.strainId : (record.strain || '') // iot_cattle.strain
// 使用原始IDvarietiesId如果没有则使用varieties兼容旧数据
formData.varieties = record.varietiesId !== undefined ? record.varietiesId : (record.varieties || '') // iot_cattle.varieties
// 使用原始IDcateId如果没有则使用cate兼容旧数据
formData.cate = record.cateId !== undefined ? record.cateId : (record.cate || '') // iot_cattle.cate
formData.birthWeight = record.birthWeight || 0 // iot_cattle.birth_weight
formData.birthday = record.birthday ? dayjs(record.birthday) : null // iot_cattle.birthday
formData.penId = record.penId || null // iot_cattle.pen_id
formData.intoTime = record.intoTime || null
// 处理出生日期如果是时间戳使用dayjs.unix如果是日期字符串使用dayjs
if (record.birthday) {
if (typeof record.birthday === 'number') {
// 时间戳转换为dayjs对象
const birthdayDate = dayjs.unix(record.birthday)
formData.birthday = birthdayDate.isValid() ? birthdayDate : null
console.log('转换出生日期:', record.birthday, '->', formData.birthday?.format('YYYY-MM-DD'))
} else {
formData.birthday = dayjs(record.birthday) // 日期字符串转换为dayjs对象
}
} else {
formData.birthday = null
}
// 处理所属栏舍如果为0或null则设为null
formData.penId = (record.penId && record.penId !== 0) ? record.penId : null // iot_cattle.pen_id
// 处理入场日期如果是时间戳使用dayjs.unix如果是日期字符串使用dayjs
if (record.intoTime) {
if (typeof record.intoTime === 'number') {
formData.intoTime = dayjs.unix(record.intoTime) // 时间戳转换为dayjs对象
} else {
formData.intoTime = dayjs(record.intoTime) // 日期字符串转换为dayjs对象
}
} else {
formData.intoTime = null
}
formData.parity = record.parity || 0
formData.source = record.source || 0
// 处理来源如果是数字包括0直接使用否则设为空字符串
if (typeof record.source === 'number') {
formData.source = record.source
} else if (record.source !== undefined && record.source !== null && record.source !== '') {
formData.source = record.source
} else {
formData.source = ''
}
formData.sourceDay = record.sourceDay || 0
formData.sourceWeight = record.sourceWeight || 0
formData.ageInMonths = calculateAgeInMonths(record.birthday) // 从iot_cattle.birthday计算月龄
// 优先使用JSON中的ageInMonths如果没有则计算
formData.ageInMonths = record.ageInMonths !== undefined ? record.ageInMonths : calculateAgeInMonths(record.birthday)
formData.physiologicalStage = record.physiologicalStage || ''
formData.currentWeight = record.currentWeight || 0
formData.weightCalculateTime = record.weightCalculateTime ? dayjs(record.weightCalculateTime) : null
// 处理体重计算时间如果是时间戳使用dayjs.unix如果是日期字符串使用dayjs
if (record.weightCalculateTime) {
if (typeof record.weightCalculateTime === 'number') {
formData.weightCalculateTime = dayjs.unix(record.weightCalculateTime) // 时间戳转换为dayjs对象
} else {
formData.weightCalculateTime = dayjs(record.weightCalculateTime) // 日期字符串转换为dayjs对象
}
} else {
formData.weightCalculateTime = null
}
formData.dayOfBirthday = record.dayOfBirthday || 0
formData.orgId = record.farmId || record.orgId || null // iot_cattle.org_id
formData.penId = record.penId || null // iot_cattle.pen_id
formData.batchId = record.batchId || null // iot_cattle.batch_id
// 保存 penId 和 batchId 的值,等待选项加载完成后再设置
const savedPenId = (record.penId && record.penId !== 0) ? record.penId : null
const savedBatchId = (record.batchId && record.batchId !== 0) ? record.batchId : null
console.log('填充后的 formData:', JSON.parse(JSON.stringify(formData)));
console.log('保存的 penId:', savedPenId, 'batchId:', savedBatchId);
console.log('出生日期转换结果:', formData.birthday);
// 如果所属农场有值,先加载对应的栏舍和批次选项,然后再设置值
if (formData.orgId) {
Promise.all([
fetchPens(formData.orgId),
fetchBatches(formData.orgId)
]).then(() => {
// 选项加载完成后再设置值,使用 nextTick 确保 DOM 更新
setTimeout(() => {
if (savedPenId !== null) {
formData.penId = savedPenId
console.log('设置 penId:', savedPenId, '当前栏舍选项:', pens.value)
}
if (savedBatchId !== null) {
formData.batchId = savedBatchId
console.log('设置 batchId:', savedBatchId, '当前批次选项:', batches.value)
}
}, 100) // 延迟100ms确保选项已加载到DOM
}).catch(error => {
console.error('加载栏舍或批次选项失败:', error)
})
} else {
// 如果没有农场ID直接设置值虽然可能没有对应的选项
formData.penId = savedPenId
formData.batchId = savedBatchId
}
}
// 配置编辑模式设置
@@ -932,7 +1057,7 @@ const handleSubmit = async () => {
console.log('原始表单数据:', JSON.parse(JSON.stringify(formData)));
// 检查必填字段
const requiredFields = ['earNumber', 'sex', 'strain', 'varieties', 'cate', 'birthWeight', 'birthday', 'orgId'];
const requiredFields = ['earNumber', 'sex', 'strain', 'varieties', 'cate', 'birthWeight', 'birthday', 'orgId', 'source'];
const missingFields = requiredFields.filter(field => !formData[field] && formData[field] !== 0);
if (missingFields.length > 0) {
console.error('缺少必填字段:', missingFields);
@@ -1006,7 +1131,7 @@ const resetForm = () => {
penId: null,
intoTime: null,
parity: 0,
source: 0,
source: '',
sourceDay: 0,
sourceWeight: 0,
ageInMonths: 0,
@@ -1268,12 +1393,8 @@ const confirmImport = async () => {
const formData = new FormData()
formData.append('file', importFile.value)
// 调用导入API
const response = await api.post('/iot-cattle/import', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
// 调用导入APIFormData会自动处理Content-Type不需要手动设置
const response = await api.post('/iot-cattle/import', formData)
if (response.data.success) {
message.destroy()
@@ -1290,7 +1411,16 @@ const confirmImport = async () => {
} catch (error) {
message.destroy()
console.error('导入失败:', error)
message.error('导入失败,请检查文件格式是否正确')
// 显示具体的错误信息
const errorMessage = error.message || error.response?.data?.message || '导入失败,请检查文件格式是否正确'
message.error(errorMessage)
// 如果是认证错误,提示用户重新登录
if (errorMessage.includes('认证') || errorMessage.includes('未授权') || errorMessage.includes('登录')) {
setTimeout(() => {
window.location.href = '/login'
}, 2000)
}
} finally {
importLoading.value = false
}

View File

@@ -110,27 +110,22 @@
>
<a-form-item label="批次名称" name="name">
<a-input
v-model="formData.name"
v-model:value="formData.name"
placeholder="请输入批次名称"
@input="handleFieldChange('name', $event.target.value)"
@change="handleFieldChange('name', $event.target.value)"
/>
</a-form-item>
<a-form-item label="批次编号" name="code">
<a-input
v-model="formData.code"
v-model:value="formData.code"
placeholder="请输入批次编号"
@input="handleFieldChange('code', $event.target.value)"
@change="handleFieldChange('code', $event.target.value)"
/>
</a-form-item>
<a-form-item label="批次类型" name="type">
<a-select
v-model="formData.type"
v-model:value="formData.type"
placeholder="请选择批次类型"
@change="handleFieldChange('type', $event)"
>
<a-select-option value="育成批次">育成批次</a-select-option>
<a-select-option value="繁殖批次">繁殖批次</a-select-option>
@@ -142,66 +137,58 @@
<a-form-item label="开始日期" name="startDate">
<a-date-picker
v-model="formData.startDate"
v-model:value="formData.startDate"
style="width: 100%"
placeholder="请选择开始日期"
@change="handleFieldChange('startDate', $event)"
/>
</a-form-item>
<a-form-item label="预计结束日期" name="expectedEndDate">
<a-date-picker
v-model="formData.expectedEndDate"
v-model:value="formData.expectedEndDate"
style="width: 100%"
placeholder="请选择预计结束日期"
@change="handleFieldChange('expectedEndDate', $event)"
/>
</a-form-item>
<a-form-item label="实际结束日期" name="actualEndDate">
<a-date-picker
v-model="formData.actualEndDate"
v-model:value="formData.actualEndDate"
style="width: 100%"
placeholder="请选择实际结束日期"
@change="handleFieldChange('actualEndDate', $event)"
/>
</a-form-item>
<a-form-item label="目标牛只数量" name="targetCount">
<a-input-number
v-model="formData.targetCount"
v-model:value="formData.targetCount"
:min="1"
:max="1000"
style="width: 100%"
placeholder="请输入目标牛只数量"
@change="handleFieldChange('targetCount', $event)"
/>
</a-form-item>
<a-form-item label="当前牛只数量" name="currentCount">
<a-input-number
v-model="formData.currentCount"
v-model:value="formData.currentCount"
:min="0"
:max="formData.targetCount || 1000"
style="width: 100%"
placeholder="当前牛只数量"
@change="handleFieldChange('currentCount', $event)"
/>
</a-form-item>
<a-form-item label="负责人" name="manager">
<a-input
v-model="formData.manager"
v-model:value="formData.manager"
placeholder="请输入负责人姓名"
@input="handleFieldChange('manager', $event.target.value)"
@change="handleFieldChange('manager', $event.target.value)"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group
v-model="formData.status"
@change="handleFieldChange('status', $event.target.value)"
v-model:value="formData.status"
>
<a-radio value="进行中">进行中</a-radio>
<a-radio value="已完成">已完成</a-radio>
@@ -211,11 +198,9 @@
<a-form-item label="备注" name="remark">
<a-textarea
v-model="formData.remark"
v-model:value="formData.remark"
:rows="3"
placeholder="请输入备注信息"
@input="handleFieldChange('remark', $event.target.value)"
@change="handleFieldChange('remark', $event.target.value)"
/>
</a-form-item>
</a-form>
@@ -646,7 +631,7 @@ const handleAdd = () => {
modalVisible.value = true
}
const handleEdit = (record) => {
const handleEdit = async (record) => {
console.log('🔄 [批次设置] 开始编辑操作')
console.log('📋 [批次设置] 原始记录数据:', {
id: record.id,
@@ -663,15 +648,76 @@ const handleEdit = (record) => {
})
modalTitle.value = '编辑批次'
Object.assign(formData, {
...record,
startDate: record.startDate ? dayjs(record.startDate) : null,
expectedEndDate: record.expectedEndDate ? dayjs(record.expectedEndDate) : null,
actualEndDate: record.actualEndDate ? dayjs(record.actualEndDate) : null
})
// 处理日期转换
let startDateValue = null
let expectedEndDateValue = null
let actualEndDateValue = null
if (record.startDate) {
if (typeof record.startDate === 'string') {
startDateValue = dayjs(record.startDate)
} else if (record.startDate instanceof Date) {
startDateValue = dayjs(record.startDate)
} else if (record.startDate && typeof record.startDate === 'object' && record.startDate.format) {
startDateValue = record.startDate
} else {
startDateValue = dayjs(record.startDate)
}
}
if (record.expectedEndDate) {
if (typeof record.expectedEndDate === 'string') {
expectedEndDateValue = dayjs(record.expectedEndDate)
} else if (record.expectedEndDate instanceof Date) {
expectedEndDateValue = dayjs(record.expectedEndDate)
} else if (record.expectedEndDate && typeof record.expectedEndDate === 'object' && record.expectedEndDate.format) {
expectedEndDateValue = record.expectedEndDate
} else {
expectedEndDateValue = dayjs(record.expectedEndDate)
}
}
if (record.actualEndDate) {
if (typeof record.actualEndDate === 'string') {
actualEndDateValue = dayjs(record.actualEndDate)
} else if (record.actualEndDate instanceof Date) {
actualEndDateValue = dayjs(record.actualEndDate)
} else if (record.actualEndDate && typeof record.actualEndDate === 'object' && record.actualEndDate.format) {
actualEndDateValue = record.actualEndDate
} else {
actualEndDateValue = dayjs(record.actualEndDate)
}
}
// 填充表单数据 - 使用直接赋值确保响应式更新
formData.id = record.id
formData.name = record.name || ''
formData.code = record.code || ''
formData.type = record.type || ''
formData.startDate = startDateValue
formData.expectedEndDate = expectedEndDateValue
formData.actualEndDate = actualEndDateValue
formData.targetCount = record.targetCount || 0
formData.currentCount = record.currentCount || 0
formData.manager = record.manager || ''
formData.status = record.status || '进行中'
formData.remark = record.remark || ''
formData.farmId = record.farmId || record.farm_id || null
console.log('📝 [批次设置] 表单数据已填充:', formData)
console.log('📝 [批次设置] startDate 是否为 dayjs:', formData.startDate && typeof formData.startDate.format === 'function')
console.log('📝 [批次设置] expectedEndDate 是否为 dayjs:', formData.expectedEndDate && typeof formData.expectedEndDate.format === 'function')
console.log('📝 [批次设置] actualEndDate 是否为 dayjs:', formData.actualEndDate && typeof formData.actualEndDate.format === 'function')
// 打开模态框
modalVisible.value = true
// 等待模态框和表单渲染完成
await nextTick()
await new Promise(resolve => setTimeout(resolve, 200))
console.log('✅ [批次设置] 模态框已打开,表单应该已填充')
}
const handleDelete = (record) => {

View File

@@ -126,7 +126,7 @@
</template>
<template v-if="column.key === 'action'">
<a-space>
<a-button type="link" size="small" @click="handleEdit(record)">
<a-button type="link" size="small" @click="() => handleEdit(record)">
编辑
</a-button>
<a-button type="link" size="small" @click="handleViewDetails(record)">
@@ -167,27 +167,23 @@
>
<a-form-item label="牛只耳号" name="earNumber">
<a-input
v-model="formData.earNumber"
v-model:value="formData.earNumber"
placeholder="请输入牛只耳号"
@input="handleFieldChange('earNumber', $event.target.value)"
@change="handleFieldChange('earNumber', $event.target.value)"
/>
</a-form-item>
<a-form-item label="离栏日期" name="exitDate">
<a-date-picker
v-model="formData.exitDate"
v-model:value="formData.exitDate"
style="width: 100%"
placeholder="请选择离栏日期"
@change="handleFieldChange('exitDate', $event)"
/>
</a-form-item>
<a-form-item label="离栏原因" name="exitReason">
<a-select
v-model="formData.exitReason"
v-model:value="formData.exitReason"
placeholder="请选择离栏原因"
@change="handleFieldChange('exitReason', $event)"
>
<a-select-option value="出售">出售</a-select-option>
<a-select-option value="死亡">死亡</a-select-option>
@@ -199,9 +195,8 @@
<a-form-item label="原栏舍" name="originalPenId">
<a-select
v-model="formData.originalPenId"
v-model:value="formData.originalPenId"
placeholder="请选择原栏舍"
@change="handleFieldChange('originalPenId', $event)"
>
<a-select-option
v-for="pen in penList"
@@ -215,18 +210,15 @@
<a-form-item label="去向" name="destination">
<a-input
v-model="formData.destination"
v-model:value="formData.destination"
placeholder="请输入牛只去向"
@input="handleFieldChange('destination', $event.target.value)"
@change="handleFieldChange('destination', $event.target.value)"
/>
</a-form-item>
<a-form-item label="处理方式" name="disposalMethod">
<a-select
v-model="formData.disposalMethod"
v-model:value="formData.disposalMethod"
placeholder="请选择处理方式"
@change="handleFieldChange('disposalMethod', $event)"
>
<a-select-option value="屠宰">屠宰</a-select-option>
<a-select-option value="转售">转售</a-select-option>
@@ -238,17 +230,14 @@
<a-form-item label="处理人员" name="handler">
<a-input
v-model="formData.handler"
v-model:value="formData.handler"
placeholder="请输入处理人员姓名"
@input="handleFieldChange('handler', $event.target.value)"
@change="handleFieldChange('handler', $event.target.value)"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group
v-model="formData.status"
@change="handleFieldChange('status', $event.target.value)"
v-model:value="formData.status"
>
<a-radio value="已确认">已确认</a-radio>
<a-radio value="待确认">待确认</a-radio>
@@ -258,11 +247,9 @@
<a-form-item label="备注" name="remark">
<a-textarea
v-model="formData.remark"
v-model:value="formData.remark"
:rows="3"
placeholder="请输入备注信息"
@input="handleFieldChange('remark', $event.target.value)"
@change="handleFieldChange('remark', $event.target.value)"
/>
</a-form-item>
</a-form>
@@ -579,38 +566,207 @@ const handleAdd = () => {
modalVisible.value = true
}
const handleEdit = (record) => {
console.log('🔄 [离栏记录] 开始编辑操作')
console.log('📋 [离栏记录] 原始记录数据:', {
id: record.id,
earNumber: record.earNumber,
exitDate: record.exitDate,
exitReason: record.exitReason,
originalPenId: record.originalPenId,
originalPenName: record.originalPenName,
destination: record.destination,
disposalMethod: record.disposalMethod,
handler: record.handler,
status: record.status,
remark: record.remark
})
// 获取离栏记录详情(用于编辑)
const getExitRecordDetail = async (id) => {
console.log('🔵 [离栏记录-编辑] ========== 开始获取详情 ==========')
console.log('🔵 [离栏记录-编辑] 函数被调用记录ID:', id)
console.log('🔵 [离栏记录-编辑] 调用时间:', new Date().toISOString())
modalTitle.value = '编辑离栏记录'
Object.assign(formData, {
id: record.id,
earNumber: record.earNumber, // 映射耳号字段
exitDate: record.exitDate ? dayjs(record.exitDate) : null,
exitReason: record.exitReason,
originalPenId: record.originalPenId, // 映射原栏舍ID
destination: record.destination,
disposalMethod: record.disposalMethod,
handler: record.handler,
status: record.status,
remark: record.remark || ''
})
try {
console.log('🔄 [离栏记录-编辑] 开始获取详情')
console.log('📋 [离栏记录-编辑] 记录ID:', id)
// 调用后端接口获取详情
console.log('📡 [离栏记录-编辑] 准备调用 API: /cattle-exit-records/' + id)
const response = await api.cattleExitRecords.getById(id)
console.log('📡 [离栏记录-编辑] API 调用完成,响应状态:', response.success)
if (response.success && response.data) {
console.log('✅ [离栏记录-编辑] 获取详情成功:', response.data)
console.log('🔵 [离栏记录-编辑] ========== 获取详情成功 ==========')
return response.data
} else {
console.error('❌ [离栏记录-编辑] 获取详情失败:', response.message)
throw new Error(response.message || '获取离栏记录详情失败')
}
} catch (error) {
console.error('❌ [离栏记录-编辑] 获取详情异常:', error)
console.error('🔵 [离栏记录-编辑] ========== 获取详情失败 ==========')
throw error
}
}
// 编辑离栏记录
const handleEdit = async (record) => {
// 立即输出日志,确认函数被调用
console.log('🟢🟢🟢 [离栏记录-编辑] ========== handleEdit 函数被调用 ==========')
console.log('🟢 [离栏记录-编辑] 调用时间:', new Date().toISOString())
console.log('🟢 [离栏记录-编辑] 记录数据:', record)
console.log('🟢 [离栏记录-编辑] 记录ID:', record?.id)
console.log('📝 [离栏记录] 表单数据已填充:', formData)
modalVisible.value = true
// 验证 record 对象
if (!record) {
console.error('❌ [离栏记录-编辑] record 为空')
message.error('记录数据为空')
return
}
if (!record.id) {
console.error('❌ [离栏记录-编辑] record.id 为空')
message.error('记录ID为空')
return
}
try {
console.log('🔄 [离栏记录-编辑] 开始编辑操作')
console.log('📋 [离栏记录-编辑] 编辑记录ID:', record.id)
// 调用封装的获取详情接口
console.log('📞 [离栏记录-编辑] 准备调用 getExitRecordDetail 函数ID:', record.id)
console.log('📞 [离栏记录-编辑] getExitRecordDetail 函数是否存在:', typeof getExitRecordDetail)
const detailData = await getExitRecordDetail(record.id)
console.log('📞 [离栏记录-编辑] getExitRecordDetail 函数调用完成,返回数据:', detailData)
if (detailData) {
console.log('📋 [离栏记录] 获取到的详情数据:', detailData)
modalTitle.value = '编辑离栏记录'
// 确保栏舍列表已加载
if (penList.value.length === 0) {
await loadPenList()
}
// 填充表单数据 - 直接赋值给 reactive 对象的属性
// 处理日期转换
let exitDateValue = null
if (detailData.exitDate) {
// 如果是字符串,转换为 dayjs 对象
if (typeof detailData.exitDate === 'string') {
exitDateValue = dayjs(detailData.exitDate)
} else if (detailData.exitDate instanceof Date) {
exitDateValue = dayjs(detailData.exitDate)
} else if (detailData.exitDate && typeof detailData.exitDate === 'object' && detailData.exitDate.format) {
// 如果已经是 dayjs 对象,直接使用
exitDateValue = detailData.exitDate
} else {
exitDateValue = dayjs(detailData.exitDate)
}
console.log('📅 [离栏记录] 日期转换:', detailData.exitDate, '->', exitDateValue?.format('YYYY-MM-DD'))
console.log('📅 [离栏记录] exitDateValue 类型:', typeof exitDateValue, '是否为 dayjs:', exitDateValue && typeof exitDateValue.format === 'function')
}
// 先填充表单数据(在打开模态框之前)
console.log('📝 [离栏记录] 开始填充表单数据')
// 直接赋值给 reactive 对象
formData.id = detailData.id || null
formData.earNumber = detailData.earNumber || ''
formData.exitDate = exitDateValue // 确保是 dayjs 对象
formData.exitReason = detailData.exitReason || ''
formData.originalPenId = detailData.originalPenId || null
formData.destination = detailData.destination || ''
formData.disposalMethod = detailData.disposalMethod || ''
formData.handler = detailData.handler || ''
formData.status = detailData.status || '待确认'
formData.remark = detailData.remark || ''
console.log('📝 [离栏记录] 表单数据已填充到 formData')
console.log('📝 [离栏记录] formData.exitDate:', formData.exitDate)
console.log('📝 [离栏记录] formData.exitDate 类型:', typeof formData.exitDate)
console.log('📝 [离栏记录] formData.exitDate 是否为 dayjs:', formData.exitDate && typeof formData.exitDate.format === 'function')
console.log('📝 [离栏记录] formData 完整数据:', {
id: formData.id,
earNumber: formData.earNumber,
exitDate: formData.exitDate ? (formData.exitDate.format ? formData.exitDate.format('YYYY-MM-DD') : formData.exitDate) : null,
exitReason: formData.exitReason,
originalPenId: formData.originalPenId,
destination: formData.destination,
disposalMethod: formData.disposalMethod,
handler: formData.handler,
status: formData.status,
remark: formData.remark
})
// 先打开模态框
modalVisible.value = true
console.log('📝 [离栏记录] 模态框已打开')
// 等待模态框完全打开和表单渲染
await nextTick()
await new Promise(resolve => setTimeout(resolve, 300))
// 确保日期字段是 dayjs 对象(防止被序列化)
if (detailData.exitDate && (!formData.exitDate || typeof formData.exitDate.format !== 'function')) {
formData.exitDate = exitDateValue
console.log('🔄 [离栏记录] 重新设置 exitDate 为 dayjs 对象:', formData.exitDate)
}
console.log('🔍 [离栏记录] 检查 formData.exitDate 是否为 dayjs:',
formData.exitDate && typeof formData.exitDate.format === 'function' ? '是' : '否',
formData.exitDate ? (formData.exitDate.format ? formData.exitDate.format('YYYY-MM-DD') : formData.exitDate) : 'null'
)
// 由于表单使用 v-model 绑定到 formData直接修改 formData 即可更新表单
// 但为了确保表单组件正确响应,我们尝试使用 setFieldsValue如果可用
if (formRef.value) {
// 检查表单实例是否有 setFieldsValue 方法
if (typeof formRef.value.setFieldsValue === 'function') {
try {
const fieldsValue = {
id: formData.id,
earNumber: formData.earNumber,
exitDate: formData.exitDate, // 确保是 dayjs 对象
exitReason: formData.exitReason,
originalPenId: formData.originalPenId,
destination: formData.destination,
disposalMethod: formData.disposalMethod,
handler: formData.handler,
status: formData.status,
remark: formData.remark
}
console.log('🔧 [离栏记录] 使用 setFieldsValue 设置表单值')
console.log('🔧 [离栏记录] exitDate 值:', fieldsValue.exitDate, '类型:', typeof fieldsValue.exitDate)
formRef.value.setFieldsValue(fieldsValue)
console.log('✅ [离栏记录] setFieldsValue 设置成功')
} catch (error) {
console.warn('⚠️ [离栏记录] setFieldsValue 失败,但 formData 已更新,表单应该会自动响应:', error)
}
} else {
console.log(' [离栏记录] 表单实例没有 setFieldsValue 方法,使用 v-model 绑定formData 已更新)')
}
} else {
console.warn('⚠️ [离栏记录] formRef.value 为空,但 formData 已更新,表单应该会自动响应')
}
// 再次使用 nextTick 确保值已更新
await nextTick()
await new Promise(resolve => setTimeout(resolve, 100))
// 验证表单值
console.log('✅ [离栏记录] 模态框已打开')
console.log('✅ [离栏记录] formData.exitDate 最终值:', formData.exitDate)
console.log('✅ [离栏记录] formData.exitDate 是否为 dayjs:', formData.exitDate && typeof formData.exitDate.format === 'function')
// 如果表单实例有 getFieldsValue 方法,验证表单值
if (formRef.value && typeof formRef.value.getFieldsValue === 'function') {
try {
const currentFormData = formRef.value.getFieldsValue()
console.log('✅ [离栏记录] 表单当前值(通过 getFieldsValue:', currentFormData)
console.log('✅ [离栏记录] 表单 exitDate 值:', currentFormData.exitDate)
} catch (error) {
console.warn('⚠️ [离栏记录] 无法获取表单值:', error)
}
}
} else {
message.error('获取离栏记录详情失败')
}
} catch (error) {
console.error('❌ [离栏记录] 编辑操作失败:', error)
message.error(error.message || '获取离栏记录详情失败')
}
}
const handleDelete = (record) => {

View File

@@ -97,27 +97,22 @@
>
<a-form-item label="栏舍名称" name="name">
<a-input
v-model="formData.name"
v-model:value="formData.name"
placeholder="请输入栏舍名称"
@input="handleFieldChange('name', $event.target.value)"
@change="handleFieldChange('name', $event.target.value)"
/>
</a-form-item>
<a-form-item label="栏舍编号" name="code">
<a-input
v-model="formData.code"
v-model:value="formData.code"
placeholder="请输入栏舍编号"
@input="handleFieldChange('code', $event.target.value)"
@change="handleFieldChange('code', $event.target.value)"
/>
</a-form-item>
<a-form-item label="栏舍类型" name="type">
<a-select
v-model="formData.type"
v-model:value="formData.type"
placeholder="请选择栏舍类型"
@change="handleFieldChange('type', $event)"
>
<a-select-option value="育成栏">育成栏</a-select-option>
<a-select-option value="产房">产房</a-select-option>
@@ -129,51 +124,45 @@
<a-form-item label="容量" name="capacity">
<a-input-number
v-model="formData.capacity"
v-model:value="formData.capacity"
:min="1"
:max="1000"
style="width: 100%"
placeholder="请输入栏舍容量"
@change="handleFieldChange('capacity', $event)"
/>
</a-form-item>
<a-form-item label="当前牛只数量" name="currentCount">
<a-input-number
v-model="formData.currentCount"
v-model:value="formData.currentCount"
:min="0"
:max="formData.capacity || 1000"
style="width: 100%"
placeholder="当前牛只数量"
@change="handleFieldChange('currentCount', $event)"
/>
</a-form-item>
<a-form-item label="面积(平方米)" name="area">
<a-input-number
v-model="formData.area"
v-model:value="formData.area"
:min="0"
:precision="2"
style="width: 100%"
placeholder="请输入栏舍面积"
@change="handleFieldChange('area', $event)"
/>
</a-form-item>
<a-form-item label="位置描述" name="location">
<a-textarea
v-model="formData.location"
v-model:value="formData.location"
:rows="3"
placeholder="请输入栏舍位置描述"
@input="handleFieldChange('location', $event.target.value)"
@change="handleFieldChange('location', $event.target.value)"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group
v-model="formData.status"
@change="handleFieldChange('status', $event.target.value)"
v-model:value="formData.status"
>
<a-radio value="启用">启用</a-radio>
<a-radio value="停用">停用</a-radio>
@@ -182,11 +171,9 @@
<a-form-item label="备注" name="remark">
<a-textarea
v-model="formData.remark"
v-model:value="formData.remark"
:rows="3"
placeholder="请输入备注信息"
@input="handleFieldChange('remark', $event.target.value)"
@change="handleFieldChange('remark', $event.target.value)"
/>
</a-form-item>
</a-form>
@@ -219,7 +206,7 @@
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue'
import { ref, reactive, onMounted, computed, nextTick } from 'vue'
import { message, Modal } from 'ant-design-vue'
import {
PlusOutlined,
@@ -415,9 +402,9 @@ const loadData = async () => {
pageSize: pagination.pageSize
}
// 精确匹配栏舍名称
if (searchValue.value) {
params.name = searchValue.value
// 搜索关键词(后端使用 search 参数)
if (searchValue.value && searchValue.value.trim()) {
params.search = searchValue.value.trim()
}
console.log('📤 [栏舍设置] 发送请求参数:', params)
@@ -454,7 +441,7 @@ const handleAdd = () => {
modalVisible.value = true
}
const handleEdit = (record) => {
const handleEdit = async (record) => {
console.log('🔄 [栏舍设置] 开始编辑操作')
console.log('📋 [栏舍设置] 原始记录数据:', {
id: record.id,
@@ -471,10 +458,30 @@ const handleEdit = (record) => {
})
modalTitle.value = '编辑栏舍'
Object.assign(formData, record)
// 填充表单数据 - 使用直接赋值确保响应式更新
formData.id = record.id || null
formData.name = record.name || ''
formData.code = record.code || ''
formData.type = record.type || ''
formData.capacity = record.capacity || 0
formData.currentCount = record.currentCount || 0
formData.area = record.area || null
formData.location = record.location || ''
formData.status = record.status || '启用'
formData.remark = record.remark || ''
formData.farmId = record.farmId || record.farm_id || null
console.log('📝 [栏舍设置] 表单数据已填充:', formData)
// 打开模态框
modalVisible.value = true
// 等待模态框和表单渲染完成
await nextTick()
await new Promise(resolve => setTimeout(resolve, 200))
console.log('✅ [栏舍设置] 模态框已打开,表单应该已填充')
}
const handleDelete = (record) => {

View File

@@ -168,18 +168,15 @@
>
<a-form-item label="牛只耳号" name="earNumber">
<a-input
v-model="formData.earNumber"
v-model:value="formData.earNumber"
placeholder="请输入牛只耳号"
@input="handleFieldChange('earNumber', $event.target.value)"
@change="handleFieldChange('earNumber', $event.target.value)"
/>
</a-form-item>
<a-form-item label="转出栏舍" name="fromPenId">
<a-select
v-model="formData.fromPenId"
v-model:value="formData.fromPenId"
placeholder="请选择转出栏舍"
@change="handleFieldChange('fromPenId', $event)"
>
<a-select-option
v-for="pen in penList"
@@ -193,9 +190,8 @@
<a-form-item label="转入栏舍" name="toPenId">
<a-select
v-model="formData.toPenId"
v-model:value="formData.toPenId"
placeholder="请选择转入栏舍"
@change="handleFieldChange('toPenId', $event)"
>
<a-select-option
v-for="pen in penList"
@@ -209,18 +205,16 @@
<a-form-item label="转栏日期" name="transferDate">
<a-date-picker
v-model="formData.transferDate"
v-model:value="formData.transferDate"
style="width: 100%"
placeholder="请选择转栏日期"
@change="handleFieldChange('transferDate', $event)"
/>
</a-form-item>
<a-form-item label="转栏原因" name="reason">
<a-select
v-model="formData.reason"
v-model:value="formData.reason"
placeholder="请选择转栏原因"
@change="handleFieldChange('reason', $event)"
>
<a-select-option value="正常调栏">正常调栏</a-select-option>
<a-select-option value="疾病治疗">疾病治疗</a-select-option>
@@ -233,17 +227,14 @@
<a-form-item label="操作人员" name="operator">
<a-input
v-model="formData.operator"
v-model:value="formData.operator"
placeholder="请输入操作人员姓名"
@input="handleFieldChange('operator', $event.target.value)"
@change="handleFieldChange('operator', $event.target.value)"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group
v-model="formData.status"
@change="handleFieldChange('status', $event.target.value)"
v-model:value="formData.status"
>
<a-radio value="已完成">已完成</a-radio>
<a-radio value="进行中">进行中</a-radio>
@@ -252,11 +243,9 @@
<a-form-item label="备注" name="remark">
<a-textarea
v-model="formData.remark"
v-model:value="formData.remark"
:rows="3"
placeholder="请输入备注信息"
@input="handleFieldChange('remark', $event.target.value)"
@change="handleFieldChange('remark', $event.target.value)"
/>
</a-form-item>
</a-form>
@@ -567,7 +556,7 @@ const handleAdd = () => {
modalVisible.value = true
}
const handleEdit = (record) => {
const handleEdit = async (record) => {
console.log('🔄 [转栏记录] 开始编辑操作')
console.log('📋 [转栏记录] 原始记录数据:', {
id: record.id,
@@ -584,20 +573,50 @@ const handleEdit = (record) => {
})
modalTitle.value = '编辑转栏记录'
Object.assign(formData, {
id: record.id,
earNumber: record.earNumber,
fromPenId: record.fromPenId,
toPenId: record.toPenId,
transferDate: record.transferDate ? dayjs(record.transferDate) : null,
reason: record.reason,
operator: record.operator,
status: record.status,
remark: record.remark || ''
})
// 确保栏舍列表已加载
if (penList.value.length === 0) {
await loadPenList()
}
// 处理日期转换
let transferDateValue = null
if (record.transferDate) {
if (typeof record.transferDate === 'string') {
transferDateValue = dayjs(record.transferDate)
} else if (record.transferDate instanceof Date) {
transferDateValue = dayjs(record.transferDate)
} else if (record.transferDate && typeof record.transferDate === 'object' && record.transferDate.format) {
// 如果已经是 dayjs 对象,直接使用
transferDateValue = record.transferDate
} else {
transferDateValue = dayjs(record.transferDate)
}
console.log('📅 [转栏记录] 日期转换:', record.transferDate, '->', transferDateValue?.format('YYYY-MM-DD'))
}
// 填充表单数据
formData.id = record.id
formData.earNumber = record.earNumber || ''
formData.fromPenId = record.fromPenId || null
formData.toPenId = record.toPenId || null
formData.transferDate = transferDateValue
formData.reason = record.reason || ''
formData.operator = record.operator || ''
formData.status = record.status || '进行中'
formData.remark = record.remark || ''
console.log('📝 [转栏记录] 表单数据已填充:', formData)
console.log('📝 [转栏记录] transferDate 是否为 dayjs:', formData.transferDate && typeof formData.transferDate.format === 'function')
// 打开模态框
modalVisible.value = true
// 等待模态框和表单渲染完成
await nextTick()
await new Promise(resolve => setTimeout(resolve, 200))
console.log('✅ [转栏记录] 模态框已打开,表单应该已填充')
}
const handleDelete = (record) => {

View File

@@ -95,19 +95,16 @@
<a-col :span="12">
<a-form-item label="栏舍名" name="name" required>
<a-input
v-model="formData.name"
placeholder="请输入栏舍名"
@input="(e) => { console.log('栏舍名输入:', e.target.value); formData.name = e.target.value; }"
@change="(e) => { console.log('栏舍名变化:', e.target.value); }"
v-model:value="formData.name"
placeholder="请输入栏舍名"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="动物类型" name="animal_type" required>
<a-select
v-model="formData.animal_type"
v-model:value="formData.animal_type"
placeholder="请选择动物类型"
@change="(value) => { console.log('动物类型变化:', value); }"
>
<a-select-option value="马"></a-select-option>
<a-select-option value="牛"></a-select-option>
@@ -123,20 +120,16 @@
<a-col :span="12">
<a-form-item label="栏舍类型" name="pen_type">
<a-input
v-model="formData.pen_type"
v-model:value="formData.pen_type"
placeholder="请输入栏舍类型"
@input="(e) => { console.log('栏舍类型输入:', e.target.value); formData.pen_type = e.target.value; }"
@change="(e) => { console.log('栏舍类型变化:', e.target.value); }"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="负责人" name="responsible" required>
<a-input
v-model="formData.responsible"
v-model:value="formData.responsible"
placeholder="请输入负责人"
@input="(e) => { console.log('负责人输入:', e.target.value); formData.responsible = e.target.value; }"
@change="(e) => { console.log('负责人变化:', e.target.value); }"
/>
</a-form-item>
</a-col>
@@ -146,20 +139,18 @@
<a-col :span="12">
<a-form-item label="容量" name="capacity" required>
<a-input-number
v-model="formData.capacity"
v-model:value="formData.capacity"
:min="1"
:max="10000"
placeholder="请输入容量"
style="width: 100%"
@change="(value) => { console.log('容量变化:', value); formData.capacity = value; }"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="状态" name="status">
<a-switch
:checked="formData.status"
@change="(checked) => { console.log('状态变化:', checked); formData.status = checked; }"
v-model:checked="formData.status"
:checked-children="'开启'"
:un-checked-children="'关闭'"
/>
@@ -169,11 +160,9 @@
<a-form-item label="备注" name="description">
<a-textarea
v-model="formData.description"
v-model:value="formData.description"
placeholder="请输入备注信息"
:rows="3"
@input="(e) => { console.log('备注输入:', e.target.value); formData.description = e.target.value; }"
@change="(e) => { console.log('备注变化:', e.target.value); }"
/>
</a-form-item>
</a-form>
@@ -182,7 +171,7 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { PlusOutlined, SearchOutlined, ExportOutlined } from '@ant-design/icons-vue'
import { api } from '../utils/api'
@@ -364,21 +353,21 @@ const showAddModal = () => {
modalVisible.value = true
}
const handleEdit = (record) => {
const handleEdit = async (record) => {
console.log('=== 开始编辑栏舍 ===')
console.log('点击编辑按钮,原始记录数据:', record)
isEdit.value = true
Object.assign(formData, {
id: record.id,
name: record.name,
animal_type: record.animal_type,
pen_type: record.pen_type,
responsible: record.responsible,
capacity: record.capacity,
status: record.status,
description: record.description
})
// 填充表单数据 - 使用直接赋值确保响应式更新
formData.id = record.id || null
formData.name = record.name || ''
formData.animal_type = record.animal_type || ''
formData.pen_type = record.pen_type || ''
formData.responsible = record.responsible || ''
formData.capacity = record.capacity || 0
formData.status = record.status || false
formData.description = record.description || ''
console.log('编辑模式:表单数据已填充')
console.log('formData对象:', formData)
@@ -390,8 +379,14 @@ const handleEdit = (record) => {
console.log('formData.status:', formData.status)
console.log('formData.description:', formData.description)
// 打开模态框
modalVisible.value = true
console.log('编辑模态框已打开')
// 等待模态框和表单渲染完成
await nextTick()
await new Promise(resolve => setTimeout(resolve, 200))
console.log('编辑模态框已打开,表单应该已填充')
}
const handleDelete = (record) => {

View File

@@ -166,24 +166,24 @@
<!-- 操作 -->
<template v-else-if="column.dataIndex === 'action'">
<div style="display: flex; flex-wrap: wrap; gap: 2px;">
<a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="editDevice(record)">
<!-- <a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="editDevice(record)">
修改绑定
</a-button>
</a-button> -->
<a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="clearData(record)">
溯源二维码
</a-button>
<a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="bindDevice(record)">
绑定信息
</a-button>
<a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="setDefault(record)">
<!-- <a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="setDefault(record)">
设置频次
</a-button>
<a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="viewTrack(record)">
</a-button> -->
<!-- <a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="viewTrack(record)">
日志
</a-button>
<a-button type="link" size="small" style="color: #1890ff; padding: 0 4px;" @click="viewTrack(record)">
查看轨迹
</a-button>
</a-button> -->
</div>
</template>
</template>
@@ -553,6 +553,24 @@
</div>
</a-modal>
<!-- 溯源二维码模态框 -->
<a-modal
:open="qrcodeVisible"
title="溯源二维码"
:footer="null"
width="450px"
@cancel="handleQRCodeCancel"
>
<div class="qrcode-modal-content">
<div class="qrcode-container">
<img v-if="qrcodeUrl" :src="qrcodeUrl" alt="溯源二维码" class="qrcode-image" />
</div>
<div class="device-sn-display">
{{ currentDeviceSn }}
</div>
</div>
</a-modal>
<!-- 添加/编辑模态框 -->
<a-modal
:open="modalVisible"
@@ -613,6 +631,7 @@ import { PlusOutlined, ReloadOutlined, ExportOutlined, EnvironmentOutlined, Crow
import { api, directApi } from '../utils/api'
import { ExportUtils } from '../utils/exportUtils'
import { loadBMapScript, createMap } from '@/utils/mapService'
import QRCode from 'qrcode'
// 响应式数据
const collars = ref([])
@@ -643,6 +662,11 @@ const currentLocation = ref(null)
const baiduMap = ref(null)
const locationMarker = ref(null)
// 二维码相关数据
const qrcodeVisible = ref(false)
const qrcodeUrl = ref('')
const currentDeviceSn = ref('')
// 标签页配置
const tabs = [
{ key: 'identity', label: '身份信息' },
@@ -1013,8 +1037,30 @@ const editDevice = (record) => {
message.info(`修改设备 ${record.deviceId}`)
}
const clearData = (record) => {
message.info(`清除设备 ${record.deviceId} 的数据`)
// 显示溯源二维码
const clearData = async (record) => {
try {
const deviceSn = record.sn
if (!deviceSn) {
message.warning('设备编号不存在')
return
}
currentDeviceSn.value = deviceSn
const traceUrl = `https://farm.aiotagro.com/source/source-index.html?device_sn=${deviceSn}`
// 生成二维码
const qrCodeDataUrl = await QRCode.toDataURL(traceUrl, {
width: 250,
margin: 2
})
qrcodeUrl.value = qrCodeDataUrl
qrcodeVisible.value = true
} catch (error) {
console.error('生成二维码失败:', error)
message.error('生成二维码失败,请重试')
}
}
// 搜索项圈
@@ -1334,6 +1380,13 @@ const handleLocationCancel = () => {
}
}
// 关闭二维码模态框
const handleQRCodeCancel = () => {
qrcodeVisible.value = false
qrcodeUrl.value = ''
currentDeviceSn.value = ''
}
// 初始化百度地图
const initBaiduMap = async () => {
if (!currentLocation.value) return
@@ -1773,4 +1826,37 @@ onUnmounted(() => {
border-top: 1px solid #f0f0f0;
margin-top: 20px;
}
/* 二维码模态框样式 */
.qrcode-modal-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.qrcode-container {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}
.qrcode-image {
width: 250px;
height: 250px;
border: 1px solid #e8e8e8;
border-radius: 4px;
}
.device-sn-display {
font-size: 16px;
font-weight: 500;
color: #333;
text-align: center;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
width: 100%;
}
</style>

View File

@@ -154,18 +154,18 @@
<!-- 操作列 -->
<template #action="{ record }">
<div class="action-buttons">
<a-button type="link" size="small" @click="bindLivestock(record)">
<!-- <a-button type="link" size="small" @click="bindLivestock(record)">
绑定牲畜
</a-button>
</a-button> -->
<a-button type="link" size="small" @click="showQRCode(record)">
溯源二维码
</a-button>
<a-button type="link" size="small" @click="showBindingInfo(record)">
绑定信息
</a-button>
<a-button type="link" size="small" @click="showLog(record)">
<!-- <a-button type="link" size="small" @click="showLog(record)">
日志
</a-button>
</a-button> -->
</div>
</template>
</a-table>
@@ -219,6 +219,24 @@
</div>
</a-modal>
<!-- 溯源二维码模态框 -->
<a-modal
:open="qrcodeVisible"
title="溯源二维码"
:footer="null"
width="450px"
@cancel="handleQRCodeCancel"
>
<div class="qrcode-modal-content">
<div class="qrcode-container">
<img v-if="qrcodeUrl" :src="qrcodeUrl" alt="溯源二维码" class="qrcode-image" />
</div>
<div class="device-sn-display">
{{ currentDeviceSn }}
</div>
</div>
</a-modal>
<!-- 绑定信息模态框 -->
<a-modal
:open="bindInfoVisible"
@@ -337,6 +355,7 @@ import { EnvironmentOutlined, SearchOutlined, ExportOutlined } from '@ant-design
import { api, directApi } from '../utils/api'
import { ExportUtils } from '../utils/exportUtils'
import { formatBindingInfo } from '../utils/fieldMappings'
import QRCode from 'qrcode'
// 响应式数据
const loading = ref(false)
@@ -362,6 +381,11 @@ const bindInfoData = ref({
const activeTab = ref('basic')
const currentEartagNumber = ref('')
// 二维码相关数据
const qrcodeVisible = ref(false)
const qrcodeUrl = ref('')
const currentDeviceSn = ref('')
// 分页配置
const pagination = reactive({
current: 1,
@@ -752,9 +776,30 @@ const bindLivestock = (record) => {
message.info('绑定牲畜功能开发中...')
}
// 显示二维码
const showQRCode = (record) => {
message.info('溯源二维码功能开发中...')
// 显示溯源二维码
const showQRCode = async (record) => {
try {
const deviceSn = record.eartagNumber
if (!deviceSn) {
message.warning('设备编号不存在')
return
}
currentDeviceSn.value = deviceSn
const traceUrl = `https://farm.aiotagro.com/source/source-index.html?device_sn=${deviceSn}`
// 生成二维码
const qrCodeDataUrl = await QRCode.toDataURL(traceUrl, {
width: 250,
margin: 2
})
qrcodeUrl.value = qrCodeDataUrl
qrcodeVisible.value = true
} catch (error) {
console.error('生成二维码失败:', error)
message.error('生成二维码失败,请重试')
}
}
// 关闭绑定信息模态框
@@ -773,6 +818,13 @@ const handleBindingInfoCancel = () => {
activeTab.value = 'basic'
}
// 关闭二维码模态框
const handleQRCodeCancel = () => {
qrcodeVisible.value = false
qrcodeUrl.value = ''
currentDeviceSn.value = ''
}
// 显示绑定信息
const showBindingInfo = async (record) => {
try {
@@ -1238,6 +1290,39 @@ onUnmounted(() => {
font-weight: 500;
}
/* 二维码模态框样式 */
.qrcode-modal-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.qrcode-container {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}
.qrcode-image {
width: 250px;
height: 250px;
border: 1px solid #e8e8e8;
border-radius: 4px;
}
.device-sn-display {
font-size: 16px;
font-weight: 500;
color: #333;
text-align: center;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
width: 100%;
}
@media (max-width: 768px) {
.search-bar .ant-col {
margin-bottom: 12px;

View File

@@ -96,9 +96,9 @@
<a-button type="link" class="action-link" @click="viewLocation(record)">
查看定位
</a-button>
<a-button type="link" class="action-link" @click="viewCollectionInfo(record)">
<!-- <a-button type="link" class="action-link" @click="viewCollectionInfo(record)">
查看采集信息
</a-button>
</a-button> -->
</div>
</template>
</template>

View File

@@ -60,7 +60,7 @@ export default defineConfig(({ mode }) => {
__APP_ENV__: JSON.stringify(env),
// 在生产环境中强制使用正确的API URL
'import.meta.env.VITE_API_BASE_URL': JSON.stringify(
mode === 'production' ? 'https://ad.ningmuyun.com/farm/api' : (env.VITE_API_BASE_URL || '/api')
mode === 'production' ? 'https://ad.liaoniuyun.com/farm/api' : (env.VITE_API_BASE_URL || '/api')
)
}
}