提交 85bb0940 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 e07a9edb
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LAB_URL=https://bi.ezijing.com/bi/
VITE_LAB_URL=https://digitalmarketinglab.ezijing.com
......@@ -11,9 +11,9 @@
"@element-plus/icons-vue": "^2.0.10",
"@tinymce/tinymce-vue": "^5.0.0",
"@vant/area-data": "^1.3.2",
"@vueuse/core": "^9.4.0",
"@vueuse/head": "^0.9.8",
"@vueuse/math": "^9.4.0",
"@vueuse/core": "^9.5.0",
"@vueuse/head": "^1.0.16",
"@vueuse/math": "^9.5.0",
"ali-oss": "^6.17.1",
"axios": "^1.1.3",
"blueimp-md5": "^2.19.0",
......@@ -44,12 +44,12 @@
"@vue/eslint-config-typescript": "^11.0.2",
"@vue/tsconfig": "^0.1.3",
"chalk": "^5.1.2",
"eslint": "^8.26.0",
"eslint": "^8.27.0",
"eslint-plugin-vue": "^9.7.0",
"sass": "^1.56.1",
"typescript": "~4.8.4",
"typescript": "~4.9.3",
"unplugin-auto-import": "^0.11.4",
"vite": "^3.2.3",
"vite": "^3.2.4",
"vite-plugin-checker": "^0.5.1",
"vue-tsc": "^1.0.9"
}
......@@ -610,6 +610,43 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@unhead/dom": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/dom/-/dom-1.0.1.tgz",
"integrity": "sha512-P4jkzDhQR9tJ+IATV/hfpdxngHglP5KcaaKM+fuRTLy1ilCYNNdp9QhnkHIp5k8C+c8xl7VsfIiGGUe0xBA1UA==",
"dependencies": {
"@unhead/schema": "1.0.1"
}
},
"node_modules/@unhead/schema": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/schema/-/schema-1.0.1.tgz",
"integrity": "sha512-aUEC8NwAPRb33J/19tna0u9YDMWW0O6KVi48XKf+OJVTr3UKrk34E8xzuv4G5WZl/oZ6gIjpbPymV34VirV/bQ==",
"dependencies": {
"@zhead/schema": "1.0.1",
"hookable": "^5.4.1"
}
},
"node_modules/@unhead/ssr": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/ssr/-/ssr-1.0.1.tgz",
"integrity": "sha512-IBP48cDMfKp6bIMXmSv55chRlQ73jGw79EJ5WBQHl+e4jt5sTjeo2ditPiBjoIgQdpdSlybuiYyvk8+cJHCsVw==",
"dependencies": {
"@unhead/schema": "1.0.1"
}
},
"node_modules/@unhead/vue": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/vue/-/vue-1.0.1.tgz",
"integrity": "sha512-MiiKk5R02r+iQeY+Jz2uOxy+QuJFCraDi+U4QSnDFnMkBo9Pix7IuHDQQGJjZoZoZnR/xvJBk/esV3lhKjTk8w==",
"dependencies": {
"@unhead/schema": "1.0.1",
"hookable": "^5.4.1"
},
"peerDependencies": {
"vue": ">=2.7 || >=3"
}
},
"node_modules/@vant/area-data": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@vant/area-data/-/area-data-1.3.2.tgz",
......@@ -896,13 +933,13 @@
}
},
"node_modules/@vueuse/core": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.4.0.tgz",
"integrity": "sha512-JzgenGj1ZF2BHOen5rsFiAyyI9sXAv7aKhNLlm9b7SwYQeKTcxTWdhudonURCSP3Egl9NQaRBzes2lv/1JUt/Q==",
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.5.0.tgz",
"integrity": "sha512-6GsWBsJHEb3sYw15mbLrcbslAVY45pkzjJYTKYKCXv88z7srAF0VEW0q+oXKsl58tCbqooplInahXFg8Yo1m4w==",
"dependencies": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.4.0",
"@vueuse/shared": "9.4.0",
"@vueuse/metadata": "9.5.0",
"@vueuse/shared": "9.5.0",
"vue-demi": "*"
}
},
......@@ -929,24 +966,25 @@
}
},
"node_modules/@vueuse/head": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/@vueuse/head/-/head-0.9.8.tgz",
"integrity": "sha512-zt8+JksoVFKxRvmABlaUHA62w+8nOcD8cJnaJ0+SHcr6xaIP3GXgh7/n2TzUoWw4l3d9UxRNs+tapgHdsQ7RbA==",
"version": "1.0.16",
"resolved": "https://registry.npmmirror.com/@vueuse/head/-/head-1.0.16.tgz",
"integrity": "sha512-0nYyrnMh42hE4uL3E0OYY5lFg03BIL26+pE1e6285Oh7pebHgmZlmSCFRmLAlVMSxhp/+6aOrIZLIObm7SKNfg==",
"dependencies": {
"@vueuse/shared": "^9.3.0",
"@zhead/schema": "^0.8.5",
"@zhead/schema-vue": "^0.8.5"
"@unhead/dom": "^1.0.1",
"@unhead/schema": "^1.0.1",
"@unhead/ssr": "^1.0.1",
"@unhead/vue": "^1.0.1"
},
"peerDependencies": {
"vue": ">=2.7 || >=3"
}
},
"node_modules/@vueuse/math": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/math/-/math-9.4.0.tgz",
"integrity": "sha512-dugk8X+zrvpV9ATVg+s/NJjBhd6ZnzVTIZYRZRuJikqnNq5T4OIVH8iYVAS11ONAXj8KGfUeXnMvEpIq4cU6fw==",
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/math/-/math-9.5.0.tgz",
"integrity": "sha512-dPr5CkxE4Oo+OEvTqPfAZ8Lv1AVbnLH2N5gJSm5EWykxGPLbSaimUIckqXXR8DDyvaWIV545tELekpFUHLoFmw==",
"dependencies": {
"@vueuse/shared": "9.4.0",
"@vueuse/shared": "9.5.0",
"vue-demi": "*"
}
},
......@@ -976,21 +1014,21 @@
}
},
"node_modules/@vueuse/metadata": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.4.0.tgz",
"integrity": "sha512-7GKMdGAsJyQJl35MYOz/RDpP0FxuiZBRDSN79QIPbdqYx4Sd0sVTnIC68KJ6Oln0t0SouvSUMvRHuno216Ud2Q=="
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.5.0.tgz",
"integrity": "sha512-4M1AyPZmIv41pym+K5+4wup3bKuYebbH8w8BROY1hmT7rIwcyS4tEL+UsGz0Hiu1FCOxcoBrwtAizc0YmBJjyQ=="
},
"node_modules/@vueuse/shared": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.4.0.tgz",
"integrity": "sha512-fTuem51KwMCnqUKkI8B57qAIMcFovtGgsCtAeqxIzH3i6nE9VYge+gVfneNHAAy7lj8twbkNfqQSygOPJTm4tQ==",
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.5.0.tgz",
"integrity": "sha512-HnnCWU1Vg9CVWRCcI8ohDKDRB2Sc4bTgT1XAIaoLSfVHHn+TKbrox6pd3klCSw4UDxkhDfOk8cAdcK+Z5KleCA==",
"dependencies": {
"vue-demi": "*"
}
},
"node_modules/@vueuse/shared/node_modules/vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"hasInstallScript": true,
"bin": {
......@@ -1000,9 +1038,6 @@
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
......@@ -1022,39 +1057,9 @@
}
},
"node_modules/@zhead/schema": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema/-/schema-0.8.5.tgz",
"integrity": "sha512-1S3Otr2zpl1zwP72dNseVXQNG9tnTQ6hHUEUYwINvBjRj6bHcUwdE+Itc9OLxnGAJT/7p8P7GHGo5sshXJNJsA==",
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
"node_modules/@zhead/schema-raw": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-raw/-/schema-raw-0.8.5.tgz",
"integrity": "sha512-Aq+9mksf5zbtj7HYluT6PVyfpQ6z7mja9MzjFxg76Vt+Q9i0oL1XN6ZYaCXImWRafwbyAxjFQ5aUCVyFn79OpA==",
"dependencies": {
"@zhead/schema": "0.8.5"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
"node_modules/@zhead/schema-vue": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-vue/-/schema-vue-0.8.5.tgz",
"integrity": "sha512-6aXjYy3fZVeYBLrHcJQqzqwzC/2tafRO5UxZEgBHnryRnzeLNZV6nTptDvIPWiJObMoJTK21vbg3gkfLNQg84g==",
"dependencies": {
"@vueuse/shared": "^9.2.0",
"@zhead/schema": "0.8.5",
"@zhead/schema-raw": "0.8.5"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
},
"peerDependencies": {
"vue": ">=2.7 || >=3"
}
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@zhead/schema/-/schema-1.0.1.tgz",
"integrity": "sha512-n6BDs+MjSOesuv6krG2QGyCPfdndxWX0M/G2wEGu1SPHc5jLHHi3EY1+vQvudFVXRVXquZHKsDPE7pSyeyGgHg=="
},
"node_modules/acorn": {
"version": "8.8.0",
......@@ -2154,9 +2159,9 @@
}
},
"node_modules/eslint": {
"version": "8.26.0",
"resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.26.0.tgz",
"integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==",
"version": "8.27.0",
"resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.27.0.tgz",
"integrity": "sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==",
"dev": true,
"dependencies": {
"@eslint/eslintrc": "^1.3.3",
......@@ -2788,6 +2793,11 @@
"he": "bin/he"
}
},
"node_modules/hookable": {
"version": "5.4.2",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.4.2.tgz",
"integrity": "sha512-6rOvaUiNKy9lET1X0ECnyZ5O5kSV0PJbtA5yZUgdEF7fGJEVwSLSislltyt7nFwVVALYHQJtfGeAR2Y0A0uJkg=="
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
......@@ -4345,9 +4355,9 @@
}
},
"node_modules/typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"version": "4.9.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"devOptional": true,
"bin": {
"tsc": "bin/tsc",
......@@ -4603,9 +4613,9 @@
}
},
"node_modules/vite": {
"version": "3.2.3",
"resolved": "https://registry.npmmirror.com/vite/-/vite-3.2.3.tgz",
"integrity": "sha512-h8jl1TZ76eGs3o2dIBSsvXDLb1m/Ec1iej8ZMdz+PsaFUsftZeWe2CZOI3qogEsMNaywc17gu0q6cQDzh/weCQ==",
"version": "3.2.4",
"resolved": "https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz",
"integrity": "sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==",
"dev": true,
"dependencies": {
"esbuild": "^0.15.9",
......@@ -5425,6 +5435,40 @@
"eslint-visitor-keys": "^3.3.0"
}
},
"@unhead/dom": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/dom/-/dom-1.0.1.tgz",
"integrity": "sha512-P4jkzDhQR9tJ+IATV/hfpdxngHglP5KcaaKM+fuRTLy1ilCYNNdp9QhnkHIp5k8C+c8xl7VsfIiGGUe0xBA1UA==",
"requires": {
"@unhead/schema": "1.0.1"
}
},
"@unhead/schema": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/schema/-/schema-1.0.1.tgz",
"integrity": "sha512-aUEC8NwAPRb33J/19tna0u9YDMWW0O6KVi48XKf+OJVTr3UKrk34E8xzuv4G5WZl/oZ6gIjpbPymV34VirV/bQ==",
"requires": {
"@zhead/schema": "1.0.1",
"hookable": "^5.4.1"
}
},
"@unhead/ssr": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/ssr/-/ssr-1.0.1.tgz",
"integrity": "sha512-IBP48cDMfKp6bIMXmSv55chRlQ73jGw79EJ5WBQHl+e4jt5sTjeo2ditPiBjoIgQdpdSlybuiYyvk8+cJHCsVw==",
"requires": {
"@unhead/schema": "1.0.1"
}
},
"@unhead/vue": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@unhead/vue/-/vue-1.0.1.tgz",
"integrity": "sha512-MiiKk5R02r+iQeY+Jz2uOxy+QuJFCraDi+U4QSnDFnMkBo9Pix7IuHDQQGJjZoZoZnR/xvJBk/esV3lhKjTk8w==",
"requires": {
"@unhead/schema": "1.0.1",
"hookable": "^5.4.1"
}
},
"@vant/area-data": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@vant/area-data/-/area-data-1.3.2.tgz",
......@@ -5670,13 +5714,13 @@
"requires": {}
},
"@vueuse/core": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.4.0.tgz",
"integrity": "sha512-JzgenGj1ZF2BHOen5rsFiAyyI9sXAv7aKhNLlm9b7SwYQeKTcxTWdhudonURCSP3Egl9NQaRBzes2lv/1JUt/Q==",
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.5.0.tgz",
"integrity": "sha512-6GsWBsJHEb3sYw15mbLrcbslAVY45pkzjJYTKYKCXv88z7srAF0VEW0q+oXKsl58tCbqooplInahXFg8Yo1m4w==",
"requires": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.4.0",
"@vueuse/shared": "9.4.0",
"@vueuse/metadata": "9.5.0",
"@vueuse/shared": "9.5.0",
"vue-demi": "*"
},
"dependencies": {
......@@ -5689,21 +5733,22 @@
}
},
"@vueuse/head": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/@vueuse/head/-/head-0.9.8.tgz",
"integrity": "sha512-zt8+JksoVFKxRvmABlaUHA62w+8nOcD8cJnaJ0+SHcr6xaIP3GXgh7/n2TzUoWw4l3d9UxRNs+tapgHdsQ7RbA==",
"version": "1.0.16",
"resolved": "https://registry.npmmirror.com/@vueuse/head/-/head-1.0.16.tgz",
"integrity": "sha512-0nYyrnMh42hE4uL3E0OYY5lFg03BIL26+pE1e6285Oh7pebHgmZlmSCFRmLAlVMSxhp/+6aOrIZLIObm7SKNfg==",
"requires": {
"@vueuse/shared": "^9.3.0",
"@zhead/schema": "^0.8.5",
"@zhead/schema-vue": "^0.8.5"
"@unhead/dom": "^1.0.1",
"@unhead/schema": "^1.0.1",
"@unhead/ssr": "^1.0.1",
"@unhead/vue": "^1.0.1"
}
},
"@vueuse/math": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/math/-/math-9.4.0.tgz",
"integrity": "sha512-dugk8X+zrvpV9ATVg+s/NJjBhd6ZnzVTIZYRZRuJikqnNq5T4OIVH8iYVAS11ONAXj8KGfUeXnMvEpIq4cU6fw==",
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/math/-/math-9.5.0.tgz",
"integrity": "sha512-dPr5CkxE4Oo+OEvTqPfAZ8Lv1AVbnLH2N5gJSm5EWykxGPLbSaimUIckqXXR8DDyvaWIV545tELekpFUHLoFmw==",
"requires": {
"@vueuse/shared": "9.4.0",
"@vueuse/shared": "9.5.0",
"vue-demi": "*"
},
"dependencies": {
......@@ -5716,21 +5761,21 @@
}
},
"@vueuse/metadata": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.4.0.tgz",
"integrity": "sha512-7GKMdGAsJyQJl35MYOz/RDpP0FxuiZBRDSN79QIPbdqYx4Sd0sVTnIC68KJ6Oln0t0SouvSUMvRHuno216Ud2Q=="
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.5.0.tgz",
"integrity": "sha512-4M1AyPZmIv41pym+K5+4wup3bKuYebbH8w8BROY1hmT7rIwcyS4tEL+UsGz0Hiu1FCOxcoBrwtAizc0YmBJjyQ=="
},
"@vueuse/shared": {
"version": "9.4.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.4.0.tgz",
"integrity": "sha512-fTuem51KwMCnqUKkI8B57qAIMcFovtGgsCtAeqxIzH3i6nE9VYge+gVfneNHAAy7lj8twbkNfqQSygOPJTm4tQ==",
"version": "9.5.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.5.0.tgz",
"integrity": "sha512-HnnCWU1Vg9CVWRCcI8ohDKDRB2Sc4bTgT1XAIaoLSfVHHn+TKbrox6pd3klCSw4UDxkhDfOk8cAdcK+Z5KleCA==",
"requires": {
"vue-demi": "*"
},
"dependencies": {
"vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"requires": {}
}
......@@ -5742,27 +5787,9 @@
"integrity": "sha512-HHXP9hskkFQHy8QxxUXkS7946FFIhYVfGqsk0WLwllmexN9x/+R4UBLvurHEuyXRfVEObVR8APuQehykLviwSQ=="
},
"@zhead/schema": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema/-/schema-0.8.5.tgz",
"integrity": "sha512-1S3Otr2zpl1zwP72dNseVXQNG9tnTQ6hHUEUYwINvBjRj6bHcUwdE+Itc9OLxnGAJT/7p8P7GHGo5sshXJNJsA=="
},
"@zhead/schema-raw": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-raw/-/schema-raw-0.8.5.tgz",
"integrity": "sha512-Aq+9mksf5zbtj7HYluT6PVyfpQ6z7mja9MzjFxg76Vt+Q9i0oL1XN6ZYaCXImWRafwbyAxjFQ5aUCVyFn79OpA==",
"requires": {
"@zhead/schema": "0.8.5"
}
},
"@zhead/schema-vue": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-vue/-/schema-vue-0.8.5.tgz",
"integrity": "sha512-6aXjYy3fZVeYBLrHcJQqzqwzC/2tafRO5UxZEgBHnryRnzeLNZV6nTptDvIPWiJObMoJTK21vbg3gkfLNQg84g==",
"requires": {
"@vueuse/shared": "^9.2.0",
"@zhead/schema": "0.8.5",
"@zhead/schema-raw": "0.8.5"
}
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@zhead/schema/-/schema-1.0.1.tgz",
"integrity": "sha512-n6BDs+MjSOesuv6krG2QGyCPfdndxWX0M/G2wEGu1SPHc5jLHHi3EY1+vQvudFVXRVXquZHKsDPE7pSyeyGgHg=="
},
"acorn": {
"version": "8.8.0",
......@@ -6524,9 +6551,9 @@
}
},
"eslint": {
"version": "8.26.0",
"resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.26.0.tgz",
"integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==",
"version": "8.27.0",
"resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.27.0.tgz",
"integrity": "sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==",
"dev": true,
"requires": {
"@eslint/eslintrc": "^1.3.3",
......@@ -7031,6 +7058,11 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"hookable": {
"version": "5.4.2",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.4.2.tgz",
"integrity": "sha512-6rOvaUiNKy9lET1X0ECnyZ5O5kSV0PJbtA5yZUgdEF7fGJEVwSLSislltyt7nFwVVALYHQJtfGeAR2Y0A0uJkg=="
},
"http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
......@@ -8273,9 +8305,9 @@
"dev": true
},
"typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"version": "4.9.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"devOptional": true
},
"ua-parser-js": {
......@@ -8482,9 +8514,9 @@
}
},
"vite": {
"version": "3.2.3",
"resolved": "https://registry.npmmirror.com/vite/-/vite-3.2.3.tgz",
"integrity": "sha512-h8jl1TZ76eGs3o2dIBSsvXDLb1m/Ec1iej8ZMdz+PsaFUsftZeWe2CZOI3qogEsMNaywc17gu0q6cQDzh/weCQ==",
"version": "3.2.4",
"resolved": "https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz",
"integrity": "sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==",
"dev": true,
"requires": {
"esbuild": "^0.15.9",
......
......@@ -17,9 +17,9 @@
"@element-plus/icons-vue": "^2.0.10",
"@tinymce/tinymce-vue": "^5.0.0",
"@vant/area-data": "^1.3.2",
"@vueuse/core": "^9.4.0",
"@vueuse/head": "^0.9.8",
"@vueuse/math": "^9.4.0",
"@vueuse/core": "^9.5.0",
"@vueuse/head": "^1.0.16",
"@vueuse/math": "^9.5.0",
"ali-oss": "^6.17.1",
"axios": "^1.1.3",
"blueimp-md5": "^2.19.0",
......@@ -28,7 +28,7 @@
"element-plus": "^2.2.21",
"file-saver": "^2.0.5",
"lodash-es": "^4.17.21",
"pinia": "^2.0.23",
"pinia": "^2.0.24",
"qs": "^6.11.0",
"ua-parser-js": "^1.0.32",
"video.js": "^7.20.3",
......@@ -50,12 +50,12 @@
"@vue/eslint-config-typescript": "^11.0.2",
"@vue/tsconfig": "^0.1.3",
"chalk": "^5.1.2",
"eslint": "^8.26.0",
"eslint": "^8.27.0",
"eslint-plugin-vue": "^9.7.0",
"sass": "^1.56.1",
"typescript": "~4.8.4",
"typescript": "~4.9.3",
"unplugin-auto-import": "^0.11.4",
"vite": "^3.2.3",
"vite": "^3.2.4",
"vite-plugin-checker": "^0.5.1",
"vue-tsc": "^1.0.9"
}
......
import type { Router, RouteRecordRaw } from 'vue-router'
export default function ({ router }: { router: Router }) {
const modules: Array<{ routes: Array<RouteRecordRaw> }> = Object.values(import.meta.globEager('./**/index.ts'))
const modules: Array<{ routes: Array<RouteRecordRaw> }> = Object.values(
import.meta.glob('./**/index.ts', { eager: true })
)
modules.forEach(({ routes = [] }) => {
// 注册路由
routes.forEach(route => {
......
......@@ -79,3 +79,34 @@ export function getExperimentResult(params: { experiment_id: string }) {
export function updateExperimentResult(data: { experiment_id: string; detail: string }) {
return httpRequest.post('/api/lab/v1/student/experiment/result-save', data)
}
// 获取实验报告模板
export function getExperimentReportTemplate(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/student/experiment/report-template', { params })
}
// 更新实验在线报告
export function updateExperimentReport(data: {
experiment_id: string
experiment_address: string
experiment_date: string
detail: string
}) {
return httpRequest.post('/api/lab/v1/student/experiment/upload-online-report', data)
}
// 缓存实验在线报告
export function cacheExperimentReport(data: {
experiment_id: string
experiment_address: string
experiment_date: string
detail: string
}) {
return httpRequest.post('/api/lab/v1/student/experiment/cache-online-report', data)
}
// 获取实验在线报告缓存
export function getExperimentReportCache(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/student/experiment/online-report-cached', { params })
}
// 获取实验在线报告详情
export function getExperimentReport(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/student/experiment/online-report-detail', { params })
}
<script setup>
import { getExperimentRecord } from '../api'
import { useNow, useDateFormat } from '@vueuse/core'
const props = defineProps({ experiment: Object, data: Object })
const emit = defineEmits(['update', 'update:modelValue'])
let pictures = $ref([])
const currentPictures = $computed(() => {
return pictures.map(item => {
const is_bind = !!props.data.files.find(file => file.url === item.url)
return { ...item, is_bind }
})
})
function fetchRecord() {
getExperimentRecord({ experiment_id: props.experiment.id }).then(res => {
pictures = res.data.data.pictures || []
})
}
onMounted(() => {
fetchRecord()
})
function handleBind(row) {
const files = props.data.files
const formatted = useDateFormat(useNow(), 'YYYY-MM-DD HH:mm:ss')
emit('update', [
...files,
{
id: Date.now().toString(),
url: row.url,
name: row.name,
type: '2',
file_type: 'png',
updated_time: formatted.value,
upload_time: row.upload_time
}
])
}
function handleRemove(row) {
const files = props.data.files
emit(
'update',
files.filter(file => file.url !== row.url)
)
}
</script>
<template>
<el-dialog append-to-body title="关联截图" width="800px" @update:modelValue="$emit('update:modelValue')">
<el-table :data="currentPictures" stripe>
<el-table-column prop="name"></el-table-column>
<el-table-column prop="upload_time"></el-table-column>
<el-table-column width="100">
<template #default="{ row }">
<el-button text type="danger" @click="handleRemove(row)" v-if="row.is_bind">取消关联</el-button>
<el-button text type="primary" @click="handleBind(row)" v-else>关联截图</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-row justify="center">
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
</template>
</el-dialog>
</template>
<script setup>
import { getExperiment } from '../api'
const props = defineProps({ experiment_id: String })
let detail = $ref()
const props = defineProps({ data: Object })
const teacherText = $computed(() => {
if (!detail) return ''
return detail.teachers.map(item => item.name).join('、')
})
function fetchInfo() {
if (!props.experiment_id) return
getExperiment({ experiment_id: props.experiment_id }).then(res => {
detail = res.data.detail
})
}
watchEffect(() => {
fetchInfo()
if (!props.data) return ''
return props.data.teachers.map(item => item.name).join('、')
})
</script>
<template>
<template v-if="detail && experiment_id">
<template v-if="data">
<el-form label-suffix=":" label-width="120px" label-position="right">
<el-form-item label="实验名称">{{ detail.name }}</el-form-item>
<el-form-item label="实验课程名称">{{ detail.course.name }}</el-form-item>
<el-form-item label="所属机构/学校">{{ detail.organization.name }}</el-form-item>
<el-form-item label="实验类型">{{ detail.type_level }}</el-form-item>
<el-form-item label="实验名称">{{ data.name }}</el-form-item>
<el-form-item label="实验课程名称">{{ data.course.name }}</el-form-item>
<el-form-item label="所属机构/学校">{{ data.organization.name }}</el-form-item>
<el-form-item label="实验类型">{{ data.type_level }}</el-form-item>
<el-form-item label="指导教师">{{ teacherText }}</el-form-item>
<el-form-item label="实验学时">{{ detail.length }}学时</el-form-item>
<el-form-item label="实验总成绩">{{ detail.score }}</el-form-item>
<el-form-item label="实验学时">{{ data.length }}学时</el-form-item>
<el-form-item label="实验总成绩">{{ data.score }}</el-form-item>
</el-form>
<el-divider />
<el-form label-suffix=":" label-position="top">
<el-form-item label="实验目的">{{ detail.purpose }}</el-form-item>
<el-form-item label="实验要求">{{ detail.requirements }}</el-form-item>
<el-form-item label="实验内容及原理">{{ detail.content }}</el-form-item>
<el-form-item label="实验步骤及过程">{{ detail.procedure }}</el-form-item>
<el-form-item label="实验目的">{{ data.purpose }}</el-form-item>
<el-form-item label="实验要求">{{ data.requirements }}</el-form-item>
<el-form-item label="实验内容及原理">{{ data.content }}</el-form-item>
<el-form-item label="实验步骤及过程">{{ data.procedure }}</el-form-item>
</el-form>
</template>
<el-empty description="暂无数据" v-else />
......
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import type { ExperimentInfo } from '../types'
import { ElMessage } from 'element-plus'
import AppEditor from '@/components/base/AppEditor.vue'
import { getExperimentPrepare, updateExperimentPrepare } from '../api'
interface Props {
experiment_id: string
data: ExperimentInfo
}
const props = defineProps<Props>()
......@@ -17,7 +18,7 @@ const emit = defineEmits<{
const formRef = $ref<FormInstance>()
const form = reactive({
experiment_id: props.experiment_id,
experiment_id: props.data.id,
detail: ''
})
const rules = ref<FormRules>({
......@@ -25,7 +26,7 @@ const rules = ref<FormRules>({
})
function fetchInfo() {
getExperimentPrepare({ experiment_id: props.experiment_id }).then(res => {
getExperimentPrepare({ experiment_id: props.data.id }).then(res => {
Object.assign(form, res.data.detail)
})
}
......@@ -53,7 +54,8 @@ const update = () => {
:close-on-click-modal="false"
width="800px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":">
<el-form-item label="实验名称">{{ data.name }} </el-form-item>
<el-form-item prop="detail">
<AppEditor v-model="form.detail"></AppEditor>
</el-form-item>
......
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import type { ExperimentRecord } from '../types'
import type { ExperimentInfo, ExperimentRecord } from '../types'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'
import { uploadExperimentReport } from '../api'
interface Props {
experiment_id: string
data: ExperimentInfo
}
const props = defineProps<Props>()
......@@ -34,7 +34,7 @@ function handleSubmit() {
const update = () => {
const [file] = form.files
uploadExperimentReport({
experiment_id: props.experiment_id,
experiment_id: props.data.id,
file: JSON.stringify({ name: file.name, url: file.url, upload_time: dayjs().format('YYYY-MM-DD HH:mm:ss') })
}).then(() => {
ElMessage({ message: '上传成功', type: 'success' })
......
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import type { ExperimentInfo } from '../types'
import { ElMessage } from 'element-plus'
import AppEditor from '@/components/base/AppEditor.vue'
import { getExperimentResult, updateExperimentResult } from '../api'
interface Props {
experiment_id: string
data: ExperimentInfo
}
const props = defineProps<Props>()
......@@ -17,7 +18,7 @@ const emit = defineEmits<{
const formRef = $ref<FormInstance>()
const form = reactive({
experiment_id: props.experiment_id,
experiment_id: props.data.id,
detail: ''
})
const rules = ref<FormRules>({
......@@ -25,7 +26,7 @@ const rules = ref<FormRules>({
})
function fetchInfo() {
getExperimentResult({ experiment_id: props.experiment_id }).then(res => {
getExperimentResult({ experiment_id: props.data.id }).then(res => {
Object.assign(form, res.data.detail)
})
}
......@@ -53,7 +54,8 @@ const update = () => {
:close-on-click-modal="false"
width="800px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":">
<el-form-item label="实验名称">{{ data.name }} </el-form-item>
<el-form-item prop="detail">
<AppEditor v-model="form.detail"></AppEditor>
</el-form-item>
......
......@@ -6,6 +6,9 @@ export const routes: Array<RouteRecordRaw> = [
path: '/student/lab',
component: AppLayout,
props: { sidebar: false },
children: [{ path: '', component: () => import('./views/Index.vue') }]
children: [
{ path: '', component: () => import('./views/Index.vue') },
{ path: 'report/:id', component: () => import('./views/Report.vue'), props: true }
]
}
]
......@@ -103,7 +103,7 @@ export interface ExperimentRecord {
length: number
}
course: CourseType
student: ExperimentRecordStudent
student: ExperimentStudent
}
export interface ExperimentRecordFile {
url: string
......@@ -111,18 +111,37 @@ export interface ExperimentRecordFile {
upload_time: string
}
export interface ExperimentRecordStudent {
export interface ExperimentStudent {
id: string
name: string
sno_number: string
specialty: {
id: string
name: string
}
classes: [
{
id: string
name: string
}
]
specialty: IdName
classes: IdName[]
}
// 实验信息
export interface ExperimentInfo {
id: string
name: string
length: number
type: number
type_level: string
score: number
status: number
purpose: string
requirements: string
content: string
procedure: string
report_upload_way: 1 | 2
teachers: IdName[]
course: IdName
organization: IdName
student: ExperimentStudent
is_commit_report: boolean
is_commit: boolean
}
interface IdName {
id: string
name: string
}
<script setup lang="ts">
import type { CourseType, ExperimentRecord } from '../types'
import type { CourseType, ExperimentInfo, ExperimentRecord } from '../types'
import { HomeFilled } from '@element-plus/icons-vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import DragPanel from '@/components/DragPanel.vue'
import { useGetCourseList } from '../composables/useGetCourseList'
import { upload } from '@/utils/upload'
import { getExperimentRecord, uploadExperimentPicture, submitExperimentRecord } from '../api'
import { getExperiment, getExperimentRecord, uploadExperimentPicture, submitExperimentRecord } from '../api'
import dayjs from 'dayjs'
const Info = defineAsyncComponent(() => import('../components/Info.vue'))
......@@ -59,14 +59,25 @@ function handleExperimentChange(value: string) {
let detail = $ref<ExperimentRecord>()
provide('detail', $$(detail))
let experimentInfo = $ref<ExperimentInfo>()
provide('info', $$(experimentInfo))
function fetchInfo() {
if (!form.experiment_id) return
getExperiment({ experiment_id: form.experiment_id }).then(res => {
experimentInfo = res.data.detail
})
}
function fetchExperimentRecord() {
if (!form.experiment_id) return
getExperimentRecord({ experiment_id: form.experiment_id }).then(res => {
detail = Array.isArray(res.data.data) ? undefined : res.data.data
})
}
watchEffect(() => {
fetchInfo()
fetchExperimentRecord()
})
// 右侧
......@@ -129,7 +140,7 @@ function uploadPicture(url: string) {
const pictures = detail?.pictures || []
pictures.unshift({ url, name: Date.now() + '.png', upload_time: dayjs().format('YYYY-MM-DD HH:mm:ss') })
uploadExperimentPicture({ experiment_id: form.experiment_id, pictures: JSON.stringify(pictures) }).then(() => {
fetchInfo()
fetchExperimentRecord()
screenshotLoading = false
})
}
......@@ -139,7 +150,7 @@ function handleSubmit() {
() => {
submitExperimentRecord({ experiment_id: form.experiment_id }).then(() => {
ElMessage({ message: '提交成功', type: 'success' })
fetchInfo()
fetchExperimentRecord()
})
}
)
......@@ -173,7 +184,7 @@ const resultDialogVisible = $ref(false)
</el-form>
<el-tabs type="border-card">
<el-tab-pane label="实验信息" lazy>
<Info :experiment_id="form.experiment_id"></Info>
<Info :data="experimentInfo"></Info>
</el-tab-pane>
<el-tab-pane label="实训指导" lazy>
<Book :course_id="form.course_id" :experiment_id="form.experiment_id" :key="resizeKey"></Book>
......@@ -185,7 +196,7 @@ const resultDialogVisible = $ref(false)
<Discuss :experiment_id="form.experiment_id"></Discuss>
</el-tab-pane>
<el-tab-pane label="过程与结果" lazy>
<Result :experiment_id="form.experiment_id" @update="fetchInfo"></Result>
<Result :experiment_id="form.experiment_id" @update="fetchExperimentRecord"></Result>
</el-tab-pane>
</el-tabs>
</div>
......@@ -205,10 +216,30 @@ const resultDialogVisible = $ref(false)
>
<el-button type="primary" :disabled="disabled" @click="prepareDialogVisible = true">实验准备</el-button>
<el-button type="primary" :disabled="disabled" @click="resultDialogVisible = true">实验结果</el-button>
<el-button type="primary" :disabled="disabled" @click="reportDialogVisible = true">在线实验报告</el-button>
<el-button type="primary" :disabled="disabled" @click="reportDialogVisible = true">上传实验报告</el-button>
<el-button type="primary" :disabled="disabled" @click="reportDialogVisible = true">查看实验报告</el-button>
<el-button type="primary" :disabled="disabled" @click="reportDialogVisible = true">导出实验报告</el-button>
<el-button type="primary" :disabled="disabled" v-if="experimentInfo?.report_upload_way === 2">
<router-link :to="`/student/lab/report/${form.experiment_id}`" target="_blank">在线实验报告</router-link>
</el-button>
<el-button
type="primary"
:disabled="disabled"
@click="reportDialogVisible = true"
v-if="experimentInfo?.report_upload_way === 1"
>上传实验报告</el-button
>
<el-button
type="primary"
:disabled="disabled"
@click="reportDialogVisible = true"
v-if="experimentInfo?.is_commit_report"
>查看实验报告</el-button
>
<el-button
type="primary"
:disabled="disabled"
@click="reportDialogVisible = true"
v-if="experimentInfo?.is_commit"
>导出实验报告</el-button
>
</div>
</el-row>
</AppCard>
......@@ -222,21 +253,21 @@ const resultDialogVisible = $ref(false)
<!-- 上传报告 -->
<ReportDialog
v-model="reportDialogVisible"
:experiment_id="form.experiment_id"
@update="fetchInfo"
v-if="reportDialogVisible && form.experiment_id"></ReportDialog>
:data="experimentInfo"
@update="fetchExperimentRecord"
v-if="reportDialogVisible && experimentInfo"></ReportDialog>
<!-- 实验准备 -->
<PrepareDialog
v-model="prepareDialogVisible"
:experiment_id="form.experiment_id"
v-if="prepareDialogVisible && form.experiment_id"></PrepareDialog>
:data="experimentInfo"
v-if="prepareDialogVisible && experimentInfo"></PrepareDialog>
<!-- 实验结果 -->
<ResultDialog
v-model="resultDialogVisible"
:experiment_id="form.experiment_id"
v-if="resultDialogVisible && form.experiment_id"></ResultDialog>
:data="experimentInfo"
v-if="resultDialogVisible && experimentInfo"></ResultDialog>
</template>
<style lang="scss" scoped>
......
<script setup>
import AppEditor from '@/components/base/AppEditor.vue'
import { useMapStore } from '@/stores/map'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useFileDialog } from '@vueuse/core'
import { upload } from '@/utils/upload'
import { useNow, useDateFormat } from '@vueuse/core'
import {
getExperimentReport,
getExperimentReportTemplate,
getExperimentReportCache,
cacheExperimentReport,
updateExperimentReport
} from '../api'
import BindCaptureDialog from '../components/BindCaptureDialog.vue'
const props = defineProps({ id: String })
const experimentAddress = useMapStore().getMapValuesByKey('experiment_address')
let experiment = $ref()
let report = $ref()
const disabled = $computed(() => {
return experiment?.is_commit_report
})
const teacherText = $computed(() => {
if (!experiment) return ''
return experiment.teachers.map(item => item.name).join('、')
})
const classText = $computed(() => {
if (!experiment) return ''
return experiment.student.classes.map(item => item.name).join('、')
})
const formRef = $ref()
const form = reactive({
experiment_id: props.id,
experiment_address: '',
experiment_date: useDateFormat(useNow(), 'YYYY-MM-DD').value,
detail: []
})
function fetchInfo() {
getExperimentReport({ experiment_id: props.id }).then(res => {
experiment = res.data.experiment
report = res.data.report
if (experiment.is_commit_report) {
let detail = []
try {
detail = JSON.parse(report.detail)
} catch (error) {
console.log(error)
}
Object.assign(form, report, { detail })
} else {
fetchCached()
}
})
}
function fetchTemplate() {
getExperimentReportTemplate({ experiment_id: props.id }).then(res => {
form.detail = res.data.template
})
}
function fetchCached() {
getExperimentReportCache({ experiment_id: props.id }).then(res => {
const report = res.data.report
if (!report.experiment_id) {
fetchTemplate()
return
}
let detail = []
try {
detail = JSON.parse(report.detail)
} catch (error) {
console.log(error)
}
Object.assign(form, report, { detail })
})
}
onMounted(() => {
fetchInfo()
})
// 关闭
function handleClose() {
ElMessageBox.confirm('此操作将不做任何修改,确认关闭该页面?', '提示').then(() => {
window.close()
})
}
// 暂存
function handleCache() {
const params = { ...form, detail: JSON.stringify(form.detail) }
cacheExperimentReport(params).then(() => {
ElMessage.success('暂存成功')
})
}
// 提交
function handleSubmit() {
formRef.validate().then(() => {
const params = { ...form, detail: JSON.stringify(form.detail) }
ElMessageBox.confirm('提交报告之后,您将不能进行修改报告,确认提交实验报告?', '提示').then(() => {
updateExperimentReport(params).then(() => {
ElMessage.success('提交成功')
fetchInfo()
})
})
})
}
// 附件
let currentRaw = $ref(null)
// 本地上传
const { files, open, reset } = useFileDialog()
function handleUpload(data) {
currentRaw = data
open({ multiple: false })
}
watchEffect(() => {
if (!files.value?.length) return
const [file] = files.value
upload(file).then(url => {
const formatted = useDateFormat(useNow(), 'YYYY-MM-DD HH:mm:ss')
currentRaw.files.push({
id: Date.now().toString(),
url,
name: file.name,
type: '1',
file_type: file.type,
updated_time: formatted.value,
upload_time: formatted.value
})
reset()
})
})
// 关联截图
let bindCaptureVisible = $ref(false)
function handleBindCapture(data) {
currentRaw = data
bindCaptureVisible = true
}
// 删除文件
function handleRemoveFile(data, index) {
data.files.splice(index, 1)
}
function handleUpdateBindCapture(files) {
currentRaw.files = files
}
</script>
<template>
<AppCard title="在线填写实验报告">
<h1 class="report-title">在线填写实验报告</h1>
<el-form
ref="formRef"
:model="form"
:disabled="disabled"
label-suffix=":"
hide-required-asterisk
v-if="experiment">
<el-row>
<el-col :span="12">
<el-form-item label="课程名称">{{ experiment.course.name }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实验名称">{{ experiment.name }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="实验地点">
<el-select v-model="form.experiment_address">
<el-option v-for="item in experimentAddress" v-bind="item" :key="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实验日期">
<el-date-picker v-model="form.experiment_date" value-format="YYYY-MM-DD"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="实验类型">{{ experiment.type_level }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="指导教师">{{ teacherText }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="专业">{{ experiment.student.specialty.name }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班级">{{ classText }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="姓名">{{ experiment.student.name }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学号">{{ experiment.student.sno_number }}</el-form-item>
</el-col>
</el-row>
<el-divider />
<el-form-item :label="item.name" v-for="item in form.detail" :key="item.id" class="report-form-item">
<!-- 内容 -->
<template v-if="item.type === 1">
{{ item.notice_message }}
<AppEditor v-model="item.content" :disabled="disabled" :height="200"></AppEditor>
</template>
<!-- 附件 -->
<template v-if="item.type === 2">
<el-button type="primary" @click="handleUpload(item)" v-if="item.categories.includes(2)">上传附件</el-button>
<el-button type="primary" @click="handleBindCapture(item)" v-if="item.categories.includes(1)"
>关联截图</el-button
>
<el-table :data="item.files" stripe :header-cell-style="{ background: '#ededed' }" style="margin-top: 20px">
<el-table-column label="序号" type="index" width="80" align="center"></el-table-column>
<el-table-column label="附件类型" prop="type" align="center">
<template #default="{ row }">
{{ row.type === '1' ? '本地上传' : '实验截图' }}
</template>
</el-table-column>
<el-table-column label="附件名称" prop="name" align="center"></el-table-column>
<el-table-column label="更新时间" prop="updated_time" align="center"></el-table-column>
<el-table-column label="操作" width="200" align="center">
<template #default="{ row, $index }">
<el-button text type="primary"><a :href="row.url" target="_blank">查看</a></el-button>
<el-button text type="danger" @click="handleRemoveFile(item, $index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<!-- 思考题 -->
<template v-if="item.type === 3">
{{ item.question_stem }}
<AppEditor v-model="item.content" :disabled="disabled" :height="200"></AppEditor>
</template>
</el-form-item>
<el-row justify="center">
<el-button type="primary" auto-insert-space @click="handleClose">关闭</el-button>
<el-button type="primary" auto-insert-space @click="handleCache">暂存</el-button>
<el-button type="primary" auto-insert-space @click="handleSubmit">提交报告</el-button>
</el-row>
</el-form>
<!-- 关联截图 -->
<BindCaptureDialog
v-model="bindCaptureVisible"
:experiment="detail"
:data="currentRaw"
@update="handleUpdateBindCapture"
v-if="bindCaptureVisible && currentRaw"></BindCaptureDialog>
</AppCard>
</template>
<style lang="scss">
.report-title {
padding: 20px;
font-size: 20px;
text-align: center;
}
.report-form-item {
flex-direction: column;
.el-form-item__label {
font-weight: bold;
text-align: left;
justify-content: flex-start;
}
}
</style>
<script setup>
import AppEditor from '@/components/base/AppEditor.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useFileDialog } from '@vueuse/core'
import { upload } from '@/utils/upload'
import { useNow, useDateFormat } from '@vueuse/core'
import {
getExperimentReport,
getExperimentReportTemplate,
getExperimentReportCache,
cacheExperimentReport,
updateExperimentReport
} from '../api'
import BindCaptureDialog from '../components/BindCaptureDialog.vue'
const props = defineProps({ id: String })
let experiment = $ref()
let report = $ref()
const disabled = $computed(() => {
return experiment?.is_commit_report
})
const teacherText = $computed(() => {
if (!experiment) return ''
return experiment.teachers.map(item => item.name).join('、')
})
const classText = $computed(() => {
if (!experiment) return ''
return experiment.student.classes.map(item => item.name).join('、')
})
const formRef = $ref()
const form = reactive({
experiment_id: props.id,
experiment_address: '',
experiment_date: useDateFormat(useNow(), 'YYYY-MM-DD').value,
detail: []
})
function fetchInfo() {
getExperimentReport({ experiment_id: props.id }).then(res => {
experiment = res.data.experiment
report = res.data.report
if (experiment.is_commit_report) {
let detail = []
try {
detail = JSON.parse(report.detail)
} catch (error) {
console.log(error)
}
Object.assign(form, report, { detail })
} else {
fetchCached()
}
})
}
function fetchTemplate() {
getExperimentReportTemplate({ experiment_id: props.id }).then(res => {
form.detail = res.data.template
})
}
function fetchCached() {
getExperimentReportCache({ experiment_id: props.id }).then(res => {
const report = res.data.report
if (!report.experiment_id) {
fetchTemplate()
return
}
let detail = []
try {
detail = JSON.parse(report.detail)
} catch (error) {
console.log(error)
}
Object.assign(form, report, { detail })
})
}
onMounted(() => {
fetchInfo()
})
// 关闭
function handleClose() {
ElMessageBox.confirm('此操作将不做任何修改,确认关闭该页面?', '提示').then(() => {
window.close()
})
}
// 暂存
function handleCache() {
const params = { ...form, detail: JSON.stringify(form.detail) }
cacheExperimentReport(params).then(() => {
ElMessage.success('暂存成功')
})
}
// 提交
function handleSubmit() {
formRef.validate().then(() => {
const params = { ...form, detail: JSON.stringify(form.detail) }
ElMessageBox.confirm('提交报告之后,您将不能进行修改报告,确认提交实验报告?', '提示').then(() => {
updateExperimentReport(params).then(() => {
ElMessage.success('提交成功')
fetchInfo()
})
})
})
}
// 附件
let currentRaw = $ref(null)
// 本地上传
const { files, open, reset } = useFileDialog()
function handleUpload(data) {
currentRaw = data
open({ multiple: false })
}
watchEffect(() => {
if (!files.value?.length) return
const [file] = files.value
upload(file).then(url => {
const formatted = useDateFormat(useNow(), 'YYYY-MM-DD HH:mm:ss')
currentRaw.files.push({
id: Date.now().toString(),
url,
name: file.name,
type: '1',
file_type: file.type,
updated_time: formatted.value,
upload_time: formatted.value
})
reset()
})
})
// 关联截图
let bindCaptureVisible = $ref(false)
function handleBindCapture(data) {
currentRaw = data
bindCaptureVisible = true
}
// 删除文件
function handleRemoveFile(data, index) {
data.files.splice(index, 1)
}
function handleUpdateBindCapture(files) {
currentRaw.files = files
}
</script>
<template>
<AppCard title="查看实验报告">
<h1 class="report-title">查看实验报告</h1>
<el-form ref="formRef" :model="form" label-suffix=":" hide-required-asterisk v-if="experiment">
<el-row>
<el-col :span="12">
<el-form-item label="课程名称">{{ experiment.course.name }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实验名称">{{ experiment.name }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="实验地点">{{ report.experiment_address }} </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实验日期"> {{ report.experiment_date }} </el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="实验类型">{{ experiment.type_level }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="指导教师">{{ teacherText }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="专业">{{ experiment.student.specialty.name }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班级">{{ classText }}</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="姓名">{{ experiment.student.name }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学号">{{ experiment.student.sno_number }}</el-form-item>
</el-col>
</el-row>
<el-divider />
<el-form-item :label="item.name" v-for="item in form.detail" :key="item.id" class="report-form-item">
<!-- 内容 -->
<template v-if="item.type === 1">
{{ item.notice_message }}
<AppEditor v-model="item.content" :disabled="disabled" :height="200"></AppEditor>
</template>
<!-- 附件 -->
<template v-if="item.type === 2">
<el-button type="primary" @click="handleUpload(item)" v-if="item.categories.includes(2)">上传附件</el-button>
<el-button type="primary" @click="handleBindCapture(item)" v-if="item.categories.includes(1)"
>关联截图</el-button
>
<el-table :data="item.files" stripe :header-cell-style="{ background: '#ededed' }" style="margin-top: 20px">
<el-table-column label="序号" type="index" width="80" align="center"></el-table-column>
<el-table-column label="附件类型" prop="type" align="center">
<template #default="{ row }">
{{ row.type === '1' ? '本地上传' : '实验截图' }}
</template>
</el-table-column>
<el-table-column label="附件名称" prop="name" align="center"></el-table-column>
<el-table-column label="更新时间" prop="updated_time" align="center"></el-table-column>
<el-table-column label="操作" width="200" align="center">
<template #default="{ row, $index }">
<el-button text type="primary"><a :href="row.url" target="_blank">查看</a></el-button>
<el-button text type="danger" @click="handleRemoveFile(item, $index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<!-- 思考题 -->
<template v-if="item.type === 3">
{{ item.question_stem }}
<AppEditor v-model="item.content" :disabled="disabled" :height="200"></AppEditor>
</template>
</el-form-item>
<el-row justify="center">
<el-button type="primary" auto-insert-space @click="handleClose">关闭</el-button>
<el-button type="primary" auto-insert-space @click="handleCache">暂存</el-button>
<el-button type="primary" auto-insert-space @click="handleSubmit">提交报告</el-button>
</el-row>
</el-form>
<!-- 关联截图 -->
<BindCaptureDialog
v-model="bindCaptureVisible"
:experiment="detail"
:data="currentRaw"
@update="handleUpdateBindCapture"
v-if="bindCaptureVisible && currentRaw"></BindCaptureDialog>
</AppCard>
</template>
<style lang="scss">
.report-title {
padding: 20px;
font-size: 20px;
text-align: center;
}
.report-form-item {
flex-direction: column;
.el-form-item__label {
font-weight: bold;
text-align: left;
justify-content: flex-start;
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论