Kaynağa Gözat

Merge branch 'startLogin'

mo 1 yıl önce
ebeveyn
işleme
534902940c

+ 306 - 20
package-lock.json

@@ -17,6 +17,7 @@
         "cropperjs": "^1.5.13",
         "dayjs": "^1.11.7",
         "echarts": "^5.4.2",
+        "html2canvas": "^1.4.1",
         "lodash": "^4.17.21",
         "lodash-es": "^4.17.21",
         "moveable": "^0.49.0",
@@ -28,8 +29,10 @@
         "umi-request": "^1.4.0",
         "vudio.js": "^1.0.3",
         "vue": "^3.3.4",
+        "vue-qr": "^4.0.9",
         "vue-router": "^4.1.6",
         "vue3-lottie": "^2.7.0",
+        "vuedraggable": "^4.1.0",
         "wavesurfer.js": "^7.0.0-beta.11"
       },
       "devDependencies": {
@@ -3609,8 +3612,15 @@
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
     },
     "node_modules/base64-js": {
       "version": "1.5.1",
@@ -4069,6 +4079,14 @@
       "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
       "dev": true
     },
+    "node_modules/css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/css-render": {
       "version": "0.15.12",
       "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz",
@@ -4176,6 +4194,17 @@
         "node": ">=14.16"
       }
     },
+    "node_modules/decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "dependencies": {
+        "mimic-response": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/deep-is": {
       "version": "0.1.4",
       "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
@@ -5054,8 +5083,7 @@
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
-      "dev": true
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
     },
     "node_modules/fsevents": {
       "version": "2.3.2",
@@ -5344,6 +5372,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/human-signals": {
       "version": "4.3.1",
       "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-4.3.1.tgz",
@@ -5439,7 +5479,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "dev": true,
       "dependencies": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -5448,8 +5487,7 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "node_modules/ini": {
       "version": "1.3.8",
@@ -5868,6 +5906,11 @@
         "whatwg-fetch": ">=0.10.0"
       }
     },
+    "node_modules/js-binary-schema-parser": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz",
+      "integrity": "sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg=="
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -6506,6 +6549,14 @@
         "node": ">=12"
       }
     },
+    "node_modules/mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
@@ -6872,7 +6923,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
       "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "dev": true,
       "dependencies": {
         "wrappy": "1"
       }
@@ -7086,6 +7136,11 @@
         "node": ">=6"
       }
     },
+    "node_modules/parenthesis": {
+      "version": "3.1.8",
+      "resolved": "https://registry.npmmirror.com/parenthesis/-/parenthesis-3.1.8.tgz",
+      "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw=="
+    },
     "node_modules/parse-filepath": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/parse-filepath/-/parse-filepath-1.0.2.tgz",
@@ -7825,6 +7880,21 @@
       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "dev": true
     },
+    "node_modules/simple-concat": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz",
+      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
+    },
+    "node_modules/simple-get": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz",
+      "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+      "dependencies": {
+        "decompress-response": "^6.0.0",
+        "once": "^1.3.1",
+        "simple-concat": "^1.0.0"
+      }
+    },
     "node_modules/slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz",
@@ -7866,6 +7936,11 @@
         "tslib": "^2.0.3"
       }
     },
+    "node_modules/sortablejs": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
+      "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+    },
     "node_modules/source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
@@ -7929,6 +8004,14 @@
         "node": ">=0.6.19"
       }
     },
+    "node_modules/string-split-by": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/string-split-by/-/string-split-by-1.0.0.tgz",
+      "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
+      "dependencies": {
+        "parenthesis": "^3.1.5"
+      }
+    },
     "node_modules/string-width": {
       "version": "5.1.2",
       "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
@@ -8067,6 +8150,14 @@
       "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
       "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
     },
+    "node_modules/text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
@@ -8391,6 +8482,14 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "dev": true
     },
+    "node_modules/utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "dependencies": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
     "node_modules/v8flags": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/v8flags/-/v8flags-4.0.0.tgz",
@@ -8648,6 +8747,51 @@
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
       "dev": true
     },
+    "node_modules/vue-qr": {
+      "version": "4.0.9",
+      "resolved": "https://registry.npmmirror.com/vue-qr/-/vue-qr-4.0.9.tgz",
+      "integrity": "sha512-pAISV94T0MNEYA3NGjykUpsXRE2QfaNxlu9ZhEL6CERgqNc21hJYuP3hRVzAWfBQlgO18DPmZTbrFerJC3+Ikw==",
+      "dependencies": {
+        "glob": "^8.0.1",
+        "js-binary-schema-parser": "^2.0.2",
+        "simple-get": "^4.0.1",
+        "string-split-by": "^1.0.0"
+      }
+    },
+    "node_modules/vue-qr/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/vue-qr/node_modules/glob": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vue-qr/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/vue-router": {
       "version": "4.2.2",
       "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.2.2.tgz",
@@ -8734,6 +8878,17 @@
         "vue": "^3.2"
       }
     },
+    "node_modules/vuedraggable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
+      "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+      "dependencies": {
+        "sortablejs": "1.14.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.1"
+      }
+    },
     "node_modules/vueuc": {
       "version": "0.4.51",
       "resolved": "https://registry.npmmirror.com/vueuc/-/vueuc-0.4.51.tgz",
@@ -8899,8 +9054,7 @@
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "dev": true
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
     },
     "node_modules/xml-name-validator": {
       "version": "4.0.0",
@@ -11601,8 +11755,12 @@
     "balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
     },
     "base64-js": {
       "version": "1.5.1",
@@ -12000,6 +12158,14 @@
       "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
       "dev": true
     },
+    "css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "requires": {
+        "utrie": "^1.0.2"
+      }
+    },
     "css-render": {
       "version": "0.15.12",
       "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz",
@@ -12084,6 +12250,14 @@
       "resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
       "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ=="
     },
+    "decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "requires": {
+        "mimic-response": "^3.1.0"
+      }
+    },
     "deep-is": {
       "version": "0.1.4",
       "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
@@ -12779,8 +12953,7 @@
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
-      "dev": true
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
     },
     "fsevents": {
       "version": "2.3.2",
@@ -13005,6 +13178,15 @@
       "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==",
       "dev": true
     },
+    "html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "requires": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      }
+    },
     "human-signals": {
       "version": "4.3.1",
       "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-4.3.1.tgz",
@@ -13070,7 +13252,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "dev": true,
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -13079,8 +13260,7 @@
     "inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "ini": {
       "version": "1.3.8",
@@ -13408,6 +13588,11 @@
         "whatwg-fetch": ">=0.10.0"
       }
     },
+    "js-binary-schema-parser": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz",
+      "integrity": "sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg=="
+    },
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -13914,6 +14099,11 @@
       "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
       "dev": true
     },
+    "mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+    },
     "minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
@@ -14219,7 +14409,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
       "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "dev": true,
       "requires": {
         "wrappy": "1"
       }
@@ -14387,6 +14576,11 @@
         "callsites": "^3.0.0"
       }
     },
+    "parenthesis": {
+      "version": "3.1.8",
+      "resolved": "https://registry.npmmirror.com/parenthesis/-/parenthesis-3.1.8.tgz",
+      "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw=="
+    },
     "parse-filepath": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/parse-filepath/-/parse-filepath-1.0.2.tgz",
@@ -14979,6 +15173,21 @@
       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "dev": true
     },
+    "simple-concat": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz",
+      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
+    },
+    "simple-get": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz",
+      "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+      "requires": {
+        "decompress-response": "^6.0.0",
+        "once": "^1.3.1",
+        "simple-concat": "^1.0.0"
+      }
+    },
     "slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz",
@@ -15013,6 +15222,11 @@
         "tslib": "^2.0.3"
       }
     },
+    "sortablejs": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
+      "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+    },
     "source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
@@ -15061,6 +15275,14 @@
       "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
       "dev": true
     },
+    "string-split-by": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/string-split-by/-/string-split-by-1.0.0.tgz",
+      "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
+      "requires": {
+        "parenthesis": "^3.1.5"
+      }
+    },
     "string-width": {
       "version": "5.1.2",
       "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
@@ -15167,6 +15389,14 @@
         }
       }
     },
+    "text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "requires": {
+        "utrie": "^1.0.2"
+      }
+    },
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
@@ -15421,6 +15651,14 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "dev": true
     },
+    "utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "requires": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
     "v8flags": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/v8flags/-/v8flags-4.0.0.tgz",
@@ -15580,6 +15818,47 @@
         }
       }
     },
+    "vue-qr": {
+      "version": "4.0.9",
+      "resolved": "https://registry.npmmirror.com/vue-qr/-/vue-qr-4.0.9.tgz",
+      "integrity": "sha512-pAISV94T0MNEYA3NGjykUpsXRE2QfaNxlu9ZhEL6CERgqNc21hJYuP3hRVzAWfBQlgO18DPmZTbrFerJC3+Ikw==",
+      "requires": {
+        "glob": "^8.0.1",
+        "js-binary-schema-parser": "^2.0.2",
+        "simple-get": "^4.0.1",
+        "string-split-by": "^1.0.0"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+          "requires": {
+            "balanced-match": "^1.0.0"
+          }
+        },
+        "glob": {
+          "version": "8.1.0",
+          "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz",
+          "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^5.0.1",
+            "once": "^1.3.0"
+          }
+        },
+        "minimatch": {
+          "version": "5.1.6",
+          "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz",
+          "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+          "requires": {
+            "brace-expansion": "^2.0.1"
+          }
+        }
+      }
+    },
     "vue-router": {
       "version": "4.2.2",
       "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.2.2.tgz",
@@ -15644,6 +15923,14 @@
         "lottie-web": "5.12.2"
       }
     },
+    "vuedraggable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
+      "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+      "requires": {
+        "sortablejs": "1.14.0"
+      }
+    },
     "vueuc": {
       "version": "0.4.51",
       "resolved": "https://registry.npmmirror.com/vueuc/-/vueuc-0.4.51.tgz",
@@ -15784,8 +16071,7 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "dev": true
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
     },
     "xml-name-validator": {
       "version": "4.0.0",

+ 1 - 0
package.json

@@ -30,6 +30,7 @@
     "cropperjs": "^1.5.13",
     "dayjs": "^1.11.7",
     "echarts": "^5.4.2",
+    "html2canvas": "^1.4.1",
     "lodash": "^4.17.21",
     "lodash-es": "^4.17.21",
     "moveable": "^0.49.0",

BIN
src/common/images/logo.png


+ 16 - 16
src/components/TheQrCode/index.tsx

@@ -178,22 +178,22 @@ export default defineComponent({
         gifBackground: gifBgSrc,
         text: this.text,
         size: this.size,
-        // margin: this.margin,
-        // colorDark: this.colorDark,
-        // colorLight: this.colorLight,
-        // backgroundColor: this.backgroundColor,
-        // backgroundImage: img,
-        // // backgroundDimming: this.backgroundDimming,
-        // // // logoImage: logoImg + '?' + new Date().getTime(),
-        // logoScale: this.logoScale,
-        // logoBackgroundColor: this.logoBackgroundColor,
-        // correctLevel: this.correctLevel,
-        // logoMargin: this.logoMargin,
-        // logoCornerRadius: this.logoCornerRadius,
-        // whiteMargin: toBoolean(this.whiteMargin),
-        // dotScale: this.dotScale,
-        // autoColor: toBoolean(this.autoColor),
-        // components: this.components
+        margin: this.margin,
+        colorDark: this.colorDark,
+        colorLight: this.colorLight,
+        backgroundColor: this.backgroundColor,
+        backgroundImage: img,
+        backgroundDimming: this.backgroundDimming,
+        logoImage: logoImg + '?' + new Date().getTime(),
+        logoScale: this.logoScale,
+        logoBackgroundColor: this.logoBackgroundColor,
+        correctLevel: this.correctLevel,
+        logoMargin: this.logoMargin,
+        logoCornerRadius: this.logoCornerRadius,
+        whiteMargin: toBoolean(this.whiteMargin),
+        dotScale: this.dotScale,
+        autoColor: toBoolean(this.autoColor),
+        components: this.components
       })
         .draw()
         .then((dataUri: any) => {

+ 7 - 2
src/views/attend-class/model/train-type/index.tsx

@@ -52,6 +52,10 @@ export default defineComponent({
     offShelf: {
       type: Boolean,
       default: false
+    },
+    isCLassWork: {
+      type: Boolean,
+      default: false
     }
   },
   emits: ['click', 'delete', 'edit', 'offShelf'],
@@ -132,7 +136,7 @@ export default defineComponent({
         </div>
         <div class={styles['train-content']}>
           <NImage src={props.item.coverImg} previewDisabled objectFit="cover" />
-          {props.isDisabled ? (
+          {props.isDisabled && !props.isCLassWork ? (
             <div class={styles.disPreview}>
               <NProgress
                 percentage={
@@ -158,8 +162,9 @@ export default defineComponent({
                     </h4>
                   ) : (
                     <h4>
+
                       {props.item.trainingTimes
-                        ? (props.item.trainingTimes / 60).toFixed(0)
+                        ? parseInt((props.item.trainingTimes / 60)+'')
                         : 0}
                       <span>分钟</span>
                     </h4>

+ 10 - 0
src/views/classList/api.ts

@@ -159,3 +159,13 @@ export const getTrainingClassDetail = (params: any) => {
     requestType: 'form'
   });
 };
+
+
+/**
+ * 学生练习记录
+ */
+export const getTrainingStatList = (params: any) => {
+  return request.post(`/edu-app/musicPracticeRecordStat/trainingList`, {
+    data: params
+  });
+};

+ 0 - 1
src/views/classList/components/classStudent.tsx

@@ -191,7 +191,6 @@ export default defineComponent({
             v-model:pageTotal={state.pagination.pageTotal}
             onList={getList}
             sync
-            saveKey="orchestraRegistration-key"
           />
         </div>
       </div>

+ 2 - 1
src/views/classList/components/testRecode.tsx

@@ -100,6 +100,7 @@ export default defineComponent({
         });
         state.testInfo.practiceDurationAvg = res.data.practiceDurationAvg;
         state.testInfo.practiceUserCount = res.data.practiceUserCount;
+        state.testInfo.vipUserCount = res.data.vipUserCount
       } catch (e) {
         console.log(e);
       }
@@ -147,7 +148,7 @@ export default defineComponent({
           title: '学生类型',
           key: 'studentType',
           render(row: any) {
-            return <>{row.studentType == 'member' ? '会员' : '普通'}</>;
+            return <>{row.vipFlag ? '会员' : '普通'}</>;
           }
         },
         {

+ 2 - 2
src/views/classList/index.tsx

@@ -40,7 +40,6 @@ export default defineComponent({
         currentClass: null,
         currentGradeNum: null
       },
-
       orchestraType: null,
       courseTypeCode: null,
       loading: false,
@@ -171,6 +170,7 @@ export default defineComponent({
                     学生调整
                   </NButton>
                   <NButton
+                  disabled={!(row.preStudentNum > 0)}
                     type="primary"
                     text
                     onClick={() => classesBegin(row)}>
@@ -262,7 +262,7 @@ export default defineComponent({
           v-slots={{
             icon: () => (
               <>
-                <NImage class={styles.addBtnIcon} src={add}></NImage>
+                <NImage class={styles.addBtnIcon} previewDisabled src={add}></NImage>
               </>
             )
           }}>

+ 1 - 1
src/views/classList/modals/Gotoclass.tsx

@@ -68,7 +68,7 @@ export default defineComponent({
           emit('close');
           window.open(href, +new Date() + '');
         } else {
-          message.error('当前章节没有课件,请重新学则');
+          message.error('当前章节暂无课件,请重新选择');
         }
       });
 

+ 1 - 0
src/views/classList/modals/classTrainingDetails.tsx

@@ -151,6 +151,7 @@ export default defineComponent({
                 style={{ marginBottom: '20px' }}
                 isDisabled={true}
                 isDelete={false}
+                isCLassWork={true}
                 item={item}></TrainType>
             ))}
           </div>

+ 32 - 0
src/views/studentList/api.ts

@@ -18,3 +18,35 @@ export const getPracticeRecordList = (params: any) => {
     // requestType: 'form'
   });
 };
+
+/**
+ * 获取学生列表
+ */
+export const getStudentList = (params: any) => {
+  return request.post('/edu-app/student/page', {
+    data: params
+  });
+};
+
+
+/**
+ * 修改学生信息
+ */
+export const resetStudentInfo = (params: any) => {
+  return request.post('/edu-app/student/update', {
+    data: params
+  });
+};
+
+
+/**
+ * 获取学生课后训练
+ */
+
+export const getStudentAfterWork = (params: any) => {
+  return request.post('/edu-app/lessonTraining/trainingTeacherList', {
+    data: params,
+    params
+  });
+};
+

+ 182 - 0
src/views/studentList/components/baseInfo.tsx

@@ -0,0 +1,182 @@
+import { defineComponent, onMounted, reactive, ref, watch } from 'vue';
+import styles from '../index.module.less';
+import {
+  NImage,
+  NForm,
+  NFormItem,
+  NInput,
+  NGrid,
+  NGi,
+  NButton,
+  NSelect,
+  NSpace,
+  SelectOption,
+  useMessage,
+  NModal,
+  NCalendar,
+  NCascader
+} from 'naive-ui';
+import headerD from '../images/headerD.png';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+import maleIcon from '../images/maleIcon.png';
+import femaleIcon from '../images/femaleIcon.png';
+import { useUserStore } from '/src/store/modules/users';
+import { api_teacherUpdate } from '/src/api/user';
+import { resetStudentInfo } from '../api'
+import UploadFile from '/src/components/upload-file';
+export default defineComponent({
+  name: 'setting-personInfo',
+  props:{
+    studentInfo:{
+      type:Object,
+      default: () => (
+        { nickname:'',
+      currentGradeNum:'',
+      gender:null,
+      phone:'',
+      id:''
+   })
+  }},
+  setup(props) {
+    const message = useMessage();
+    const userStore = useUserStore();
+    const formOptions = reactive({
+      sexs: [
+        { label: '男', value: 1, class: 'option' },
+        { label: '女', value: 0, class: 'option' }
+      ] as SelectOption[],
+      areaList: [] as any[]
+    });
+    const formRef = ref();
+    const studentForm = reactive({
+      nickname: props.studentInfo.nickname as any,
+      phone: props.studentInfo.phone,
+      gender: props.studentInfo.gender,
+      id: props.studentInfo.id,
+      classGroupName:props.studentInfo.classGroupName
+    });
+    const data = reactive({
+      disabled: true,
+      openChangePwd: false,
+      uploadShow: true
+    });
+    watch(()=>props.studentInfo,(val)=>{
+      studentForm.nickname= val.nickname as any
+      studentForm.phone=val.phone
+      studentForm.gender= val.gender
+      studentForm.id= val.id
+      studentForm.classGroupName=val.classGroupName
+    })
+    // onMounted(() => {});
+
+    const handleSave = () => {
+      formRef.value.validate(async (err: any) => {
+        if (err) {
+          return;
+        }
+        await resetStudentInfo({...props.studentInfo,...studentForm});
+        data.disabled = true;
+        message.success('修改成功');
+      });
+    };
+    const cancel = ()=>{
+      studentForm.nickname = props.studentInfo.nickname
+      studentForm.phone = props.studentInfo.phone
+      studentForm.gender = props.studentInfo.gender
+      studentForm.classGroupName = props.studentInfo.classGroupName
+
+      data.disabled = true
+    }
+    return () => (
+      <div class={styles.infoWrap}>
+        <div class={styles.setInfo}>
+          <NForm ref={formRef} model={studentForm} disabled={data.disabled}>
+            <NGrid cols={4} x-gap="100">
+              <NGi>
+                <NFormItem
+                  label="姓名"
+                  path="nickname"
+                  rule={{
+                    required: true,
+                    message: '请填写老师姓名',
+                    trigger: 'blur'
+                  }}>
+                  <NInput
+                    bordered={!data.disabled}
+                    placeholder="请填写老师姓名"
+                    v-model:value={studentForm.nickname}></NInput>
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem
+                  label="手机号"
+                  path="phone"
+                  rule={[
+                    {
+                      required: true,
+                      message: '请填写老师手机号',
+                      trigger: 'blur'
+                    },
+                    {
+                      pattern: /^1[3456789]\d{9}$/,
+                      message: '手机号格式不正确',
+                      trigger: 'blur'
+                    }
+                  ]}>
+                  <NInput
+                    bordered={!data.disabled}
+                    placeholder="请填写老师手机号"
+                    v-model:value={studentForm.phone}></NInput>
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem label="性别" path="sex">
+                  <NSelect
+                    bordered={!data.disabled}
+                    class={styles.select}
+                    showArrow={!data.disabled}
+                    placeholder="请选择性别"
+                    options={formOptions.sexs}
+                    v-model:value={studentForm.gender}
+                  />
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem label="年级班级" path="sex">
+                <NInput
+                    bordered={!data.disabled}
+                    disabled={true}
+                    placeholder="年级班级"
+                    v-model:value={studentForm.classGroupName}></NInput>
+
+                </NFormItem>
+              </NGi>
+            </NGrid>
+          </NForm>
+        </div>
+        {data.disabled ? (
+          <NSpace class={styles.btnList} align="center" justify="end">
+            <NButton
+              class={styles.btn}
+              color="#f24433"
+              onClick={() => (data.disabled = false)}>
+              修改信息
+            </NButton>
+          </NSpace>
+        ) : (
+          <NSpace class={styles.btnList} align="center" justify="end">
+            <NButton class={styles.btn} onClick={() => ( cancel())}>
+              取消
+            </NButton>
+            <NButton
+              class={styles.btn}
+              type="primary"
+              onClick={() => handleSave()}>
+              完成
+            </NButton>
+          </NSpace>
+        )}
+      </div>
+    );
+  }
+});

+ 1 - 0
src/views/studentList/components/evaluationRecords.tsx

@@ -144,6 +144,7 @@ export default defineComponent({
       const res = await getPracticeRecordList({
         userId: props.studentId,
         ...state.pagination,
+        feature: "EVALUATION",
         ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
       });
       state.tableList = res.data.rows;

+ 18 - 4
src/views/studentList/components/practiceData.tsx

@@ -12,6 +12,7 @@ import numeral from 'numeral';
 import { useECharts } from '@/hooks/web/useECharts';
 import Pagination from '/src/components/pagination';
 import { getTrainingStat } from '../api';
+import { getTrainingStatList } from '@/views/classList/api'
 import {
   getNowDateAndMonday,
   getNowDateAndSunday,
@@ -85,8 +86,18 @@ export default defineComponent({
         }
       ];
     };
-    const getList = () => {
-      console.log('1');
+    const getList =  async () => {
+        try{
+          const res = await getTrainingStatList({
+            page:1,
+            rows:999,
+            studentId: props.studentId,
+            ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
+          })
+          state.tableList = res.data.rows;
+        }catch(e){
+          console.log(e)
+        }
     };
     const setChart = () => {
       setOptions({
@@ -222,7 +233,7 @@ export default defineComponent({
         payForm.timeList = res.data.trainingStatDetailList.map((item: any) => {
           return item.practiceDuration;
         });
-        state.tableList = res.data.trainingStatDetailList;
+
         setChart();
         console.log(payForm);
       } catch (e) {
@@ -232,6 +243,7 @@ export default defineComponent({
     const search = () => {
       state.pagination.page = 1;
       getChartDetail();
+      getList()
       console.log('search');
     };
     const onReset = () => {
@@ -240,11 +252,13 @@ export default defineComponent({
         getNowDateAndSunday(new Date().getTime())
       ];
       search();
+      getList()
       console.log('onReset');
     };
     onMounted(() => {
       console.log(props.studentId);
       getChartDetail();
+      getList()
     });
     return () => (
       <>
@@ -317,7 +331,7 @@ export default defineComponent({
                       <NNumberAnimation
                         from={0}
                         to={getSecend(
-                          payForm.practiceDurationTotal
+                          payForm.practiceDurationAvg
                         )}></NNumberAnimation>
                     </span>{' '}

+ 284 - 0
src/views/studentList/components/studentAfterWork.tsx

@@ -0,0 +1,284 @@
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import {
+  NButton,
+  NDataTable,
+  NForm,
+  NFormItem,
+  NImage,
+  NModal,
+  NSelect,
+  NSpace
+} from 'naive-ui';
+import SearchInput from '@/components/searchInput';
+import CSelect from '@/components/CSelect';
+import Pagination from '@/components/pagination';
+import { getStudentAfterWork } from '../api';
+import add from './images/add.png';
+import { useRoute } from 'vue-router';
+import CBreadcrumb from '/src/components/CBreadcrumb';
+import CDatePicker from '/src/components/CDatePicker';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+import {
+  getNowDateAndMonday,
+  getNowDateAndSunday,
+  getTimes
+} from '/src/utils/dateFormat';
+import { trainingStatusArray } from '@/utils/searchArray';
+import StudentTraomomhDetails from '../modals/studentTraomomhDetails'
+import dayjs from 'dayjs';
+import { lookup } from 'dns';
+export default defineComponent({
+  name: 'student-studentList',
+  setup(props, { emit }) {
+    const state = reactive({
+      searchForm: { keyword: '', trainingStatus: null as any },
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 4
+      },
+      tableList: [] as any,
+      workInfo: {
+        createTime: '',
+        expireDate: '',
+        teacherAvatar: '',
+        teacherName: ''
+      },
+      detailVisiable: false,
+      activeRow: null as any,
+      index: 0
+    });
+    const timer = ref<[number, number]>([
+      getNowDateAndMonday(new Date().getTime()),
+      getNowDateAndSunday(new Date().getTime())
+    ]);
+    const TrainingDetailsRef = ref();
+    const route = useRoute();
+    const routerList = ref([
+      { name: '班级管理', path: '/classList' },
+      { name: route.query.name, path: '/classDetail' },
+      { name: route.query.teacherName, path: '/afterWorkDetail' }
+    ] as any);
+
+    const search = () => {
+      state.pagination.page = 1;
+      getList();
+      console.log('search', state);
+    };
+
+    const onReset = () => {
+      state.searchForm = { keyword: '', trainingStatus: null as any };
+      timer.value = [
+        getNowDateAndMonday(new Date().getTime()),
+        getNowDateAndSunday(new Date().getTime())
+      ];
+      search();
+    };
+    const getList = async () => {
+      state.loading = true;
+      try {
+        const res = await getStudentAfterWork({
+          studentId: route.query.studentId,
+          ...state.searchForm,
+          ...state.pagination,
+          ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
+        });
+
+        state.tableList = res.data.rows;
+
+        state.pagination.pageTotal = res.data.total;
+        state.loading = false;
+      } catch (e) {
+        state.loading = false;
+        console.log(e);
+      }
+    };
+    // const getWorkInfo = async () => {
+    //   console.log(route.query);
+    //   try {
+    //     const res = await getWorkDetail({ trainingId: route.query.trainingId });
+    //     state.workInfo = { ...res.data };
+    //   } catch (e) {
+    //     console.log(e);
+    //   }
+    // };
+
+    const lookDetail = (row: any, index: number) => {
+      console.log(index, 'index');
+      state.index = index + 1;
+      state.activeRow = row;
+      state.detailVisiable = true;
+    };
+    onMounted(() => {
+      // getWorkInfo();
+      getList();
+    });
+    const columns = () => {
+      return [
+        {
+          title: '布置老师',
+          key: 'teacherName'
+        },
+        {
+          title: '布置时间',
+          key: 'createTime',
+          render(row: any) {
+            return row.createTime
+              ? dayjs(row.createTime).format('YYYY-MM-DD')
+              : '--';
+          }
+        },
+        {
+          title: '截止时间',
+          key: 'expireDate',
+          render(row: any) {
+            return row.expireDate
+              ? dayjs(row.expireDate).format('YYYY-MM-DD')
+              : '--';
+          }
+        },
+        {
+          title: '最后提交时间',
+          key: 'submitTime',
+          render(row: any) {
+            return row.submitTime
+              ? dayjs(row.submitTime).format('YYYY-MM-DD')
+              : '--';
+          }
+        },
+        {
+          title: '提交状态',
+          key: 'sex',
+          render(row: any) {
+            return (
+              <div>
+                {row.trainingStatus == 'UNSUBMITTED' ? (
+                  <p class={styles.nosub}>未提交</p>
+                ) : null}
+                {row.trainingStatus == 'SUBMITTED' ? (
+                  <p class={styles.ison}>不合格</p>
+                ) : null}
+                {row.trainingStatus == 'TARGET' ? (
+                  <p class={styles.isok}>合格</p>
+                ) : null}
+              </div>
+            );
+          }
+        },
+        {
+          title: '操作',
+          key: 'id',
+          render(row: any, index: number) {
+            return (
+              <NButton
+                text
+                type="primary"
+                onClick={() => {
+                  lookDetail(row, index);
+                }}>
+                详情
+              </NButton>
+            );
+          }
+        }
+      ];
+    };
+
+    const goToNext = () => {
+      ++state.index;
+
+      state.activeRow = state.tableList[state.index - 1];
+
+      TrainingDetailsRef.value.getTrainingDetail(
+        state.activeRow.studentLessonTrainingId
+      );
+    };
+    const gotoPre = () => {
+      --state.index;
+      state.activeRow = state.tableList[state.index - 1];
+      TrainingDetailsRef.value.getTrainingDetail(
+        state.activeRow.studentLessonTrainingId
+      );
+    };
+    return () => (
+      <div>
+        <div >
+          <div class={styles.searchList}>
+            <NForm label-placement="left" inline>
+                       <NFormItem>
+                <CDatePicker
+                  v-model:value={timer.value}
+                  separator={'至'}
+                  type="daterange"
+                  timerValue={timer.value}></CDatePicker>
+              </NFormItem>
+              <NFormItem>
+                <CSelect
+                  {...({
+                    options: [
+                      {
+                        label: '提交状态',
+                        value: null
+                      },
+                      ...trainingStatusArray
+                    ],
+                    placeholder: '提交状态',
+                    clearable: true,
+                    inline: true
+                  } as any)}
+                  v-model:value={state.searchForm.trainingStatus}></CSelect>
+              </NFormItem>
+
+
+              <NFormItem>
+                <NSpace justify="end">
+                  <NButton type="primary" class="searchBtn" onClick={search}>
+                    搜索
+                  </NButton>
+                  <NButton
+                    type="primary"
+                    ghost
+                    class="resetBtn"
+                    onClick={onReset}>
+                    重置
+                  </NButton>
+                </NSpace>
+              </NFormItem>
+            </NForm>
+          </div>
+          <div >
+            <NDataTable
+              class={styles.classTable}
+              loading={state.loading}
+              columns={columns()}
+              data={state.tableList}></NDataTable>
+            <Pagination
+              v-model:page={state.pagination.page}
+              v-model:pageSize={state.pagination.rows}
+              v-model:pageTotal={state.pagination.pageTotal}
+              onList={getList}
+              sync
+
+            />
+          </div>
+        </div>
+        <NModal
+          v-model:show={state.detailVisiable}
+          preset="card"
+          class={['modalTitle background', styles.wordDetailModel]}
+          title={'训练详情'}>
+          <StudentTraomomhDetails
+            // onNext={() => goToNext()}
+            // onPre={() => gotoPre()}
+            ref={TrainingDetailsRef}
+            onClose={() => (state.detailVisiable = false)}
+            total={state.tableList.length}
+            current={state.index}
+            activeRow={state.activeRow}></StudentTraomomhDetails>
+        </NModal>
+      </div>
+    );
+  }
+});

BIN
src/views/studentList/images/cordWrap.png


BIN
src/views/studentList/images/strudentCore.png


BIN
src/views/studentList/images/studentBg.png


BIN
src/views/studentList/images/studentClose.png


BIN
src/views/studentList/images/studentStart.png


BIN
src/views/studentList/images/studentTitle.png


+ 216 - 0
src/views/studentList/index.module.less

@@ -252,3 +252,219 @@
   width: 830px;
   overflow: hidden;
 }
+.addStudentWrap {
+  position: relative;
+  width: 467px;
+  height: 688px;
+  margin: 80px auto 0;
+  background: url(./images/studentBg.png) no-repeat;
+  background-size: 467px 688px;
+  padding: 18px 21px 15px 27px;
+  .studentCLose {
+    cursor: pointer;
+    position: absolute;
+    right: -38px;
+    top: -37px;
+    width: 51px;
+    height: 51px;
+    z-index: 100;
+  }
+  .stunentStart {
+    position: absolute;
+    width: 601px;
+    height: 275px;
+    left: -38px;
+    top: -68px;
+  }
+  .addStudentInfo {
+    width: 419px;
+    height: 655px;
+    position: relative;
+
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .studentInfoBg {
+      position: absolute;
+      width: 419px;
+      height: 655px;
+      img {
+        left: 0;
+        top: 0;
+        width: 419px;
+        height: 655px;
+      }
+    }
+    .addTitle {
+      width: 230px;
+      height: 48px;
+      top: -9px;
+      position: relative;
+      margin: 0 auto;
+      z-index: 100;
+      img {
+        width: 230px;
+        height: 48px;
+      }
+    }
+    .studentCore {
+      position: absolute;
+      bottom: 27px;
+      left: 48px;
+      width: 330px;
+      height: 339px;
+      background: rgba(255, 255, 255, 0.33);
+      border-radius: 17px;
+      border: 2px solid #ffffff;
+      backdrop-filter: blur(17px);
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      .schoolLogo {
+        width: 67px;
+        height: 67px;
+        margin-top: -34px;
+        border: 1px solid #fff;
+        border-radius: 50%;
+        z-index: 100;
+        overflow: hidden;
+        background-color: #fff;
+        position: absolute;
+      }
+      .studentCoreInfo {
+        margin-top: 6px;
+        width: 314px;
+        height: 323px;
+        background: #ffffff;
+        border-radius: 17px;
+
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        h2 {
+          margin-top: 37px;
+          height: 22px;
+          font-size: 16px;
+          font-weight: 600;
+          color: #000000;
+          line-height: 22px;
+          margin-bottom: 6px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+        .studentCoreInfoSubtitle {
+          font-size: 14px;
+          font-weight: 400;
+          color: #000000;
+          span {
+            color: #198cfe;
+            font-weight: 600;
+          }
+        }
+        .codewrap {
+          width: 147px;
+          height: 145px;
+          background: url(./images/cordWrap.png) no-repeat;
+          position: relative;
+          background-size: 147px 145px;
+          margin: 16px 0 18px;
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          justify-content: center;
+        }
+        .codewrapSubmit {
+          width: 213px;
+          height: 35px;
+          background: linear-gradient(135deg, #d1fdf9 0%, #d6eeff 100%);
+          box-shadow: inset 0px 1px 0px 0px rgba(255, 255, 255, 0.62);
+          border-radius: 18px;
+          text-align: center;
+          line-height: 35px;
+          font-size: 14px;
+          color: #117de9;
+          span {
+            font-weight: 600;
+          }
+        }
+      }
+    }
+  }
+  .studentBottom {
+    width: 419px;
+
+    position: absolute;
+    bottom: -120px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    p {
+      text-align: center;
+      font-size: 16px;
+      color: #ffffff;
+      margin-bottom: 30px;
+    }
+    .downBtn {
+      width: 295px;
+      height: 60px;
+      background: linear-gradient(90deg, #ff7b57 0%, #ff3460 100%);
+      border-radius: 30px;
+      line-height: 60px;
+      font-size: 24px;
+      font-weight: 600;
+      text-align: center;
+      color: #fff;
+
+      cursor: pointer;
+    }
+  }
+}
+
+.setInfo {
+  margin-top: 64px;
+
+
+  :global {
+    .n-form-item-label {
+      font-size: 15px;
+      color: rgba(0, 0, 0, 0.8);
+    }
+
+    .n-input {
+      height: 50px;
+      border-radius: 8px;
+
+      .n-input__input-el {
+        height: 100%;
+      }
+
+      &.n-input--disabled {
+        background-color: #f5f6fa;
+        color: rgba(149, 149, 152, 1);
+      }
+    }
+
+    .n-base-selection {
+      border-radius: 8px;
+
+      .n-base-selection-label {
+        height: 50px;
+      }
+    }
+
+    .n-base-selection.n-base-selection--disabled .n-base-selection-label {
+      background-color: #f5f6fa;
+      color: rgba(149, 149, 152, 1);
+    }
+  }
+}
+
+.btnList {
+  margin-top: 300px;
+}
+
+
+.wordDetailModel {
+  width: 1012px;
+}

+ 100 - 88
src/views/studentList/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, reactive } from 'vue';
+import { defineComponent, onMounted, reactive } from 'vue';
 import styles from './index.module.less';
 import {
   NButton,
@@ -6,6 +6,7 @@ import {
   NForm,
   NFormItem,
   NImage,
+  NModal,
   NSelect,
   NSpace
 } from 'naive-ui';
@@ -14,10 +15,19 @@ import CSelect from '@/components/CSelect';
 import Pagination from '@/components/pagination';
 import add from './images/add.png';
 import { useRoute, useRouter } from 'vue-router';
+import { getStudentList } from './api';
+import { classGroupList } from '@/views/classList/api';
+import AddStudentModel from './modals/addStudentModel';
 export default defineComponent({
   name: 'student-studentList',
   setup(props, { emit }) {
     const state = reactive({
+      searchForm: {
+        keyword: '',
+        gender: null as any,
+        classGroupId: null as any,
+        membership: null as any
+      },
       searchWord: '',
       orchestraType: null,
       courseTypeCode: null,
@@ -30,60 +40,60 @@ export default defineComponent({
         rows: 10,
         pageTotal: 4
       },
-      tableList: [
-        {
-          nickname: '汤科斯',
-          phone: '17625367893',
-          sex: '0',
-          className: '一年级3班',
-          classType: 'normal',
-          studentType: 'member',
-          id: '1001415'
-        },
-        {
-          studentName: '丁曼蓉',
-          phone: '14677789334',
-          sex: '1',
-          className: '一年级3班',
-          classType: 'normal',
-          studentType: ''
-        },
-        {
-          studentName: '李书意',
-          phone: '13467857893',
-          sex: '1',
-          className: '一年级3班',
-          classType: 'graduate',
-          studentType: 'member'
-        },
-        {
-          studentName: '夏小满',
-          phone: '13925367893',
-          sex: '0',
-          className: '一年级3班',
-          classType: 'none',
-          studentType: ''
-        }
-      ] as any
+      tableList: [] as any,
+      classList: [],
+      addStudentVisible: false
     });
     const route = useRoute();
     const router = useRouter();
     const search = () => {
-      console.log('search', state);
+      state.pagination.page = 1;
+      getList();
+    };
+    const getClasslist = async () => {
+      try {
+        const res = await classGroupList({ page: 1, rows: 999 });
+        state.classList = res.data.rows.map((item: any) => {
+          return {
+            label: item.name,
+            value: item.id
+          };
+        });
+      } catch (e) {
+        console.log(e);
+      }
     };
-
     const onReset = () => {
-      console.log('search');
+      state.searchForm = {
+        keyword: '',
+        gender: null as any,
+        classGroupId: null as any,
+        membership: null as any
+      };
+      search();
     };
-    const getList = () => {
+    const getList = async () => {
+      try {
+        const res = await getStudentList({
+          ...state.searchForm,
+          ...state.pagination
+        });
+        state.tableList = res.data.rows;
+        state.pagination.pageTotal = res.data.total;
+      } catch (e) {
+        console.log(e);
+      }
       console.log('getList');
     };
-
+    onMounted(() => {
+      getList();
+      getClasslist();
+    });
     const columns = () => {
       return [
         {
-          title: '姓名',
-          key: 'studentName'
+          title: '学生姓名',
+          key: 'nickname'
         },
         {
           title: '手机号',
@@ -91,38 +101,27 @@ export default defineComponent({
         },
         {
           title: '性别',
-          key: 'sex',
-          render(row: any) {
-            return <>{row.sex == '0' ? '女' : '男'}</>;
-          }
-        },
-
-        {
-          title: '班级',
-          key: 'className',
+          key: 'gender',
           render(row: any) {
             return (
               <>
-                <div>
-                  {row.classType == 'none' ? (
-                    <p style={{ color: '#EA4132' }}>{'未在班级'}</p>
-                  ) : null}
-                  {row.classType == 'graduate' ? (
-                    <p style={{ color: '#AAAAAA' }}>{'毕业'}</p>
-                  ) : null}
-                  {row.classType == 'normal' ? <p>{row.className}</p> : null}
-                </div>
+                {row.gender + '' != 'null'
+                  ? row.gender == '0'
+                    ? '女'
+                    : '男'
+                  : '--'}
               </>
             );
           }
         },
-        {
-          title: '学生类型',
-          key: 'studentType',
-          render(row: any) {
-            return <>{row.studentType == 'member' ? '会员' : '普通'}</>;
-          }
-        },
+
+        // {
+        //   title: '学生类型',
+        //   key: 'studentType',
+        //   render(row: any) {
+        //     return <>{row.studentType == 'member' ? '会员' : '普通'}</>;
+        //   }
+        // },
         {
           title: '操作',
           key: 'id',
@@ -151,9 +150,9 @@ export default defineComponent({
               <SearchInput
                 {...{ placeholder: '请输入学生姓名' }}
                 class={styles.searchInput}
-                searchWord={state.searchWord}
+                searchWord={state.searchForm.keyword}
                 onChangeValue={(val: string) =>
-                  (state.searchWord = val)
+                  (state.searchForm.keyword = val)
                 }></SearchInput>
             </NFormItem>
 
@@ -162,57 +161,59 @@ export default defineComponent({
                 {...({
                   options: [
                     {
-                      label: '竖笛',
-                      value: 'song0'
+                      label: '学生性别',
+                      value: null
                     },
                     {
-                      label: '葫芦丝',
-                      value: 'song1'
+                      label: '男',
+                      value: 1
+                    },
+                    {
+                      label: '女',
+                      value: 0
                     }
                   ],
-                  placeholder: '学生声部',
+                  placeholder: '性别',
                   clearable: true,
                   inline: true
                 } as any)}
-                v-model:value={state.subjectId}></CSelect>
+                v-model:value={state.searchForm.gender}></CSelect>
             </NFormItem>
             <NFormItem>
               <CSelect
                 {...({
                   options: [
-                    {
-                      label: '一年级2班',
-                      value: 'song0'
-                    },
-                    {
-                      label: '一年级3班',
-                      value: 'song1'
-                    }
+                    { label: '年级班级', value: null },
+                    ...state.classList
                   ],
                   placeholder: '年级班级',
                   clearable: true,
                   inline: true
                 } as any)}
-                v-model:value={state.classId}></CSelect>
+                v-model:value={state.searchForm.classGroupId}></CSelect>
             </NFormItem>
             <NFormItem>
               <CSelect
                 {...({
                   options: [
                     {
+                      label: '学生类型',
+                      value: null
+                    },
+                    {
                       label: '会员',
-                      value: 'song0'
+                      value: true
                     },
                     {
                       label: '普通',
-                      value: 'song1'
+                      value: false
                     }
                   ],
                   placeholder: '学生类型',
                   clearable: true,
                   inline: true
                 } as any)}
-                v-model:value={state.studentType}></CSelect>
+                v-model:value={state.searchForm.membership}></CSelect>
             </NFormItem>
             <NFormItem>
               <NSpace justify="end">
@@ -231,6 +232,9 @@ export default defineComponent({
           </NForm>
         </div>
         <NButton
+          onClick={() => {
+            state.addStudentVisible = true;
+          }}
           class={styles.addBtn}
           type="primary"
           v-slots={{
@@ -257,6 +261,14 @@ export default defineComponent({
             saveKey="orchestraRegistration-key"
           />
         </div>
+        {state.addStudentVisible ? (
+          <div v-model:show={state.addStudentVisible} class="n-modal-mask">
+            <AddStudentModel
+              onClose={() => {
+                state.addStudentVisible = false;
+              }}></AddStudentModel>
+          </div>
+        ) : null}
       </div>
     );
   }

+ 164 - 0
src/views/studentList/modals/addStudentModel.tsx

@@ -0,0 +1,164 @@
+import {
+  NButton,
+  NSpace,
+  useMessage,
+  NForm,
+  NFormItem,
+  NSelect,
+  NImage
+} from 'naive-ui';
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import CSelect from '/src/components/CSelect';
+import stunentStart from '../images/studentStart.png';
+import studentCLose from '../images/studentClose.png';
+import { useUserStore } from '@/store/modules/users';
+import TheQrCode from '/src/components/TheQrCode';
+import { vaildUrl } from '@/utils/urlUtils';
+import logo from '@/common/images/logo.png';
+import studentTitle from '../images/studentTitle.png';
+import studentInfo from '../images/strudentCore.png';
+import codewrapBg from '../images/cordWrap.png';
+import html2canvas from 'html2canvas';
+export default defineComponent({
+  props: {
+    activeRow: {
+      type: Object,
+      default: () => ({ id: '' })
+    },
+    gradeNumList: {
+      type: Array,
+      default: () => []
+    },
+    classArray: {
+      type: Array,
+      default: () => []
+    }
+  },
+  name: 'resetStudent',
+  emits: ['close', 'getList'],
+  setup(props, { emit }) {
+    const data = reactive({
+      uploading: false
+    });
+    const message = useMessage();
+    const userStore = useUserStore();
+    const foemsRef = ref();
+    const url = ref(
+      vaildUrl() +
+        `/classroom-app/#/student-register?sId=${userStore.getUserInfo.schoolInfos[0].id}`
+    );
+    const imgs = reactive({
+      saveLoading: false,
+      image: null as any,
+      shareLoading: false
+    });
+    const downImg = () => {
+      if (imgs.saveLoading) {
+        return;
+      }
+      imgs.saveLoading = true;
+      // 判断是否已经生成图片
+      if (imgs.image) {
+        saveImg();
+      } else {
+        const container: any = document.getElementById(`preview-container`);
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async canvas => {
+            const url = canvas.toDataURL('image/png');
+            imgs.image = url;
+            saveImg();
+          })
+          .catch(() => {
+            imgs.saveLoading = false;
+          });
+      }
+    };
+
+    const saveImg = async () => {
+      // showLoadingToast({ message: '图片生成中...', forbidClick: true });
+      setTimeout(() => {
+        imgs.saveLoading = false;
+      }, 100);
+      const link = document.createElement('a');
+      link.setAttribute('download', '报名图片' + '.png');
+      // 添加时间戳,防止浏览器缓存图片
+      // console.log(imgUrl,'imgUrl')
+
+      link.href = imgs.image;
+      link.click();
+      // const res = await promisefiyPostMessage({
+      //   api: 'savePicture',
+      //   content: {
+      //     base64: imgs.image
+      //   }
+      // });
+      // if (res?.content?.status === 'success') {
+      //   showSuccessToast('保存成功');
+      // } else {
+      //   showFailToast('保存失败');
+      // }
+    };
+    return () => (
+      <div class={[styles.addStudentWrap]}>
+        <div
+          onClick={() => {
+            console.log('点击');
+            emit('close');
+          }}>
+          <NImage
+            src={studentCLose}
+            previewDisabled
+            class={styles.studentCLose}></NImage>
+        </div>
+        <NImage
+          previewDisabled
+          class={styles.stunentStart}
+          src={stunentStart}></NImage>
+        <div class={styles.addStudentInfo} id="preview-container">
+          <NImage
+            class={styles.addTitle}
+            previewDisabled
+            src={studentTitle}></NImage>
+          <NImage
+            class={styles.studentInfoBg}
+            previewDisabled
+            src={studentInfo}></NImage>
+
+          <div class={styles.studentCore}>
+            <NImage
+              previewDisabled
+              class={styles.schoolLogo}
+              src={
+                userStore.getUserInfo.schoolInfos[0].logo +
+                `?some=${new Date().getTime()}`
+              }></NImage>
+            <div class={styles.studentCoreInfo}>
+              <h2>{userStore.getUserInfo.schoolInfos[0].name}</h2>
+              <p class={styles.studentCoreInfoSubtitle}>
+                邀请您的孩子加入
+                <span>数字化乐器课堂</span>
+              </p>
+              <div class={styles.codewrap}>
+                <TheQrCode logoSrc={logo} text={url.value} size={119} />
+              </div>
+              <div class={styles.codewrapSubmit}>
+                请扫描上方二维码 <span>完成报名</span>{' '}
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class={styles.studentBottom}>
+          <p>请将二维码图片发送给学生家长,扫码完成报名</p>
+          <div class={styles.downBtn} onClick={downImg}>
+            下载图片
+          </div>
+        </div>
+      </div>
+    );
+  }
+});

+ 181 - 0
src/views/studentList/modals/studentTraomomhDetails.tsx

@@ -0,0 +1,181 @@
+import {
+  NButton,
+  NSpace,
+  useMessage,
+  NForm,
+  NFormItem,
+  NSelect,
+  NImage,
+  NScrollbar
+} from 'naive-ui';
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from '@/views/classList/index.module.less';
+import TrainType from '@/views/attend-class/model/train-type';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+import noSub from '@/views/classList/images/nosub.png';
+import qualified from '@/views/classList/images/qualified.png';
+import unqualified from '@/views/classList/images/unqualified.png';
+import { evaluateDifficult } from '/src/utils/contants';
+import dayjs from 'dayjs';
+import { getTrainingStudentDetail } from '../../classList/api';
+export default defineComponent({
+  props: {
+    activeRow: {
+      type: Object,
+      default: () => ({ id: '' })
+    },
+    total: {
+      type: Number,
+      default: 0
+    },
+    current: {
+      type: Number,
+      default: 0
+    }
+  },
+  name: 'studentTraomomhDetails',
+  emits: ['close'],
+
+  setup(props, { emit, expose }) {
+    const data = reactive({
+      uploading: false
+    });
+    const teacherInfo = ref({
+      teacherName: '',
+      createTime: '',
+      expireDate: '',
+      teacherAvatar: '',
+      studentLessonTrainingDetails: [] as any
+    } as any);
+    const message = useMessage();
+    const foemsRef = ref();
+    const typeFormat = (trainingType: string, configJson: any) => {
+      let tList: string[] = [];
+
+      if (trainingType === 'EVALUATION') {
+        tList = [
+          `${evaluateDifficult[configJson.evaluateDifficult]}`,
+          '全部小节',
+          `速度${configJson.evaluateSpeed}`,
+          `${configJson.trainingTimes}分合格`
+        ];
+        console.log('configJson.evaluateDifficult--', tList);
+      } else {
+        tList = [
+          `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
+          `速度${configJson.practiceSpeed}`,
+          `${configJson.trainingTimes}分钟`
+        ];
+        console.log('configJson.evaluateDifficult', tList);
+      }
+      return tList;
+    };
+    const getTrainingDetail = async (id: any) => {
+      console.log(id,'getTrainingDetail')
+      try {
+        const res = await getTrainingStudentDetail({
+          studentLessonTrainingId: id
+        });
+        const arr = res.data.studentLessonTrainingDetails.map((item: any) => {
+          const tList = typeFormat(
+            item.trainingType,
+            JSON.parse(item.trainingContent)
+          );
+          return {
+            ...item,
+            coverImg: item.titleImg,
+            allTimes: JSON.parse(item.trainingContent).trainingTimes,
+            typeList: tList || []
+          };
+        });
+        teacherInfo.value = {
+          ...res.data,
+
+          studentLessonTrainingDetails: arr
+        };
+      } catch (e) {
+        console.log(e);
+      }
+    };
+    expose({ getTrainingDetail });
+    onMounted(() => {
+      getTrainingDetail(props.activeRow.id);
+    });
+
+    return () => (
+      <div class={[styles.trainingDetails]}>
+        <div class={styles.studentList}>
+          <div class={styles.studentHeaderWrap}>
+            <div class={styles.studentHeader}>
+              <div class={styles.studentHeaderBorder}>
+                <NImage
+                  class={styles.studentHeaderImg}
+                  src={
+                    teacherInfo.value.teacherAvatar
+                      ? teacherInfo.value.teacherAvatar
+                      : defultHeade
+                  }
+                  previewDisabled></NImage>
+              </div>
+            </div>
+
+            <div class={styles.workafterInfo}>
+              <h4>
+                {teacherInfo.value.teacherName}{' '}
+                <div
+                  class={[
+                    styles.workafterInfoDot,
+                    styles.workafterTeacherInfoDot
+                  ]}>
+                  老师
+                </div>
+              </h4>
+              <p>
+                开始时间:
+                {teacherInfo.value.createTime
+                  ? dayjs(new Date(teacherInfo.value.createTime)).format(
+                      'YYYY-MM-DD'
+                    )
+                  : '--'}{' '}
+                | 结束时间:
+                {dayjs(new Date(teacherInfo.value.expireDate)).format(
+                  'YYYY-MM-DD'
+                )}
+              </p>
+            </div>
+          </div>
+          {teacherInfo.value.trainingStatus == 'UNSUBMITTED' ? (
+            <NImage
+              previewDisabled
+              class={styles.workStatus}
+              src={noSub}></NImage>
+          ) : null}
+          {teacherInfo.value.trainingStatus == 'SUBMITTED' ? (
+            <NImage
+              previewDisabled
+              class={styles.workStatus}
+              src={unqualified}></NImage>
+          ) : null}
+          {teacherInfo.value.trainingStatus == 'TARGET' ? (
+            <NImage
+              previewDisabled
+              class={styles.workStatus}
+              src={qualified}></NImage>
+          ) : null}
+        </div>
+        <NScrollbar style="max-height:400px" trigger="none">
+          <div class={styles.workList}>
+            {teacherInfo.value.studentLessonTrainingDetails.map((item: any) => (
+              <TrainType
+                style={{ marginBottom: '20px' }}
+                isDisabled={true}
+                isDelete={false}
+                isCLassWork={false}
+                item={item}></TrainType>
+            ))}
+          </div>
+        </NScrollbar>
+      </div>
+    );
+  }
+});

+ 14 - 10
src/views/studentList/studentDetail.tsx

@@ -22,15 +22,17 @@ import femaleIcon from '@/views/setting/images/femaleIcon.png';
 import maleIcon from '@/views/setting/images/maleIcon.png';
 import PracticeData from '@/views/studentList/components/practiceData';
 import EvaluationRecords from '@/views/studentList/components/evaluationRecords';
-
+import BaseInfo from '@/views/studentList/components/baseInfo';
+import StudentAfterWork from './components/studentAfterWork'
+import { getTabsCache, setTabsCaches } from '@/hooks/use-async';
 import dayjs from 'dayjs';
 export default defineComponent({
   name: 'studentDetail',
   setup(props, { emit }) {
     const state = reactive({
-      studentInfo: { avatar: '', nickname: '', gender: null, subjectNames: '' }
+      studentInfo: { avatar: '', nickname: '', gender: null, subjectNames: '',classGroupName:'' }
     });
-    const activeStudentTab = ref('textRcode');
+    const activeStudentTab = ref('baseInfo');
     const route = useRoute();
     console.log(route.meta.isClass);
     const routerList = ref(
@@ -42,7 +44,6 @@ export default defineComponent({
           ]
         : ([
             { name: '学生管理', path: '/studentList' },
-
             { name: route.query.studentName, path: '/classStudentRecode' }
           ] as any)
     );
@@ -61,7 +62,9 @@ export default defineComponent({
     onMounted(() => {
       getWorkInfo();
     });
-
+    const setTabs = (val: any) => {
+      setTabsCaches(val, 'tabName', route);
+    };
     return () => (
       <div>
         <CBreadcrumb list={routerList.value}></CBreadcrumb>
@@ -88,14 +91,15 @@ export default defineComponent({
                   }></NImage>
               </h4>
               <p>
-                {route.query.name}{' '}
+                {state.studentInfo.classGroupName}{' '}
                 {state.studentInfo.subjectNames
-                  ? '|' + state.studentInfo.subjectNames
-                  : ''}
+                  ? '| ' + state.studentInfo.subjectNames
+                  : ' '}
               </p>
             </div>
           </div>
           <NTabs
+              onUpdate:value={(val: any) => setTabs(val)}
             class={styles.customTabs}
             v-model:value={activeStudentTab.value}
             size="large"
@@ -103,10 +107,10 @@ export default defineComponent({
             pane-wrapper-style="margin: 0 -4px"
             pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;">
             <NTabPane name="baseInfo" tab="基本信息">
-              基本信息
+              <BaseInfo studentInfo={state.studentInfo}></BaseInfo>
             </NTabPane>
             <NTabPane name="afterWork" tab="课后训练">
-              课后训练
+              <StudentAfterWork></StudentAfterWork>
             </NTabPane>
             <NTabPane name="textRcode" tab="练习记录">
               <PracticeData