Commit a699aedcfbdf42359c72a6ca0029aa5fea7b0146
Merge branch 'main_dev' into 'main'
Main dev See merge request yunteng/thingskit-view!232
Showing
25 changed files
with
909 additions
and
828 deletions
... | ... | @@ -57,7 +57,11 @@ |
57 | 57 | "vue-router": "4.0.12", |
58 | 58 | "vue3-lazyload": "^0.2.5-beta", |
59 | 59 | "vue3-sketch-ruler": "^1.3.3", |
60 | - "vuedraggable": "^4.1.0" | |
60 | + "vuedraggable": "^4.1.0", | |
61 | + "xgplayer": "^3.0.14", | |
62 | + "xgplayer-flv": "^3.0.14", | |
63 | + "xgplayer-hls": "^3.0.14", | |
64 | + "xgplayer-mp4": "^3.0.14" | |
61 | 65 | }, |
62 | 66 | "devDependencies": { |
63 | 67 | "@commitlint/cli": "^17.0.2", | ... | ... |
1 | -lockfileVersion: '6.1' | |
1 | +lockfileVersion: '6.0' | |
2 | 2 | |
3 | 3 | settings: |
4 | 4 | autoInstallPeers: true |
... | ... | @@ -11,6 +11,9 @@ dependencies: |
11 | 11 | '@amap/amap-jsapi-types': |
12 | 12 | specifier: ^0.0.8 |
13 | 13 | version: 0.0.8 |
14 | + '@fingerprintjs/fingerprintjs': | |
15 | + specifier: ^3.4.1 | |
16 | + version: 3.4.2 | |
14 | 17 | '@types/color': |
15 | 18 | specifier: ^3.0.3 |
16 | 19 | version: 3.0.3 |
... | ... | @@ -41,6 +44,9 @@ dependencies: |
41 | 44 | dom-helpers: |
42 | 45 | specifier: ^5.2.1 |
43 | 46 | version: 5.2.1 |
47 | + echarts-gl: | |
48 | + specifier: ^2.0.9 | |
49 | + version: 2.0.9(echarts@5.3.3) | |
44 | 50 | echarts-liquidfill: |
45 | 51 | specifier: ^3.1.0 |
46 | 52 | version: 3.1.0(echarts@5.3.3) |
... | ... | @@ -50,6 +56,9 @@ dependencies: |
50 | 56 | echarts-wordcloud: |
51 | 57 | specifier: ^2.0.0 |
52 | 58 | version: 2.0.0(echarts@5.3.3) |
59 | + flv.js: | |
60 | + specifier: ^1.6.2 | |
61 | + version: 1.6.2 | |
53 | 62 | gsap: |
54 | 63 | specifier: ^3.11.3 |
55 | 64 | version: 3.11.3 |
... | ... | @@ -89,6 +98,9 @@ dependencies: |
89 | 98 | video.js: |
90 | 99 | specifier: ^7.20.3 |
91 | 100 | version: 7.21.4 |
101 | + videojs-flvjs-es6: | |
102 | + specifier: ^1.0.1 | |
103 | + version: 1.0.1 | |
92 | 104 | vue: |
93 | 105 | specifier: ^3.2.31 |
94 | 106 | version: 3.2.37 |
... | ... | @@ -113,6 +125,18 @@ dependencies: |
113 | 125 | vuedraggable: |
114 | 126 | specifier: ^4.1.0 |
115 | 127 | version: 4.1.0(vue@3.2.37) |
128 | + xgplayer: | |
129 | + specifier: ^3.0.14 | |
130 | + version: 3.0.14(core-js@3.36.1) | |
131 | + xgplayer-flv: | |
132 | + specifier: ^3.0.14 | |
133 | + version: 3.0.14(core-js@3.36.1)(xgplayer@3.0.14) | |
134 | + xgplayer-hls: | |
135 | + specifier: ^3.0.14 | |
136 | + version: 3.0.14(core-js@3.36.1)(xgplayer@3.0.14) | |
137 | + xgplayer-mp4: | |
138 | + specifier: ^3.0.14 | |
139 | + version: 3.0.14(core-js@3.36.1)(xgplayer@3.0.14) | |
116 | 140 | |
117 | 141 | devDependencies: |
118 | 142 | '@commitlint/cli': |
... | ... | @@ -1158,6 +1182,12 @@ packages: |
1158 | 1182 | - supports-color |
1159 | 1183 | dev: true |
1160 | 1184 | |
1185 | + /@fingerprintjs/fingerprintjs@3.4.2: | |
1186 | + resolution: {integrity: sha512-3Ncze6JsJpB7BpYhqIgvBpfvEX1jsEKrad5hQBpyRQxtoAp6hx3+R46zqfsuQG4D9egQZ+xftQ0u4LPFMB7Wmg==} | |
1187 | + dependencies: | |
1188 | + tslib: 2.6.2 | |
1189 | + dev: false | |
1190 | + | |
1161 | 1191 | /@humanwhocodes/config-array@0.9.5: |
1162 | 1192 | resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} |
1163 | 1193 | engines: {node: '>=10.10.0'} |
... | ... | @@ -2409,6 +2439,10 @@ packages: |
2409 | 2439 | engines: {node: '>=6.0'} |
2410 | 2440 | dev: true |
2411 | 2441 | |
2442 | + /claygl@1.3.0: | |
2443 | + resolution: {integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==} | |
2444 | + dev: false | |
2445 | + | |
2412 | 2446 | /clean-css@5.3.2: |
2413 | 2447 | resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==} |
2414 | 2448 | engines: {node: '>= 10.0'} |
... | ... | @@ -2540,6 +2574,12 @@ packages: |
2540 | 2574 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} |
2541 | 2575 | dev: true |
2542 | 2576 | |
2577 | + /concat-typed-array@1.0.2: | |
2578 | + resolution: {integrity: sha512-aC878bxeWSlrY6h60cCDwBUXpKwovZrB7+C4+VHNO1CIXW2gBLxbQ757jWtOXUscLGgYI8R84N6uy9fTJPe+0g==} | |
2579 | + engines: {node: '>=0.10.0'} | |
2580 | + deprecated: 'WARNING: This package has been renamed to typed-array-concat.' | |
2581 | + dev: false | |
2582 | + | |
2543 | 2583 | /connect-history-api-fallback@1.6.0: |
2544 | 2584 | resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} |
2545 | 2585 | engines: {node: '>=0.8'} |
... | ... | @@ -2605,6 +2645,11 @@ packages: |
2605 | 2645 | safe-buffer: 5.1.2 |
2606 | 2646 | dev: true |
2607 | 2647 | |
2648 | + /core-js@3.36.1: | |
2649 | + resolution: {integrity: sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==} | |
2650 | + requiresBuild: true | |
2651 | + dev: false | |
2652 | + | |
2608 | 2653 | /cosmiconfig-typescript-loader@2.0.1(@types/node@17.0.43)(cosmiconfig@7.0.1)(typescript@4.7.3): |
2609 | 2654 | resolution: {integrity: sha512-B9s6sX/omXq7I6gC6+YgLmrBFMJhPWew7ty/X5Tuwtd2zOSgWaUdXjkuVwbe3qqcdETo60+1nSVMekq//LIXVA==} |
2610 | 2655 | engines: {node: '>=12', npm: '>=6'} |
... | ... | @@ -2646,6 +2691,10 @@ packages: |
2646 | 2691 | which: 2.0.2 |
2647 | 2692 | dev: true |
2648 | 2693 | |
2694 | + /crypto-es@1.2.7: | |
2695 | + resolution: {integrity: sha512-UUqiVJ2gUuZFmbFsKmud3uuLcNP2+Opt+5ysmljycFCyhA0+T16XJmo1ev/t5kMChMqWh7IEvURNCqsg+SjZGQ==} | |
2696 | + dev: false | |
2697 | + | |
2649 | 2698 | /crypto-js@4.1.1: |
2650 | 2699 | resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} |
2651 | 2700 | dev: false |
... | ... | @@ -2692,6 +2741,20 @@ packages: |
2692 | 2741 | resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} |
2693 | 2742 | dev: false |
2694 | 2743 | |
2744 | + /d@1.0.2: | |
2745 | + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} | |
2746 | + engines: {node: '>=0.12'} | |
2747 | + dependencies: | |
2748 | + es5-ext: 0.10.64 | |
2749 | + type: 2.7.2 | |
2750 | + dev: false | |
2751 | + | |
2752 | + /danmu.js@1.1.13: | |
2753 | + resolution: {integrity: sha512-knFd0/cB2HA4FFWiA7eB2suc5vCvoHdqio33FyyCSfP7C+1A+zQcTvnvwfxaZhrxsGj4qaQI2I8XiTqedRaVmg==} | |
2754 | + dependencies: | |
2755 | + event-emitter: 0.3.5 | |
2756 | + dev: false | |
2757 | + | |
2695 | 2758 | /dargs@7.0.0: |
2696 | 2759 | resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} |
2697 | 2760 | engines: {node: '>=8'} |
... | ... | @@ -2765,6 +2828,11 @@ packages: |
2765 | 2828 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} |
2766 | 2829 | dev: true |
2767 | 2830 | |
2831 | + /deepmerge@2.2.1: | |
2832 | + resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} | |
2833 | + engines: {node: '>=0.10.0'} | |
2834 | + dev: false | |
2835 | + | |
2768 | 2836 | /deepmerge@4.2.2: |
2769 | 2837 | resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} |
2770 | 2838 | engines: {node: '>=0.10.0'} |
... | ... | @@ -2807,6 +2875,10 @@ packages: |
2807 | 2875 | engines: {node: '>=0.4.0'} |
2808 | 2876 | dev: false |
2809 | 2877 | |
2878 | + /delegate@3.2.0: | |
2879 | + resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} | |
2880 | + dev: false | |
2881 | + | |
2810 | 2882 | /detect-file@1.0.0: |
2811 | 2883 | resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} |
2812 | 2884 | engines: {node: '>=0.10.0'} |
... | ... | @@ -2900,6 +2972,20 @@ packages: |
2900 | 2972 | engines: {node: '>=12'} |
2901 | 2973 | dev: true |
2902 | 2974 | |
2975 | + /downloadjs@1.4.7: | |
2976 | + resolution: {integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==} | |
2977 | + dev: false | |
2978 | + | |
2979 | + /echarts-gl@2.0.9(echarts@5.3.3): | |
2980 | + resolution: {integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==} | |
2981 | + peerDependencies: | |
2982 | + echarts: ^5.1.2 | |
2983 | + dependencies: | |
2984 | + claygl: 1.3.0 | |
2985 | + echarts: 5.3.3 | |
2986 | + zrender: 5.3.2 | |
2987 | + dev: false | |
2988 | + | |
2903 | 2989 | /echarts-liquidfill@3.1.0(echarts@5.3.3): |
2904 | 2990 | resolution: {integrity: sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==} |
2905 | 2991 | peerDependencies: |
... | ... | @@ -3021,6 +3107,37 @@ packages: |
3021 | 3107 | is-symbol: 1.0.4 |
3022 | 3108 | dev: true |
3023 | 3109 | |
3110 | + /es5-ext@0.10.64: | |
3111 | + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} | |
3112 | + engines: {node: '>=0.10'} | |
3113 | + requiresBuild: true | |
3114 | + dependencies: | |
3115 | + es6-iterator: 2.0.3 | |
3116 | + es6-symbol: 3.1.4 | |
3117 | + esniff: 2.0.1 | |
3118 | + next-tick: 1.1.0 | |
3119 | + dev: false | |
3120 | + | |
3121 | + /es6-iterator@2.0.3: | |
3122 | + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} | |
3123 | + dependencies: | |
3124 | + d: 1.0.2 | |
3125 | + es5-ext: 0.10.64 | |
3126 | + es6-symbol: 3.1.4 | |
3127 | + dev: false | |
3128 | + | |
3129 | + /es6-promise@4.2.8: | |
3130 | + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} | |
3131 | + dev: false | |
3132 | + | |
3133 | + /es6-symbol@3.1.4: | |
3134 | + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} | |
3135 | + engines: {node: '>=0.12'} | |
3136 | + dependencies: | |
3137 | + d: 1.0.2 | |
3138 | + ext: 1.7.0 | |
3139 | + dev: false | |
3140 | + | |
3024 | 3141 | /esbuild@0.11.3: |
3025 | 3142 | resolution: {integrity: sha512-BzVRHcCtFepjS9WcqRjqoIxLqgpK21a8J4Zi4msSGxDxiXVO1IbcqT1KjhdDDnJxKfe7bvzZrvMEX+bVO0Elcw==} |
3026 | 3143 | hasBin: true |
... | ... | @@ -3265,6 +3382,16 @@ packages: |
3265 | 3382 | - supports-color |
3266 | 3383 | dev: true |
3267 | 3384 | |
3385 | + /esniff@2.0.1: | |
3386 | + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} | |
3387 | + engines: {node: '>=0.10'} | |
3388 | + dependencies: | |
3389 | + d: 1.0.2 | |
3390 | + es5-ext: 0.10.64 | |
3391 | + event-emitter: 0.3.5 | |
3392 | + type: 2.7.2 | |
3393 | + dev: false | |
3394 | + | |
3268 | 3395 | /espree@9.3.2: |
3269 | 3396 | resolution: {integrity: sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==} |
3270 | 3397 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} |
... | ... | @@ -3310,6 +3437,17 @@ packages: |
3310 | 3437 | engines: {node: '>=0.10.0'} |
3311 | 3438 | dev: true |
3312 | 3439 | |
3440 | + /event-emitter@0.3.5: | |
3441 | + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} | |
3442 | + dependencies: | |
3443 | + d: 1.0.2 | |
3444 | + es5-ext: 0.10.64 | |
3445 | + dev: false | |
3446 | + | |
3447 | + /eventemitter3@4.0.7: | |
3448 | + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} | |
3449 | + dev: false | |
3450 | + | |
3313 | 3451 | /events@3.3.0: |
3314 | 3452 | resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} |
3315 | 3453 | engines: {node: '>=0.8.x'} |
... | ... | @@ -3341,6 +3479,12 @@ packages: |
3341 | 3479 | homedir-polyfill: 1.0.3 |
3342 | 3480 | dev: true |
3343 | 3481 | |
3482 | + /ext@1.7.0: | |
3483 | + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} | |
3484 | + dependencies: | |
3485 | + type: 2.7.2 | |
3486 | + dev: false | |
3487 | + | |
3344 | 3488 | /extend@3.0.2: |
3345 | 3489 | resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} |
3346 | 3490 | dev: true |
... | ... | @@ -3490,6 +3634,13 @@ packages: |
3490 | 3634 | resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==} |
3491 | 3635 | dev: true |
3492 | 3636 | |
3637 | + /flv.js@1.6.2: | |
3638 | + resolution: {integrity: sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==} | |
3639 | + dependencies: | |
3640 | + es6-promise: 4.2.8 | |
3641 | + webworkify-webpack: 2.1.5 | |
3642 | + dev: false | |
3643 | + | |
3493 | 3644 | /follow-redirects@1.15.1: |
3494 | 3645 | resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==} |
3495 | 3646 | engines: {node: '>=4.0'} |
... | ... | @@ -4590,6 +4741,10 @@ packages: |
4590 | 4741 | resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} |
4591 | 4742 | dev: true |
4592 | 4743 | |
4744 | + /next-tick@1.1.0: | |
4745 | + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} | |
4746 | + dev: false | |
4747 | + | |
4593 | 4748 | /no-case@3.0.4: |
4594 | 4749 | resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} |
4595 | 4750 | dependencies: |
... | ... | @@ -5732,6 +5887,10 @@ packages: |
5732 | 5887 | resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} |
5733 | 5888 | dev: true |
5734 | 5889 | |
5890 | + /tslib@2.6.2: | |
5891 | + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} | |
5892 | + dev: false | |
5893 | + | |
5735 | 5894 | /tsutils@3.21.0(typescript@4.6.3): |
5736 | 5895 | resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} |
5737 | 5896 | engines: {node: '>= 6'} |
... | ... | @@ -5774,6 +5933,10 @@ packages: |
5774 | 5933 | engines: {node: '>=8'} |
5775 | 5934 | dev: true |
5776 | 5935 | |
5936 | + /type@2.7.2: | |
5937 | + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} | |
5938 | + dev: false | |
5939 | + | |
5777 | 5940 | /typescript@4.6.3: |
5778 | 5941 | resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==} |
5779 | 5942 | engines: {node: '>=4.2.0'} |
... | ... | @@ -5912,6 +6075,10 @@ packages: |
5912 | 6075 | videojs-vtt.js: 0.15.4 |
5913 | 6076 | dev: false |
5914 | 6077 | |
6078 | + /videojs-flvjs-es6@1.0.1: | |
6079 | + resolution: {integrity: sha512-wAI5ff2tZVW+uftTLyPmS38F4SHmMlxqBFOgXEBqMs2X0N4uIVQK0iCCv5XACXH+oc+mP70D23mJmT8KsoHx0g==} | |
6080 | + dev: false | |
6081 | + | |
5915 | 6082 | /videojs-font@3.2.0: |
5916 | 6083 | resolution: {integrity: sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==} |
5917 | 6084 | dev: false |
... | ... | @@ -6261,6 +6428,10 @@ packages: |
6261 | 6428 | - uglify-js |
6262 | 6429 | dev: true |
6263 | 6430 | |
6431 | + /webworkify-webpack@2.1.5: | |
6432 | + resolution: {integrity: sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==} | |
6433 | + dev: false | |
6434 | + | |
6264 | 6435 | /which-boxed-primitive@1.0.2: |
6265 | 6436 | resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} |
6266 | 6437 | dependencies: |
... | ... | @@ -6308,6 +6479,101 @@ packages: |
6308 | 6479 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} |
6309 | 6480 | dev: true |
6310 | 6481 | |
6482 | + /xgplayer-flv@3.0.14(core-js@3.36.1)(xgplayer@3.0.14): | |
6483 | + resolution: {integrity: sha512-9mSBUbbzkJhFjQOaU8SvnEzDj4Y9tkHsW0IaIxCDi/DlDcHzSubdiuuTVNf1A5cgUSTnFHxwDJtzqVm6FhmTeg==} | |
6484 | + peerDependencies: | |
6485 | + core-js: '>=3.12.1' | |
6486 | + xgplayer: '>=3.0.1' | |
6487 | + dependencies: | |
6488 | + core-js: 3.36.1 | |
6489 | + eventemitter3: 4.0.7 | |
6490 | + xgplayer: 3.0.14(core-js@3.36.1) | |
6491 | + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1) | |
6492 | + xgplayer-transmuxer: 3.0.14(core-js@3.36.1) | |
6493 | + dev: false | |
6494 | + | |
6495 | + /xgplayer-hls@3.0.14(core-js@3.36.1)(xgplayer@3.0.14): | |
6496 | + resolution: {integrity: sha512-UiVDiDcIzQ6kiDZitbyXqRx5nX/Pi69S9Vfh5lc+6R8SsA2ZyvxWtGOQ6XfpZyXYHfl56JMbyNObOgMT1iRNLw==} | |
6497 | + peerDependencies: | |
6498 | + core-js: '>=3.12.1' | |
6499 | + xgplayer: 3.0.14 | |
6500 | + dependencies: | |
6501 | + core-js: 3.36.1 | |
6502 | + eventemitter3: 4.0.7 | |
6503 | + xgplayer: 3.0.14(core-js@3.36.1) | |
6504 | + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1) | |
6505 | + xgplayer-transmuxer: 3.0.14(core-js@3.36.1) | |
6506 | + dev: false | |
6507 | + | |
6508 | + /xgplayer-mp4-loader@3.0.14(core-js@3.36.1): | |
6509 | + resolution: {integrity: sha512-cCPH7I/i+saP5i+vivfnu7Bekxm1sS3JlTzaqX2crUL9a4MfWi/r7Yumqsn52lyQGyb+lGKOsvbHeR3wTwUx1w==} | |
6510 | + peerDependencies: | |
6511 | + core-js: '>=3.12.1' | |
6512 | + dependencies: | |
6513 | + core-js: 3.36.1 | |
6514 | + eventemitter3: 4.0.7 | |
6515 | + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1) | |
6516 | + xgplayer-transmuxer: 3.0.14(core-js@3.36.1) | |
6517 | + dev: false | |
6518 | + | |
6519 | + /xgplayer-mp4@3.0.14(core-js@3.36.1)(xgplayer@3.0.14): | |
6520 | + resolution: {integrity: sha512-EEZ3KtG7JbOBL33P+Uv98PaIft9tI9zw2rfyS7Sip57hT/9tJ2az4qz9qNSRcrrlLTo0DTYGR/JzADu7l/IyUQ==} | |
6521 | + peerDependencies: | |
6522 | + core-js: '>=3.12.1' | |
6523 | + xgplayer: '>=3.0.0' | |
6524 | + dependencies: | |
6525 | + concat-typed-array: 1.0.2 | |
6526 | + core-js: 3.36.1 | |
6527 | + deepmerge: 2.2.1 | |
6528 | + eventemitter3: 4.0.7 | |
6529 | + xgplayer: 3.0.14(core-js@3.36.1) | |
6530 | + xgplayer-mp4-loader: 3.0.14(core-js@3.36.1) | |
6531 | + xgplayer-streaming-shared: 3.0.14(core-js@3.36.1) | |
6532 | + xgplayer-transmuxer: 3.0.14(core-js@3.36.1) | |
6533 | + dev: false | |
6534 | + | |
6535 | + /xgplayer-streaming-shared@3.0.14(core-js@3.36.1): | |
6536 | + resolution: {integrity: sha512-XBXNjnMfFDl15kQfXNgmAkaDqRdN0PhxVFBm7+TezillpTdLmqj+HD90F6BargdZVJ4I20/YHYdb9qBg+hUDhg==} | |
6537 | + peerDependencies: | |
6538 | + core-js: '>=3.12.1' | |
6539 | + dependencies: | |
6540 | + core-js: 3.36.1 | |
6541 | + eventemitter3: 4.0.7 | |
6542 | + dev: false | |
6543 | + | |
6544 | + /xgplayer-subtitles@3.0.14(core-js@3.36.1): | |
6545 | + resolution: {integrity: sha512-w6H1h+g3kOI477kv2QBRMZe3M/1dHLXttHBwq4LwKTPGVQ19fLIDGwkfn+HeKwe1ocGDaaq96bS+l+BadnP9TA==} | |
6546 | + peerDependencies: | |
6547 | + core-js: '>=3.12.1' | |
6548 | + dependencies: | |
6549 | + core-js: 3.36.1 | |
6550 | + eventemitter3: 4.0.7 | |
6551 | + dev: false | |
6552 | + | |
6553 | + /xgplayer-transmuxer@3.0.14(core-js@3.36.1): | |
6554 | + resolution: {integrity: sha512-lMS2EwuA6tToCC4NuyJ5Ax0UDaaWU/YuaSkI/Bsj+vHkEzO42vgi2EerSPr91Moz05KhG/+4Vt8qMKUVFjhGTw==} | |
6555 | + peerDependencies: | |
6556 | + core-js: '>=3.12.1' | |
6557 | + dependencies: | |
6558 | + '@babel/runtime': 7.20.6 | |
6559 | + concat-typed-array: 1.0.2 | |
6560 | + core-js: 3.36.1 | |
6561 | + crypto-es: 1.2.7 | |
6562 | + dev: false | |
6563 | + | |
6564 | + /xgplayer@3.0.14(core-js@3.36.1): | |
6565 | + resolution: {integrity: sha512-TPS77hUIcM1zVx6FSpGG+OzfMwjwxxWihd+YxVx82DQ22QK60v8TXWSZlWISbmS1+fAlFneesvXDjwy60tC37w==} | |
6566 | + peerDependencies: | |
6567 | + core-js: '>=3.12.1' | |
6568 | + dependencies: | |
6569 | + core-js: 3.36.1 | |
6570 | + danmu.js: 1.1.13 | |
6571 | + delegate: 3.2.0 | |
6572 | + downloadjs: 1.4.7 | |
6573 | + eventemitter3: 4.0.7 | |
6574 | + xgplayer-subtitles: 3.0.14(core-js@3.36.1) | |
6575 | + dev: false | |
6576 | + | |
6311 | 6577 | /y18n@5.0.8: |
6312 | 6578 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} |
6313 | 6579 | engines: {node: '>=10'} | ... | ... |
1 | 1 | import { defHttp } from '@/utils/external/http/axios' |
2 | -import { ConfigurationItemType, DictItem, OrganizationListItem, UploadResponse } from './model' | |
2 | +import {CameraRecord, ConfigurationItemType, DictItem, OrganizationListItem, UploadResponse} from './model' | |
3 | 3 | import { PaginationResult } from '/#/external/axios' |
4 | 4 | import { isShareMode } from '@/views/share/hook' |
5 | 5 | |
... | ... | @@ -88,7 +88,7 @@ export const getOrganizationList = (params?: OrganizationListItem) => |
88 | 88 | |
89 | 89 | //获取视频列表 |
90 | 90 | export const getVideoList = (params?: object) => |
91 | - defHttp.get({ | |
91 | + defHttp.get<{data: CameraRecord[]}>({ | |
92 | 92 | url: Api.VIDEO, |
93 | 93 | params |
94 | 94 | }) | ... | ... |
... | ... | @@ -54,3 +54,39 @@ export interface ProductAndDevice { |
54 | 54 | deviceType: string |
55 | 55 | transportType: string |
56 | 56 | } |
57 | + | |
58 | + | |
59 | +export interface CameraRecord { | |
60 | + id: string; | |
61 | + creator: string; | |
62 | + createTime: string; | |
63 | + updater: string; | |
64 | + updateTime: string; | |
65 | + name: string; | |
66 | + enabled: boolean; | |
67 | + tenantId: string; | |
68 | + videoUrl: string; | |
69 | + brand: string; | |
70 | + sn: string; | |
71 | + organizationId: string; | |
72 | + organizationName: string; | |
73 | + status: boolean; | |
74 | + accessMode: number; | |
75 | + playProtocol: number; | |
76 | + deviceId?: string; | |
77 | + channelId?: string; | |
78 | + params?: { | |
79 | + channelNo: string; | |
80 | + deviceId: string; | |
81 | + }; | |
82 | + videoPlatformDTO?: VideoPlatformDTO; | |
83 | +} | |
84 | + | |
85 | +interface VideoPlatformDTO { | |
86 | + enabled: boolean; | |
87 | + type: number; | |
88 | + host: string; | |
89 | + appKey: string; | |
90 | + appSecret: string; | |
91 | + ssl: number; | |
92 | +} | ... | ... |
src/components/Video/index.ts
0 → 100644
1 | +export {default as XGPlayer} from './src/index.vue' | ... | ... |
src/components/Video/src/index.vue
0 → 100644
1 | +<script setup lang="ts"> | |
2 | +import Player, {Events, IError} from 'xgplayer'; | |
3 | +import {FlvPlugin} from 'xgplayer-flv'; | |
4 | +import Mp4Plugin from 'xgplayer-mp4'; | |
5 | +import {HlsPlugin} from 'xgplayer-hls'; | |
6 | +import {onMounted, shallowRef, computed, unref, toRaw, onUnmounted, ref, watch} from 'vue'; | |
7 | +import PresetPlayer from 'xgplayer'; | |
8 | +import {IPlayerOptions} from 'xgplayer/es/player'; | |
9 | +import 'xgplayer/dist/index.min.css'; | |
10 | +import {StreamType, XGPlayerProps} from './types'; | |
11 | +import {isShareMode} from "@/views/share/hook"; | |
12 | +import {getJwtToken, getShareJwtToken} from "@/utils/external/auth"; | |
13 | + | |
14 | +const props = withDefaults(defineProps<{ | |
15 | + streamType?: StreamType; | |
16 | + autoPlay?: boolean; | |
17 | + url?: string; | |
18 | + withToken?: boolean; | |
19 | + config?: Omit<IPlayerOptions, 'url'>; | |
20 | +}>(), { | |
21 | + streamType: 'auto', | |
22 | + autoPlay: true, | |
23 | + config: () => ({}), | |
24 | +}); | |
25 | + | |
26 | +const emits = defineEmits<{ | |
27 | + (eventName: 'ready', player: PresetPlayer): void; | |
28 | + (eventName: 'onUnmounted', player: PresetPlayer): void; | |
29 | +}>(); | |
30 | + | |
31 | +function getStreamTypeByUrl(url = ''): StreamType | undefined { | |
32 | + if (url.endsWith('.m3u8')) return 'hls'; | |
33 | + else if (url.endsWith('.mp4')) return 'mp4'; | |
34 | + else if (url.endsWith('.flv')) { | |
35 | + return 'flv'; | |
36 | + } else return; | |
37 | +} | |
38 | + | |
39 | +const getPluginByStreamType = (): IPlayerOptions => { | |
40 | + let {url, withToken} = props; | |
41 | + let {streamType} = props; | |
42 | + streamType = streamType === 'auto' ? getStreamTypeByUrl(url)! : streamType; | |
43 | + | |
44 | + const liveConfig = { | |
45 | + targetLatency: 10, | |
46 | + maxLatency: 20, | |
47 | + disconnectTime: 0, | |
48 | + fetchOptions: withToken | |
49 | + ? { | |
50 | + headers: { | |
51 | + 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`, | |
52 | + }, | |
53 | + } | |
54 | + : {}, | |
55 | + }; | |
56 | + const config: IPlayerOptions = { | |
57 | + flv: liveConfig, | |
58 | + hls: liveConfig, | |
59 | + }; | |
60 | + switch (streamType) { | |
61 | + case 'hls': | |
62 | + config.plugins = [HlsPlugin]; | |
63 | + break; | |
64 | + case 'mp4': | |
65 | + config.plugins = [Mp4Plugin]; | |
66 | + break; | |
67 | + case 'flv': | |
68 | + config.plugins = [FlvPlugin]; | |
69 | + break; | |
70 | + } | |
71 | + return config; | |
72 | +}; | |
73 | + | |
74 | +const videoElRef = shallowRef<Nullable<HTMLDivElement>>(); | |
75 | + | |
76 | +const playerRef = shallowRef<Nullable<PresetPlayer>>(); | |
77 | + | |
78 | +const propsRef = ref<XGPlayerProps>({}); | |
79 | + | |
80 | +const getPlayerConfig = computed<IPlayerOptions>(() => { | |
81 | + const {url, autoPlay, config} = props; | |
82 | + | |
83 | + const basicConfig: IPlayerOptions = { | |
84 | + ...config, | |
85 | + ...propsRef, | |
86 | + url, | |
87 | + lang: 'zh', | |
88 | + isLive: true, | |
89 | + autoplay: autoPlay, | |
90 | + autoplayMuted: autoPlay, | |
91 | + ...getPluginByStreamType(), | |
92 | + }; | |
93 | + return basicConfig; | |
94 | +}); | |
95 | + | |
96 | +function onDecodeError() { | |
97 | + console.warn('player happend decode error'); | |
98 | + playerRef.value?.switchURL(props.url!); | |
99 | +} | |
100 | + | |
101 | +function initializePlayer() { | |
102 | + if (unref(playerRef)) { | |
103 | + playerRef.value?.destroy?.(); | |
104 | + playerRef.value = null; | |
105 | + } | |
106 | + | |
107 | + const config = toRaw(unref(getPlayerConfig)); | |
108 | + | |
109 | + if (!unref(videoElRef)) return; | |
110 | + | |
111 | + const player = (playerRef.value = new Player(Object.assign(config, {el: unref(videoElRef)}))); | |
112 | + | |
113 | + player.on(Events.READY, () => { | |
114 | + emits('ready', player); | |
115 | + }); | |
116 | + | |
117 | + player.setEventsMiddleware({ | |
118 | + error: (event, callback) => { | |
119 | + const code = ( | |
120 | + event as unknown as { | |
121 | + error: MediaError; | |
122 | + } | |
123 | + ).error.code; | |
124 | + if (code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) { | |
125 | + if (!props.url) { | |
126 | + return; | |
127 | + } | |
128 | + callback(); | |
129 | + return; | |
130 | + } | |
131 | + | |
132 | + if (code === MediaError.MEDIA_ERR_DECODE) { | |
133 | + // 视频流可以播放 中途解码失败重载 | |
134 | + if (playerRef.value?.isPlaying) { | |
135 | + onDecodeError(); | |
136 | + } | |
137 | + return; | |
138 | + } | |
139 | + | |
140 | + callback(); | |
141 | + }, | |
142 | + }); | |
143 | +} | |
144 | + | |
145 | +onMounted(() => { | |
146 | + initializePlayer(); | |
147 | +}); | |
148 | + | |
149 | +onUnmounted(() => { | |
150 | + emits('onUnmounted', unref(playerRef)!); | |
151 | + playerRef.value?.destroy?.(); | |
152 | +}); | |
153 | + | |
154 | +watch( | |
155 | + () => props.url, | |
156 | + () => { | |
157 | + initializePlayer(); | |
158 | + } | |
159 | +); | |
160 | + | |
161 | +defineExpose({ | |
162 | + getPlayerInstance: () => unref(playerRef), | |
163 | +}); | |
164 | +</script> | |
165 | + | |
166 | +<template> | |
167 | + <div ref="videoElRef"></div> | |
168 | +</template> | ... | ... |
src/components/Video/src/types.ts
0 → 100644
1 | +import { IPlayerOptions } from 'xgplayer/es/player'; | |
2 | + | |
3 | +export type StreamType = 'flv' | 'mp4' | 'hls' | 'auto'; | |
4 | +export interface XGPlayerProps { | |
5 | + streamType?: StreamType; | |
6 | + autoPlay?: boolean; | |
7 | + url?: string; | |
8 | + withToken?: boolean; | |
9 | + config?: Omit<IPlayerOptions, 'url'>; | |
10 | +} | ... | ... |
1 | -import { ref, toRefs, toRaw, watch } from 'vue' | |
1 | +import {ref, toRefs, toRaw, watch} from 'vue' | |
2 | 2 | import type VChart from 'vue-echarts' |
3 | -import { useChartDataPondFetch } from '@/hooks/' | |
4 | -import { CreateComponentType, ChartFrameEnum, CreateComponentGroupType } from '@/packages/index.d' | |
5 | -import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | |
6 | -import { isPreview, intervalUnitHandle } from '@/utils' | |
7 | -import { setOption } from '@/packages/public/chart' | |
8 | -import { useChartDataSocket } from './useChartDataSocket' | |
9 | -import { customRequest } from '@/api/external/customRequest' | |
10 | -import { useFilterFn } from './useFilterFn' | |
11 | -import { RequestContentTypeEnum } from '@/enums/external/httpEnum' | |
3 | +import {useChartDataPondFetch} from '@/hooks/' | |
4 | +import {CreateComponentType, ChartFrameEnum, CreateComponentGroupType} from '@/packages/index.d' | |
5 | +import {useChartEditStore} from '@/store/modules/chartEditStore/chartEditStore' | |
6 | +import {isPreview, intervalUnitHandle} from '@/utils' | |
7 | +import {setOption} from '@/packages/public/chart' | |
8 | +import {useChartDataSocket} from './useChartDataSocket' | |
9 | +import {customRequest} from '@/api/external/customRequest' | |
10 | +import {useFilterFn} from './useFilterFn' | |
11 | +import {RequestContentTypeEnum} from '@/enums/external/httpEnum' | |
12 | +import dayjs from 'dayjs' | |
13 | + | |
12 | 14 | |
13 | 15 | // 获取类型 |
14 | 16 | type ChartEditStoreType = typeof useChartEditStore |
... | ... | @@ -20,111 +22,125 @@ type ChartEditStoreType = typeof useChartEditStore |
20 | 22 | * @param updateCallback 自定义更新函数 |
21 | 23 | */ |
22 | 24 | export const useChartDataFetch = ( |
23 | - targetComponent: CreateComponentType, | |
24 | - useChartEditStore: ChartEditStoreType, | |
25 | - updateCallback?: (...args: any) => any | |
25 | + targetComponent: CreateComponentType, | |
26 | + useChartEditStore: ChartEditStoreType, | |
27 | + updateCallback?: (...args: any) => any | |
26 | 28 | ) => { |
27 | - const vChartRef = ref<typeof VChart | null>(null) | |
28 | - let fetchInterval: any = 0 | |
29 | + const vChartRef = ref<typeof VChart | null>(null) | |
30 | + let fetchInterval: any = 0 | |
29 | 31 | |
30 | - // 组件类型 | |
31 | - const { chartFrame } = targetComponent.chartConfig | |
32 | + // 组件类型 | |
33 | + const {chartFrame} = targetComponent.chartConfig | |
32 | 34 | |
33 | - // eCharts 组件配合 vChart 库更新方式 | |
34 | - const echartsUpdateHandle = (dataset: any) => { | |
35 | - if (chartFrame === ChartFrameEnum.ECHARTS) { | |
36 | - if (vChartRef.value) { | |
37 | - setOption(vChartRef.value, { dataset: dataset }) | |
38 | - } | |
35 | + // eCharts 组件配合 vChart 库更新方式 | |
36 | + const echartsUpdateHandle = (dataset: any) => { | |
37 | + if (chartFrame === ChartFrameEnum.ECHARTS) { | |
38 | + if (vChartRef.value) { | |
39 | + setOption(vChartRef.value, {dataset: dataset}) | |
40 | + } | |
41 | + } | |
39 | 42 | } |
40 | - } | |
41 | 43 | |
42 | - const requestIntervalFn = () => { | |
43 | - const chartEditStore = useChartEditStore() | |
44 | - if ((targetComponent.request.requestContentType as RequestContentTypeEnum) === RequestContentTypeEnum.WEB_SOCKET) return | |
44 | + const requestIntervalFn = () => { | |
45 | + const chartEditStore = useChartEditStore() | |
46 | + if ((targetComponent.request.requestContentType as RequestContentTypeEnum) === RequestContentTypeEnum.WEB_SOCKET) return | |
45 | 47 | |
46 | - // 全局数据 | |
47 | - const { | |
48 | - requestOriginUrl, | |
49 | - requestIntervalUnit: globalUnit, | |
50 | - requestInterval: globalRequestInterval | |
51 | - } = toRefs(chartEditStore.getRequestGlobalConfig) | |
48 | + // 全局数据 | |
49 | + const { | |
50 | + requestOriginUrl, | |
51 | + requestIntervalUnit: globalUnit, | |
52 | + requestInterval: globalRequestInterval | |
53 | + } = toRefs(chartEditStore.getRequestGlobalConfig) | |
52 | 54 | |
53 | - // 目标组件 | |
54 | - const { | |
55 | - requestUrl, | |
56 | - requestIntervalUnit: targetUnit, | |
57 | - requestInterval: targetInterval | |
58 | - } = toRefs(targetComponent.request) | |
55 | + // 目标组件 | |
56 | + const { | |
57 | + requestUrl, | |
58 | + requestIntervalUnit: targetUnit, | |
59 | + requestInterval: targetInterval | |
60 | + } = toRefs(targetComponent.request) | |
59 | 61 | |
60 | - try { | |
61 | - // 处理地址 | |
62 | - if (requestUrl?.value) { | |
63 | - // requestOriginUrl 允许为空 | |
64 | - const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value | |
65 | - if (!completePath) return | |
62 | + try { | |
63 | + // 处理地址 | |
64 | + if (requestUrl?.value) { | |
65 | + // requestOriginUrl 允许为空 | |
66 | + const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value | |
67 | + if (!completePath) return | |
66 | 68 | |
67 | - clearInterval(fetchInterval) | |
69 | + clearInterval(fetchInterval) | |
68 | 70 | |
69 | - const fetchFn = async () => { | |
70 | - const res = await customRequest(toRaw(targetComponent.request)) | |
71 | - if (res) { | |
72 | - try { | |
73 | - const filter = targetComponent.filter | |
74 | - const { value } = useFilterFn(filter, res) | |
75 | - echartsUpdateHandle(value) | |
76 | - // 更新回调函数 | |
77 | - if (updateCallback) { | |
78 | - updateCallback(value, targetComponent.request)//为了处理设备最新数据轮播列表,这里传过去当前组件的信息 | |
79 | - } | |
80 | - } catch (error) { | |
81 | - console.error(error) | |
82 | - } | |
83 | - } | |
84 | - } | |
71 | + const fetchFn = async () => { | |
72 | + let startTsValue = null | |
73 | + let endTsValue = null | |
74 | + const {requestParams} = toRaw(targetComponent.request) | |
75 | + const {Params} = requestParams | |
76 | + const {entityType, startTs, endTs} = Params | |
77 | + let days = Math.ceil(((endTs as unknown as number) - (startTs as unknown as number)) / (1 * 60 * 60 * 24 * 1000)) | |
78 | + if (entityType === 'DEVICE') { | |
79 | + days = days <= 2 ? 1 : days<= 8 ? 7 : 30 | |
80 | + startTsValue = dayjs().subtract(days - 1, 'day').startOf('day').valueOf() | |
81 | + startTsValue = dayjs(startTsValue).startOf('day').valueOf() | |
82 | + endTsValue = dayjs().endOf('day').valueOf() | |
83 | + ;(toRaw(targetComponent.request).requestParams.Params.startTs as unknown as number) = startTsValue as number | |
84 | + ;(toRaw(targetComponent.request).requestParams.Params.endTs as unknown as number) = endTsValue | |
85 | + } | |
86 | + const res = await customRequest(toRaw(targetComponent.request)) | |
87 | + if (res) { | |
88 | + try { | |
89 | + const filter = targetComponent.filter | |
90 | + const {value} = useFilterFn(filter, res) | |
91 | + echartsUpdateHandle(value) | |
92 | + // 更新回调函数 | |
93 | + if (updateCallback) { | |
94 | + updateCallback(value, targetComponent.request)//为了处理设备最新数据轮播列表,这里传过去当前组件的信息 | |
95 | + } | |
96 | + } catch (error) { | |
97 | + console.error(error) | |
98 | + } | |
99 | + } | |
100 | + } | |
85 | 101 | |
86 | - // 普通初始化与组件交互处理监听 | |
87 | - watch( | |
88 | - () => targetComponent.request, | |
89 | - () => { | |
90 | - fetchFn() | |
91 | - }, | |
92 | - { | |
93 | - immediate: true, | |
94 | - deep: true | |
95 | - } | |
96 | - ) | |
102 | + // 普通初始化与组件交互处理监听 | |
103 | + watch( | |
104 | + () => targetComponent.request, | |
105 | + () => { | |
106 | + fetchFn() | |
107 | + }, | |
108 | + { | |
109 | + immediate: true, | |
110 | + deep: true | |
111 | + } | |
112 | + ) | |
97 | 113 | |
98 | - // 定时时间 | |
99 | - const time = targetInterval && targetInterval.value ? targetInterval.value : globalRequestInterval.value | |
100 | - // 单位 | |
101 | - const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value | |
102 | - // 开启轮询 | |
103 | - if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit)) | |
104 | - } | |
105 | - // eslint-disable-next-line no-empty | |
106 | - } catch (error) { | |
107 | - console.log(error) | |
114 | + // 定时时间 | |
115 | + const time = targetInterval && targetInterval.value ? targetInterval.value : globalRequestInterval.value | |
116 | + // 单位 | |
117 | + const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value | |
118 | + // 开启轮询 | |
119 | + if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit)) | |
120 | + } | |
121 | + // eslint-disable-next-line no-empty | |
122 | + } catch (error) { | |
123 | + console.log(error) | |
124 | + } | |
108 | 125 | } |
109 | - } | |
110 | 126 | |
111 | - if (isPreview()) { | |
112 | - requestIntervalFn() | |
113 | - const chartEditStore = useChartEditStore() | |
114 | - const { initial } = useChartDataSocket() | |
115 | - /** | |
116 | - * 支持分组也可以接受ws | |
117 | - * 如果是分组并且绑定了ws | |
118 | - */ | |
119 | - chartEditStore.getComponentList?.forEach((item:CreateComponentType | CreateComponentGroupType)=>{ | |
120 | - if(item.isGroup){ | |
121 | - if(item.request.requestUrl?.includes('ws')){ | |
122 | - initial(item, useChartEditStore, updateCallback) | |
123 | - } | |
124 | - } | |
125 | - }) | |
126 | - // | |
127 | - initial(targetComponent, useChartEditStore, updateCallback) | |
128 | - } | |
129 | - return { vChartRef } | |
127 | + if (isPreview()) { | |
128 | + requestIntervalFn() | |
129 | + const chartEditStore = useChartEditStore() | |
130 | + const {initial} = useChartDataSocket() | |
131 | + /** | |
132 | + * 支持分组也可以接受ws | |
133 | + * 如果是分组并且绑定了ws | |
134 | + */ | |
135 | + chartEditStore.getComponentList?.forEach((item: CreateComponentType | CreateComponentGroupType) => { | |
136 | + if (item.isGroup) { | |
137 | + if (item.request.requestUrl?.includes('ws')) { | |
138 | + initial(item, useChartEditStore, updateCallback) | |
139 | + } | |
140 | + } | |
141 | + }) | |
142 | + // | |
143 | + initial(targetComponent, useChartEditStore, updateCallback) | |
144 | + } | |
145 | + return {vChartRef} | |
130 | 146 | } | ... | ... |
... | ... | @@ -45,11 +45,12 @@ export const lineSeriesItem = { |
45 | 45 | |
46 | 46 | export const option = { |
47 | 47 | tooltip: { |
48 | - show: true, | |
49 | 48 | trigger: 'axis', |
50 | 49 | axisPointer: { |
51 | - show: true, | |
52 | - type: 'shadow' | |
50 | + type: 'cross', | |
51 | + crossStyle: { | |
52 | + color: '#999' | |
53 | + } | |
53 | 54 | } |
54 | 55 | }, |
55 | 56 | legend: { | ... | ... |
... | ... | @@ -62,17 +62,30 @@ const option = computed(() => { |
62 | 62 | // dataset 无法变更条数的补丁 |
63 | 63 | watch( |
64 | 64 | () => props.chartConfig.option.dataset, |
65 | - (newData: { dimensions: any }, oldData) => { | |
65 | + (newData: any, oldData) => { | |
66 | 66 | try { |
67 | 67 | if (!isObject(newData) || !('dimensions' in newData)) return |
68 | - if (Array.isArray(newData?.dimensions)) { | |
69 | - const seriesArr = [] | |
70 | - for (let i = 0; i < newData.dimensions.length - 1; i++) { | |
71 | - seriesArr.push(cloneDeep(barSeriesItem),cloneDeep(lineSeriesItem)) | |
68 | + if (Array.isArray((newData as any)?.dimensions)) { | |
69 | + const seriesArr: typeof barSeriesItem[] = [] | |
70 | + // 对oldData进行判断,防止传入错误数据之后对旧维度判断产生干扰 | |
71 | + // 此处计算的是dimensions的Y轴维度,若是dimensions.length为0或1,则默认为1,排除X轴维度干扰 | |
72 | + const oldDimensions = | |
73 | + Array.isArray(oldData?.dimensions) && oldData.dimensions.length >= 1 ? oldData.dimensions.length : 1 | |
74 | + const newDimensions = (newData as any).dimensions.length >= 1 ? (newData as any).dimensions.length : 1 | |
75 | + const dimensionsGap = newDimensions - oldDimensions | |
76 | + if (dimensionsGap < 0) { | |
77 | + props.chartConfig.option.series.splice(newDimensions - 1) | |
78 | + } else if (dimensionsGap > 0) { | |
79 | + if (!oldData || !oldData?.dimensions || !Array.isArray(oldData?.dimensions) || !oldData?.dimensions.length) { | |
80 | + props.chartConfig.option.series = [] | |
81 | + } | |
82 | + for (let i = 0; i < dimensionsGap; i++) { | |
83 | + seriesArr.push(cloneDeep(barSeriesItem)) | |
84 | + } | |
85 | + props.chartConfig.option.series.push(...seriesArr) | |
72 | 86 | } |
73 | - useEchartsMapLegend(props.chartConfig, seriesArr) | |
87 | + useEchartsMapLegend(props.chartConfig, props.chartConfig.option.series) | |
74 | 88 | replaceMergeArr.value = ['series'] |
75 | - props.chartConfig.option.series = seriesArr | |
76 | 89 | nextTick(() => { |
77 | 90 | replaceMergeArr.value = [] |
78 | 91 | }) | ... | ... |
... | ... | @@ -240,6 +240,7 @@ const defaultTypeUpdate = (v: string) => { |
240 | 240 | } |
241 | 241 | |
242 | 242 | const shortCutSelect = (value: number) => { |
243 | + console.log(value) | |
243 | 244 | startTs.value = Date.now() - value |
244 | 245 | endTs.value = Date.now() |
245 | 246 | props.optionData.dataset = [startTs.value, endTs.value] as any | ... | ... |
src/packages/components/external/Informations/Mores/Camera/components/VideoPlay.vue
deleted
100644 → 0
1 | -<template> | |
2 | - <n-spin size="medium" :show="showLoading" | |
3 | - :style="{background: '#000',width: baseSize?.w + 'px', height: baseSize?.h + 'px' }"> | |
4 | - <template #description> 视频正在努力加载中...... </template> | |
5 | - <div> | |
6 | - <div ref="playerContainerElRef" class="go-content-box" | |
7 | - :style="{ width: baseSize?.w + 'px', height: baseSize?.h + 'px', background: '#000' }"> | |
8 | - | |
9 | - </div> | |
10 | - </div> | |
11 | - </n-spin> | |
12 | -</template> | |
13 | -<script setup lang="ts" name="VideoPlay"> | |
14 | -import { onMounted, ref, onUnmounted, watch, unref, PropType, shallowRef } from 'vue' | |
15 | -import videojs from 'video.js' | |
16 | -import 'videojs-flvjs-es6' | |
17 | -import type { VideoJsPlayerOptions } from 'video.js' | |
18 | -import 'video.js/dist/video-js.min.css' | |
19 | -import { getJwtToken, getShareJwtToken } from '@/utils/external/auth' | |
20 | -import { isShareMode } from '@/views/share/hook' | |
21 | -import { getOpenFlvPlayUrl, closeFlvPlay, getVideoControlStart } from '@/api/external/flvPlay' | |
22 | -import { useFingerprint } from '@/utils/external/useFingerprint' | |
23 | -import { GetResult } from '@fingerprintjs/fingerprintjs' | |
24 | -import { AccessModeEnum, VideoPlayerType } from '../config' | |
25 | -import { getVideoUrl } from '@/api/external/common' | |
26 | - | |
27 | -const props = defineProps({ | |
28 | - sourceSrc: { | |
29 | - type: String | |
30 | - }, | |
31 | - option: { | |
32 | - type: Object | |
33 | - }, | |
34 | - | |
35 | - baseSize: { | |
36 | - type: Object as PropType<{ w: number; h: number }> | |
37 | - }, | |
38 | - autoPlay: { | |
39 | - type: Boolean | |
40 | - }, | |
41 | - name: { | |
42 | - type: String | |
43 | - }, | |
44 | - avatar: { | |
45 | - type: String | |
46 | - }, | |
47 | - w: { | |
48 | - type: Number, | |
49 | - default: 300 | |
50 | - }, | |
51 | - h: { | |
52 | - type: Number, | |
53 | - default: 300 | |
54 | - }, | |
55 | - index: { | |
56 | - type: Number | |
57 | - } | |
58 | -}) | |
59 | - | |
60 | - | |
61 | -const { getResult } = useFingerprint() | |
62 | - | |
63 | -const showLoading = ref<boolean>(false) | |
64 | - | |
65 | -const sourceSrc = ref<string | null>(props.sourceSrc || '') | |
66 | - | |
67 | - | |
68 | -// video实例对象 | |
69 | -const videoPlayer = shallowRef<videojs.Player | null>(null) | |
70 | -const playerContainerElRef = shallowRef<HTMLElement | null>(null) | |
71 | - | |
72 | -const fingerprintResult = ref<Nullable<GetResult>>(null) | |
73 | - | |
74 | -const isRtspProtocol = (url: string) => { | |
75 | - const reg = /^rtsp:\/\//g | |
76 | - return reg.test(url) | |
77 | -} | |
78 | - | |
79 | -const getVideoTypeByUrl = (url = '') => { | |
80 | - if (!url) return; | |
81 | - try { | |
82 | - const { protocol, pathname } = new URL(url) | |
83 | - if (protocol.startsWith('rtsp:')) return VideoPlayerType.flv | |
84 | - const reg = /[^.]\w*$/ | |
85 | - const mathValue = pathname.match(reg) || [] | |
86 | - const ext = (mathValue[0] as keyof typeof VideoPlayerType) || 'webm' | |
87 | - const type = VideoPlayerType[ext] | |
88 | - return type ? type : VideoPlayerType.webm | |
89 | - } catch (error) { | |
90 | - console.error(error) | |
91 | - return VideoPlayerType.webm | |
92 | - } | |
93 | -} | |
94 | - | |
95 | - | |
96 | -//options配置 | |
97 | -const options: VideoJsPlayerOptions & Recordable = { | |
98 | - language: 'zh-CN', // 设置语言 | |
99 | - controls: true, // 是否显示控制条 | |
100 | - preload: 'auto', // 预加载 | |
101 | - autoplay: props.autoPlay ? true : false, // 是否自动播放 | |
102 | - fluid: false, // 自适应宽高 | |
103 | - poster: props?.avatar || '', | |
104 | - sources: [], | |
105 | - muted: props.autoPlay ? true : false, | |
106 | - userActions: { | |
107 | - hotkeys: true | |
108 | - }, | |
109 | - techOrder: ['html5', 'flvjs'], | |
110 | - flvjs: { | |
111 | - mediaDataSource: { | |
112 | - isLive: true, | |
113 | - cors: true, | |
114 | - withCredentials: false, | |
115 | - hasAudio: false | |
116 | - }, | |
117 | - config: { | |
118 | - autoCleanupSourceBuffer: true | |
119 | - } | |
120 | - } | |
121 | -} | |
122 | - | |
123 | - | |
124 | -async function getSource() { | |
125 | - fingerprintResult.value = await getResult() | |
126 | - let src = unref(sourceSrc) || '' | |
127 | - if (isRtspProtocol(unref(sourceSrc)!)) { | |
128 | - src = getOpenFlvPlayUrl(src, unref(fingerprintResult)?.visitorId || '') | |
129 | - } | |
130 | - return [ | |
131 | - { | |
132 | - type: getVideoTypeByUrl(src), | |
133 | - src | |
134 | - } | |
135 | - ] | |
136 | -} | |
137 | - | |
138 | -//针对萤石云或者海康威视,根据视频id获取播放流地址 | |
139 | -const getVideoUrlById = async (id: string) => { | |
140 | - const res = await getVideoUrl(id) | |
141 | - if (!res) return | |
142 | - const { url } = res.data | |
143 | - return url | |
144 | -} | |
145 | - | |
146 | -//针对gbt28181,根据设备id和通道号获取播放流地址 | |
147 | -const getVideoControlList = async (deviceId: string, channelId: string) => { | |
148 | - const { | |
149 | - data: { flv } | |
150 | - } = await getVideoControlStart({ | |
151 | - deviceId, | |
152 | - channelId | |
153 | - }) | |
154 | - return flv | |
155 | -} | |
156 | - | |
157 | -//针对自定义地址,直接获取地址 | |
158 | -const getCustomUrl = (url: string) => { | |
159 | - return url | |
160 | -} | |
161 | - | |
162 | - | |
163 | -function createVideoElement() { | |
164 | - if (!unref(playerContainerElRef)) return | |
165 | - unref(playerContainerElRef)!.innerHTML = '' | |
166 | - const video = document.createElement('video') | |
167 | - video.setAttribute('crossOrigin', 'anonymous') | |
168 | - video.style.setProperty('width', '100%') | |
169 | - video.style.setProperty('height', '100%') | |
170 | - video.classList.add('video-js', 'my-video', 'vjs-theme-city', 'vjs-big-play-centered') | |
171 | - unref(playerContainerElRef)?.appendChild(video) | |
172 | - return video | |
173 | -} | |
174 | - | |
175 | - | |
176 | -// 初始化videojs | |
177 | -const createVideoPlayer = async () => { | |
178 | - if (unref(videoPlayer)) dispose() | |
179 | - | |
180 | - options.sources = await getSource() | |
181 | - | |
182 | - if (isRtspProtocol(unref(sourceSrc) || '')) { | |
183 | - options.flvjs = { | |
184 | - ...(options.flvjs || {}), | |
185 | - config: { | |
186 | - headers: { | |
187 | - 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}` | |
188 | - } | |
189 | - } | |
190 | - } | |
191 | - } | |
192 | - const video = createVideoElement() | |
193 | - if (!video) return | |
194 | - | |
195 | - videoPlayer.value = videojs(video!, options) //fix 修复videojs解决直播延时的问题 | |
196 | - | |
197 | - videoPlayer.value?.on('timeupdate', function () { | |
198 | - // 计算表最新推流的时间和现在播放器播放推流的时间 | |
199 | - let differTime = | |
200 | - (unref(videoPlayer))!.buffered()?.end(0) - | |
201 | - (unref(videoPlayer))!.currentTime() // 差值小于1.5s时根据1倍速进行播放 | |
202 | - if (differTime < 1.5) { | |
203 | - videoPlayer.value?.playbackRate(1) | |
204 | - } // 差值大于1.5s小于10s根据1.2倍速进行播放 | |
205 | - if (differTime < 10 && differTime > 1.5) { | |
206 | - videoPlayer.value?.playbackRate(1.2) | |
207 | - } // 差值大于10s时进行重新加载直播流 | |
208 | - if (differTime > 10) { | |
209 | - createVideoPlayer() | |
210 | - } | |
211 | - }) | |
212 | -} | |
213 | - | |
214 | -const getVideosUrl = async () => { | |
215 | - try { | |
216 | - showLoading.value = true | |
217 | - videoPlayer.value?.src(''); | |
218 | - const { option } = props || {} | |
219 | - const { accessMode, id, channelId, deviceId, customUrl } = option || {} | |
220 | - if (accessMode === AccessModeEnum.Streaming) { | |
221 | - return await getVideoUrlById(id) | |
222 | - } else if (accessMode === AccessModeEnum.GBT28181) { | |
223 | - return await getVideoControlList(deviceId, channelId) | |
224 | - } else { | |
225 | - return await getCustomUrl(customUrl) | |
226 | - } | |
227 | - } finally { | |
228 | - showLoading.value = false | |
229 | - } | |
230 | -} | |
231 | - | |
232 | - | |
233 | -watch(() => props.option, async () => { | |
234 | - console.log(props, 'prop') | |
235 | - dispose() | |
236 | - sourceSrc.value = await getVideosUrl() | |
237 | - createVideoPlayer() | |
238 | -}) | |
239 | - | |
240 | - | |
241 | - | |
242 | -// 销毁 | |
243 | -function dispose() { | |
244 | - unref(videoPlayer)?.dispose() | |
245 | - videoPlayer.value = null | |
246 | -} | |
247 | - | |
248 | -watch( | |
249 | - () => props.autoPlay, | |
250 | - async (newData: boolean) => { | |
251 | - if (newData) { | |
252 | - handleVideoPlay() | |
253 | - } else { | |
254 | - videoPlayer.value?.pause() | |
255 | - } | |
256 | - } | |
257 | -) | |
258 | - | |
259 | -onMounted(async () => { | |
260 | - dispose() | |
261 | - sourceSrc.value = await getVideosUrl() | |
262 | - createVideoPlayer() | |
263 | -}) | |
264 | - | |
265 | -onUnmounted(() => { | |
266 | - if (props.sourceSrc) { | |
267 | - closeFlvPlay(props.sourceSrc, unref(fingerprintResult)!.visitorId!) | |
268 | - } | |
269 | - handleVideoDispose() | |
270 | -}) | |
271 | - | |
272 | -//播放 | |
273 | -const handleVideoPlay = () => videoPlayer.value?.play() | |
274 | - | |
275 | -//暂停和销毁 | |
276 | -const handleVideoDispose = () => videoPlayer.value?.dispose() && videoPlayer.value?.pause() | |
277 | - | |
278 | -</script> | |
279 | - | |
280 | -<style lang="scss" scoped> | |
281 | - | |
282 | -.go-content-box { | |
283 | - display: flex; | |
284 | - align-items: center; | |
285 | - justify-content: center; | |
286 | - | |
287 | - .my-video { | |
288 | - width: 100% !important; | |
289 | - height: 100% !important; | |
290 | - position: relative; | |
291 | - } | |
292 | -} | |
293 | -</style> | |
294 | -<style> | |
295 | -.vjs-poster { | |
296 | - background-size: 100% !important; | |
297 | -} | |
298 | -</style> |
1 | +<script lang="ts" setup> | |
2 | +import {XGPlayer} from '@/components/Video' | |
3 | +import {watch, ref} from "vue"; | |
4 | +import {StreamType} from "@/components/Video/src/types"; | |
5 | +import {getPlayUrl} from "@/packages/components/external/Informations/Mores/SingleCamera/config"; | |
6 | +import {CameraRecord} from "@/api/external/common/model"; | |
7 | + | |
8 | + | |
9 | + | |
10 | +const props = defineProps<{ | |
11 | + dataset?: any | |
12 | + width: number | |
13 | + height: number | |
14 | +}>() | |
15 | + | |
16 | +const loading = ref(false) | |
17 | +const playUrl = ref<string>() | |
18 | +const playType = ref<StreamType>() | |
19 | + | |
20 | +interface Dataset { | |
21 | + accessMode: number | |
22 | + autoPlay: boolean | |
23 | + channelId?: string | |
24 | + customUrl?: string | |
25 | + deviceId?: string | |
26 | + id?: string | |
27 | + playProtocol?: number | |
28 | +} | |
29 | + | |
30 | +const handleGetPlayUrl = async (dataset: Dataset) => { | |
31 | + try { | |
32 | + loading.value = true | |
33 | + const {id, channelId, deviceId, accessMode, customUrl, playProtocol} = dataset | |
34 | + if (!accessMode) return | |
35 | + const {type, url} = await getPlayUrl({ | |
36 | + id: id, | |
37 | + accessMode: accessMode, | |
38 | + playProtocol: playProtocol, | |
39 | + videoUrl: customUrl, | |
40 | + params: { | |
41 | + deviceId: deviceId, | |
42 | + channelNo: channelId, | |
43 | + }, | |
44 | + } as unknown as CameraRecord) || {} | |
45 | + playUrl.value = url | |
46 | + playType.value = type | |
47 | + } finally { | |
48 | + loading.value = false | |
49 | + } | |
50 | +} | |
51 | + | |
52 | +watch( | |
53 | + () => props.dataset, | |
54 | + (newData: Dataset) => { | |
55 | + handleGetPlayUrl(newData) | |
56 | + }, | |
57 | + { | |
58 | + immediate: true | |
59 | + } | |
60 | +) | |
61 | +</script> | |
62 | + | |
63 | +<template> | |
64 | + <NSpin :show="loading" class="player-spin"> | |
65 | + <XGPlayer | |
66 | + :url="playUrl" | |
67 | + :stream-type="playType" | |
68 | + :auto-play="dataset.autoPlay" | |
69 | + :config="{width: width, height: height, poster: dataset.avatar}" | |
70 | + /> | |
71 | + </NSpin> | |
72 | +</template> | |
73 | + | |
74 | +<style lang="scss" scoped> | |
75 | +.player-spin { | |
76 | + width: 100%; | |
77 | + height: 100%; | |
78 | + @include deep() { | |
79 | + .n-spin-content { | |
80 | + width: 100%; | |
81 | + height: 100%; | |
82 | + } | |
83 | + } | |
84 | +} | |
85 | + | |
86 | +</style> | ... | ... |
... | ... | @@ -120,15 +120,16 @@ const handleUpdateTreeValue = (e: string) => { |
120 | 120 | const getVideoLists = async (organizationId: string) => { |
121 | 121 | const res = await getVideoList({ organizationId }) |
122 | 122 | if (!res) return |
123 | - videoOptions.value = res?.data?.map((item: videoList) => ({ | |
123 | + videoOptions.value = res?.data?.map((item) => ({ | |
124 | 124 | label: item.name, |
125 | 125 | value: item.id, |
126 | 126 | id: item.id, |
127 | 127 | accessMode: item.accessMode, |
128 | 128 | customUrl: item.accessMode === AccessModeEnum.ManuallyEnter ? item.videoUrl: '', //参数只给自定义视频流使用 |
129 | 129 | channelId: item.accessMode === AccessModeEnum.GBT28181 ? item?.params?.channelNo : '', //参数只给gbt28181使用 |
130 | - deviceId: item.accessMode === AccessModeEnum.GBT28181 ? item?.params?.deviceId : '' //参数只给gbt28181使用 | |
131 | - })) | |
130 | + deviceId: item.accessMode === AccessModeEnum.GBT28181 ? item?.params?.deviceId : '', //参数只给gbt28181使用 | |
131 | + playProtocol: item?.playProtocol | |
132 | + } as any)) | |
132 | 133 | } |
133 | 134 | |
134 | 135 | //针对萤石云或者海康威视,根据视频id获取播放流地址 | ... | ... |
... | ... | @@ -2,17 +2,7 @@ |
2 | 2 | <div @mouseenter="handleMouseenter" @mouseleave="handleMouseleave" class="banner-box" ref="root"> |
3 | 3 | <div class="wrapper"> |
4 | 4 | <div v-for="(item, index) in option.dataset" :key="index" :class="item.className" :style="item.sty"> |
5 | - <VideoPlay | |
6 | - :autoPlay="item.autoPlay" | |
7 | - :option="item" | |
8 | - :name="item.name" | |
9 | - :avatar="item.avatar" | |
10 | - :key="item + index" | |
11 | - :sourceSrc="item.url" | |
12 | - :baseSize="{w,h}" | |
13 | - :index="index" | |
14 | - /> | |
15 | - <span class="video-title">{{ item.name }}</span> | |
5 | + <VideoPlayer :key="item + index" :dataset="item" :width="w" :height="h"/> | |
16 | 6 | </div> |
17 | 7 | </div> |
18 | 8 | <a v-show="isShowSvg" href="javascript:;" class="left" @click="changeSlide('left')"></a> |
... | ... | @@ -20,11 +10,10 @@ |
20 | 10 | </div> |
21 | 11 | </template> |
22 | 12 | <script setup lang="ts" name="index"> |
23 | -import { PropType, watch, toRefs, shallowReactive, onMounted, ref } from 'vue' | |
24 | -import { CreateComponentType } from '@/packages/index.d' | |
25 | -import 'video.js/dist/video-js.min.css' | |
26 | -import { option as typeOption } from './config' | |
27 | -import VideoPlay from './components' | |
13 | +import {PropType, watch, toRefs, shallowReactive, onMounted, ref} from 'vue' | |
14 | +import {CreateComponentType} from '@/packages/index.d' | |
15 | +import {option as typeOption} from './config' | |
16 | +import VideoPlayer from "@/packages/components/external/Informations/Mores/Camera/components/VideoPlayer.vue"; | |
28 | 17 | |
29 | 18 | const props = defineProps({ |
30 | 19 | chartConfig: { |
... | ... | @@ -35,9 +24,9 @@ const props = defineProps({ |
35 | 24 | |
36 | 25 | const isShowSvg = ref(false) |
37 | 26 | |
38 | -const { w, h } = toRefs(props.chartConfig.attr) | |
27 | +const {w, h} = toRefs(props.chartConfig.attr) | |
39 | 28 | |
40 | -const { autoSwitch, interval } = toRefs(props.chartConfig.option) | |
29 | +const {autoSwitch, interval} = toRefs(props.chartConfig.option) | |
41 | 30 | |
42 | 31 | //暂定 any类型 |
43 | 32 | const option = shallowReactive<{ ['dataset']: any }>({ |
... | ... | @@ -50,15 +39,15 @@ const computedFunc = (initial: number, source: Recordable[]) => { |
50 | 39 | if (initial < 0) initial = 0 |
51 | 40 | if (Array.isArray(source)) { |
52 | 41 | let len = source.length, |
53 | - temp1 = initial - 2 < 0 ? initial - 2 + len : initial - 2, | |
54 | - temp2 = initial - 1 < 0 ? initial - 1 + len : initial - 1, | |
55 | - temp3 = initial, | |
56 | - temp4 = initial + 1 >= len ? initial + 1 - len : initial + 1, | |
57 | - temp5 = initial + 2 >= len ? initial + 2 - len : initial + 2 | |
42 | + temp1 = initial - 2 < 0 ? initial - 2 + len : initial - 2, | |
43 | + temp2 = initial - 1 < 0 ? initial - 1 + len : initial - 1, | |
44 | + temp3 = initial, | |
45 | + temp4 = initial + 1 >= len ? initial + 1 - len : initial + 1, | |
46 | + temp5 = initial + 2 >= len ? initial + 2 - len : initial + 2 | |
58 | 47 | return source?.map((item: Recordable, index: number) => { |
59 | 48 | let transform = `translateX(-50%) scale(0.7)`, |
60 | - zIndex = 0, | |
61 | - className = 'slide' | |
49 | + zIndex = 0, | |
50 | + className = 'slide' | |
62 | 51 | switch (index) { |
63 | 52 | case temp3: |
64 | 53 | transform = `translateX(-50%) scale(1)` |
... | ... | @@ -93,23 +82,23 @@ const computedFunc = (initial: number, source: Recordable[]) => { |
93 | 82 | } |
94 | 83 | |
95 | 84 | watch( |
96 | - () => props.chartConfig.option.dataset, | |
97 | - newData => { | |
98 | - option.dataset = newData | |
99 | - }, | |
100 | - { | |
101 | - immediate: true, | |
102 | - deep: true | |
103 | - } | |
85 | + () => props.chartConfig.option.dataset, | |
86 | + newData => { | |
87 | + option.dataset = newData | |
88 | + }, | |
89 | + { | |
90 | + immediate: true, | |
91 | + deep: true | |
92 | + } | |
104 | 93 | ) |
105 | 94 | |
106 | 95 | option.dataset = computedFunc(initial.value, option.dataset) |
107 | 96 | |
108 | 97 | watch( |
109 | - () => initial.value, | |
110 | - newV => { | |
111 | - option.dataset = computedFunc(newV, option.dataset) | |
112 | - } | |
98 | + () => initial.value, | |
99 | + newV => { | |
100 | + option.dataset = computedFunc(newV, option.dataset) | |
101 | + } | |
113 | 102 | ) |
114 | 103 | |
115 | 104 | // 处理自动轮播 |
... | ... | @@ -155,17 +144,17 @@ function changeSlide(dir: string) { |
155 | 144 | } |
156 | 145 | |
157 | 146 | watch( |
158 | - () => autoSwitch.value, | |
159 | - (newV: boolean) => { | |
160 | - if (newV) { | |
161 | - autoPlay() | |
162 | - } else { | |
163 | - clearInterval(timer) | |
147 | + () => autoSwitch.value, | |
148 | + (newV: boolean) => { | |
149 | + if (newV) { | |
150 | + autoPlay() | |
151 | + } else { | |
152 | + clearInterval(timer) | |
153 | + } | |
154 | + }, | |
155 | + { | |
156 | + immediate: true | |
164 | 157 | } |
165 | - }, | |
166 | - { | |
167 | - immediate: true | |
168 | - } | |
169 | 158 | ) |
170 | 159 | |
171 | 160 | const handleMouseenter = () => { |
... | ... | @@ -181,6 +170,7 @@ const handleMouseleave = () => (isShowSvg.value = false) |
181 | 170 | height: 100%; |
182 | 171 | display: flex; |
183 | 172 | overflow: hidden; |
173 | + | |
184 | 174 | .slide { |
185 | 175 | width: 20%; |
186 | 176 | height: 100%; |
... | ... | @@ -200,6 +190,7 @@ const handleMouseleave = () => (isShowSvg.value = false) |
200 | 190 | } |
201 | 191 | } |
202 | 192 | } |
193 | + | |
203 | 194 | .arrow { |
204 | 195 | position: absolute; |
205 | 196 | top: 50%; |
... | ... | @@ -211,11 +202,13 @@ const handleMouseleave = () => (isShowSvg.value = false) |
211 | 202 | background-color: white; |
212 | 203 | opacity: 0.5; |
213 | 204 | } |
205 | + | |
214 | 206 | a.left { |
215 | 207 | @extend .arrow; |
216 | 208 | background-image: url('./static/left.svg'); |
217 | 209 | left: 0px; |
218 | 210 | } |
211 | + | |
219 | 212 | a.right { |
220 | 213 | @extend .arrow; |
221 | 214 | background-image: url('./static/right.svg'); | ... | ... |
src/packages/components/external/Informations/Mores/SingleCamera/components/VideoPlay.vue
deleted
100644 → 0
1 | -<template> | |
2 | - <div ref="playerContainerElRef" class="go-content-box" | |
3 | - :style="{ width: baseSize?.w + 'px', height: baseSize?.h + 'px', background: '#000' }"> | |
4 | - | |
5 | - </div> | |
6 | -</template> | |
7 | -<script setup lang="ts"> | |
8 | -import { ref, onUnmounted, watch, unref, PropType, shallowRef } from 'vue' | |
9 | -import videojs from 'video.js' | |
10 | -import 'videojs-flvjs-es6' | |
11 | -import type { VideoJsPlayerOptions } from 'video.js' | |
12 | -import 'video.js/dist/video-js.min.css' | |
13 | -import { getJwtToken, getShareJwtToken } from '@/utils/external/auth' | |
14 | -import { isShareMode } from '@/views/share/hook' | |
15 | -import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay' | |
16 | -import { useFingerprint } from '@/utils/external/useFingerprint' | |
17 | -import { GetResult } from '@fingerprintjs/fingerprintjs' | |
18 | -import { VideoPlayerTypeEnum } from '../config' | |
19 | - | |
20 | -const props = defineProps({ | |
21 | - sourceSrc: { | |
22 | - type: String | |
23 | - }, | |
24 | - autoPlay: { | |
25 | - type: Boolean | |
26 | - }, | |
27 | - name: { | |
28 | - type: String | |
29 | - }, | |
30 | - avatar: { | |
31 | - type: String | |
32 | - }, | |
33 | - baseSize: { | |
34 | - type: Object as PropType<{ w: number; h: number }> | |
35 | - } | |
36 | -}) | |
37 | - | |
38 | -const isRtspProtocol = (url: string) => { | |
39 | - const reg = /^rtsp:\/\//g | |
40 | - return reg.test(url) | |
41 | -} | |
42 | - | |
43 | -const getVideoTypeByUrl = (url = '') => { | |
44 | - if (!url) return; | |
45 | - try { | |
46 | - const { protocol, pathname } = new URL(url) | |
47 | - if (protocol.startsWith('rtsp:')) return VideoPlayerTypeEnum.flv | |
48 | - const reg = /[^.]\w*$/ | |
49 | - const mathValue = pathname.match(reg) || [] | |
50 | - const ext = (mathValue[0] as keyof typeof VideoPlayerTypeEnum) || 'webm' | |
51 | - const type = VideoPlayerTypeEnum[ext] | |
52 | - return type ? type : VideoPlayerTypeEnum.webm | |
53 | - } catch (error) { | |
54 | - console.error(error) | |
55 | - return VideoPlayerTypeEnum.webm | |
56 | - } | |
57 | -} | |
58 | - | |
59 | -// video实例对象 | |
60 | -const videoPlayer = shallowRef<videojs.Player | null>(null) | |
61 | -const playerContainerElRef = shallowRef<HTMLElement | null>(null) | |
62 | - | |
63 | -const fingerprintResult = ref<Nullable<GetResult>>(null) | |
64 | - | |
65 | -//options配置 | |
66 | -const options: VideoJsPlayerOptions & Recordable = { | |
67 | - language: 'zh-CN', // 设置语言 | |
68 | - controls: true, // 是否显示控制条 | |
69 | - autoplay: !!props.autoPlay, // 是否自动播放 | |
70 | - fluid: false, // 自适应宽高 | |
71 | - poster: props?.avatar || '', | |
72 | - sources: [], | |
73 | - muted: !!props.autoPlay, | |
74 | - userActions: { | |
75 | - hotkeys: true | |
76 | - }, | |
77 | - techOrder: ['html5', 'flvjs'], | |
78 | - flvjs: { | |
79 | - mediaDataSource: { | |
80 | - isLive: true, | |
81 | - cors: true, | |
82 | - withCredentials: false, | |
83 | - hasAudio: false | |
84 | - }, | |
85 | - config: { | |
86 | - autoCleanupSourceBuffer: true | |
87 | - } | |
88 | - } | |
89 | -} | |
90 | - | |
91 | -const { getResult } = useFingerprint() | |
92 | - | |
93 | -async function getSource() { | |
94 | - fingerprintResult.value = await getResult() | |
95 | - let src = props.sourceSrc || '' | |
96 | - if (isRtspProtocol(props.sourceSrc!)) { | |
97 | - src = getOpenFlvPlayUrl(src, unref(fingerprintResult)?.visitorId || '') | |
98 | - } | |
99 | - return [ | |
100 | - { | |
101 | - type: getVideoTypeByUrl(props.sourceSrc), | |
102 | - src | |
103 | - } | |
104 | - ] | |
105 | -} | |
106 | - | |
107 | -function createVideoElement() { | |
108 | - if (!unref(playerContainerElRef)) return | |
109 | - unref(playerContainerElRef)!.innerHTML = '' | |
110 | - const video = document.createElement('video') | |
111 | - video.setAttribute('crossOrigin', 'anonymous') | |
112 | - video.style.setProperty('width', '100%') | |
113 | - video.style.setProperty('height', '100%') | |
114 | - video.classList.add('video-js', 'my-video', 'vjs-theme-city', 'vjs-big-play-centered') | |
115 | - unref(playerContainerElRef)?.appendChild(video) | |
116 | - return video | |
117 | -} | |
118 | - | |
119 | - | |
120 | -// 初始化videojs | |
121 | -const createVideoPlayer = async () => { | |
122 | - if (unref(videoPlayer)) dispose() | |
123 | - | |
124 | - options.sources = await getSource() | |
125 | - | |
126 | - if (isRtspProtocol(props.sourceSrc || '')) { | |
127 | - options.flvjs = { | |
128 | - ...(options.flvjs || {}), | |
129 | - config: { | |
130 | - headers: { | |
131 | - 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}` | |
132 | - } | |
133 | - } | |
134 | - } | |
135 | - } | |
136 | - const video = createVideoElement() | |
137 | - if (!video) return | |
138 | - | |
139 | - videoPlayer.value = videojs(video!, options) //fix 修复videojs解决直播延时的问题 | |
140 | - | |
141 | - videoPlayer.value?.on('timeupdate', function () { | |
142 | - // 计算表最新推流的时间和现在播放器播放推流的时间 | |
143 | - let differTime = | |
144 | - (unref(videoPlayer))!.buffered()?.end(0) - | |
145 | - (unref(videoPlayer))!.currentTime() // 差值小于1.5s时根据1倍速进行播放 | |
146 | - if (differTime < 1.5) { | |
147 | - videoPlayer.value?.playbackRate(1) | |
148 | - } // 差值大于1.5s小于10s根据1.2倍速进行播放 | |
149 | - if (differTime < 10 && differTime > 1.5) { | |
150 | - videoPlayer.value?.playbackRate(1.2) | |
151 | - } // 差值大于10s时进行重新加载直播流 | |
152 | - if (differTime > 10) { | |
153 | - createVideoPlayer() | |
154 | - } | |
155 | - }) | |
156 | -} | |
157 | - | |
158 | -// watch( | |
159 | -// () => props.sourceSrc, | |
160 | -// async () => { | |
161 | -// (props as any ).sourceSrc = '' | |
162 | -// if(!props.sourceSrc) return; | |
163 | -// await nextTick(); | |
164 | -// if(unref(fingerprintResult)!.visitorId!) { | |
165 | -// closeFlvPlay(props.sourceSrc!, unref(fingerprintResult)!.visitorId!) | |
166 | -// } | |
167 | -// videoPlayer.value?.src(''); | |
168 | -// createVideoPlayer() | |
169 | - | |
170 | -// if (props.sourceSrc) { | |
171 | -// reloadSource({ src: props.sourceSrc, type: getVideoTypeByUrl(props.sourceSrc) }) | |
172 | -// } | |
173 | -// } | |
174 | -// ) | |
175 | - | |
176 | -watch( | |
177 | - () => props.autoPlay, | |
178 | - async (newData: boolean) => { | |
179 | - if (newData) { | |
180 | - handleVideoPlay() | |
181 | - } else { | |
182 | - videoPlayer.value?.pause() | |
183 | - } | |
184 | - }, | |
185 | -) | |
186 | - | |
187 | -// onMounted(() => { | |
188 | -// createVideoPlayer() | |
189 | -// }) | |
190 | - | |
191 | -onUnmounted(() => { | |
192 | - if (props.sourceSrc) { | |
193 | - closeFlvPlay(props.sourceSrc, unref(fingerprintResult)!.visitorId!) | |
194 | - } | |
195 | - handleVideoDispose() | |
196 | -}) | |
197 | - | |
198 | - | |
199 | -function anewInit() { | |
200 | - createVideoPlayer() | |
201 | -} | |
202 | - | |
203 | -function reloadSource(sources: string | videojs.Tech.SourceObject | videojs.Tech.SourceObject[]) { | |
204 | - if (!unref(videoPlayer)) return | |
205 | - unref(videoPlayer)?.pause() | |
206 | - unref(videoPlayer)?.src(sources) | |
207 | - unref(videoPlayer)?.load() | |
208 | - unref(videoPlayer)?.play() | |
209 | -} | |
210 | - | |
211 | -function dispose() { | |
212 | - unref(videoPlayer)?.dispose() | |
213 | - videoPlayer.value = null | |
214 | -} | |
215 | - | |
216 | -//播放 | |
217 | -const handleVideoPlay = () => videoPlayer.value?.play() | |
218 | - | |
219 | -//暂停和销毁 | |
220 | -const handleVideoDispose = () => videoPlayer.value?.dispose() | |
221 | - | |
222 | -defineExpose({ | |
223 | - dispose, | |
224 | - reloadSource, | |
225 | - anewInit, | |
226 | -}) | |
227 | - | |
228 | -</script> | |
229 | - | |
230 | -<style lang="scss" scoped> | |
231 | -.go-content-box { | |
232 | - display: flex; | |
233 | - align-items: center; | |
234 | - justify-content: center; | |
235 | - | |
236 | - .my-video { | |
237 | - width: 100% !important; | |
238 | - height: 100% !important; | |
239 | - position: relative; | |
240 | - } | |
241 | -} | |
242 | -</style> | |
243 | -<style> | |
244 | -.vjs-poster { | |
245 | - background-size: 100% !important; | |
246 | -} | |
247 | -</style> |
... | ... | @@ -2,6 +2,11 @@ import { PublicConfigClass } from '@/packages/public' |
2 | 2 | import { CreateComponentType } from '@/packages/index.d' |
3 | 3 | import { SingleCameraConfig } from './index' |
4 | 4 | import cloneDeep from 'lodash/cloneDeep' |
5 | +import {StreamType} from "@/components/Video/src/types"; | |
6 | +import {useFingerprint} from "@/utils/external/useFingerprint"; | |
7 | +import {getOpenFlvPlayUrl, getVideoControlStart} from "@/api/external/flvPlay"; | |
8 | +import {getVideoUrl} from "@/api/external/common"; | |
9 | +import {CameraRecord} from "@/api/external/common/model"; | |
5 | 10 | |
6 | 11 | export enum sourceTypeEnum { |
7 | 12 | CUSTOM = 'custom', |
... | ... | @@ -13,6 +18,11 @@ export enum sourceTypeNameEnum { |
13 | 18 | PLATFORM = '平台获取' |
14 | 19 | } |
15 | 20 | |
21 | +export enum FluoriteMideaProtocolEnum { | |
22 | + HLS = 2, | |
23 | + FLV = 4, | |
24 | +} | |
25 | + | |
16 | 26 | export enum VideoPlayerTypeEnum { |
17 | 27 | m3u8 = 'application/x-mpegURL', |
18 | 28 | mp4 = 'video/mp4', |
... | ... | @@ -32,6 +42,7 @@ export interface videoListInterface { |
32 | 42 | deviceId: string |
33 | 43 | customUrl: string |
34 | 44 | params: GBT28181Params |
45 | + playProtocol?: number | |
35 | 46 | } |
36 | 47 | |
37 | 48 | export interface GBT28181Params { |
... | ... | @@ -53,6 +64,7 @@ export interface Dataset { |
53 | 64 | deviceId?: string |
54 | 65 | id?: string |
55 | 66 | value?: string |
67 | + playProtocol?: number | |
56 | 68 | } |
57 | 69 | |
58 | 70 | export const option = { |
... | ... | @@ -70,3 +82,42 @@ export default class Config extends PublicConfigClass implements CreateComponent |
70 | 82 | public chartConfig = cloneDeep(SingleCameraConfig) |
71 | 83 | public option = cloneDeep(option) |
72 | 84 | } |
85 | + | |
86 | +export const isRtspProtocol = (url: string) => { | |
87 | + const reg = /^rtsp:\/\//g; | |
88 | + return reg.test(url); | |
89 | +}; | |
90 | + | |
91 | +export async function getPlayUrl( | |
92 | + params: CameraRecord | |
93 | +): Promise<{ url: string; type: StreamType } | undefined> { | |
94 | + const { accessMode } = params; | |
95 | + if (accessMode === AccessMode.ManuallyEnter) { | |
96 | + const { videoUrl } = params; | |
97 | + if (params.videoUrl) { | |
98 | + const isRTSPPlay = isRtspProtocol(videoUrl); | |
99 | + | |
100 | + if (isRTSPPlay) { | |
101 | + const { getResult } = useFingerprint(); | |
102 | + const fingerprint = await getResult(); | |
103 | + return { url: getOpenFlvPlayUrl(videoUrl, fingerprint.visitorId), type: 'flv' }; | |
104 | + } else { | |
105 | + return { url: videoUrl, type: 'auto' }; | |
106 | + } | |
107 | + } | |
108 | + } else if (accessMode === AccessMode.GBT28181) { | |
109 | + const { deviceId, channelNo } = params?.params || {}; | |
110 | + const result = await getVideoControlStart({ channelId: channelNo!, deviceId: deviceId! }); | |
111 | + return { url: result.data.flv, type: 'flv' }; | |
112 | + } else { | |
113 | + const { id, playProtocol } = params; | |
114 | + const result = await getVideoUrl(id); | |
115 | + const type: StreamType = | |
116 | + playProtocol === FluoriteMideaProtocolEnum.FLV | |
117 | + ? 'flv' | |
118 | + : playProtocol === FluoriteMideaProtocolEnum.HLS | |
119 | + ? 'hls' | |
120 | + : 'auto'; | |
121 | + return { url: result.data.url, type }; | |
122 | + } | |
123 | +} | ... | ... |
... | ... | @@ -97,7 +97,7 @@ const handleUpdateTreeValue = (value: string) => { |
97 | 97 | const getVideoLists = async (organizationId: string) => { |
98 | 98 | const res = await getVideoList({ organizationId }) |
99 | 99 | if (!res) return |
100 | - videoOptions.value = res?.data?.map((item: videoListInterface) => { | |
100 | + videoOptions.value = res?.data?.map((item) => { | |
101 | 101 | return { |
102 | 102 | label: item.name, |
103 | 103 | value: item.id, |
... | ... | @@ -105,8 +105,9 @@ const getVideoLists = async (organizationId: string) => { |
105 | 105 | id: item.id, |
106 | 106 | accessMode: item.accessMode, |
107 | 107 | channelId: item.accessMode === AccessMode.GBT28181 ? item?.params?.channelNo : '', //参数只给gbt28181使用 |
108 | - deviceId: item.accessMode === AccessMode.GBT28181 ? item?.params?.deviceId : '' //参数只给gbt28181使用 | |
109 | - } | |
108 | + deviceId: item.accessMode === AccessMode.GBT28181 ? item?.params?.deviceId : '', //参数只给gbt28181使用 | |
109 | + playProtocol: item?.playProtocol | |
110 | + } as any | |
110 | 111 | }) |
111 | 112 | } |
112 | 113 | ... | ... |
1 | 1 | <template> |
2 | 2 | <div> |
3 | - <n-spin size="medium" :show="showLoading" :content-style="{ background: 'red' }"> | |
4 | - <template #description> 视频正在努力加载中...... </template> | |
5 | - <div> | |
6 | - <VideoPlay ref="videoPlayerRef" :baseSize="{ w, h }" :sourceSrc="sourceUrl" :autoPlay="option.autoplay" | |
7 | - :avatar="option.poster" /> | |
8 | - </div> | |
3 | + <n-spin size="medium" :show="showLoading" class="player-spin"> | |
4 | + <XGPlayer :url="sourceUrl" :stream-type="playType" | |
5 | + :config="{width: '100%', height: '100%', poster: option.poster}" | |
6 | + :auto-play="option.autoplay"/> | |
9 | 7 | </n-spin> |
10 | 8 | </div> |
11 | 9 | </template> |
12 | 10 | <script setup lang="ts"> |
13 | -import { PropType, toRefs, shallowReactive, watch, ref } from 'vue' | |
14 | -import { CreateComponentType } from '@/packages/index.d' | |
15 | -import { AccessMode, Dataset, option as configOption, sourceTypeEnum } from './config' | |
16 | -import { VideoPlay } from './components' | |
17 | -import { getVideoControlStart } from '@/api/external/flvPlay' | |
18 | -import { getVideoUrl } from '@/api/external/common' | |
11 | +import {PropType, toRefs, shallowReactive, watch, ref} from 'vue' | |
12 | +import {CreateComponentType} from '@/packages/index.d' | |
13 | +import {Dataset, getPlayUrl, option as configOption} from './config' | |
14 | +import {XGPlayer} from '@/components/Video' | |
15 | +import {StreamType} from "@/components/Video/src/types"; | |
16 | +import {CameraRecord} from "@/api/external/common/model"; | |
19 | 17 | |
20 | 18 | const props = defineProps({ |
21 | 19 | chartConfig: { |
... | ... | @@ -26,9 +24,9 @@ const props = defineProps({ |
26 | 24 | |
27 | 25 | const showLoading = ref(false) |
28 | 26 | |
29 | -const { w, h } = toRefs(props.chartConfig.attr) | |
27 | +const {w, h} = toRefs(props.chartConfig.attr) | |
30 | 28 | |
31 | -const { autoplay, dataset, poster, customVideoUrl } = toRefs(props.chartConfig.option as typeof configOption) | |
29 | +const {autoplay, dataset, poster, customVideoUrl} = toRefs(props.chartConfig.option as typeof configOption) | |
32 | 30 | |
33 | 31 | const option = shallowReactive({ |
34 | 32 | dataset: configOption.dataset, |
... | ... | @@ -37,87 +35,47 @@ const option = shallowReactive({ |
37 | 35 | }) |
38 | 36 | |
39 | 37 | const sourceUrl = ref('') |
40 | -const videoPlayerRef = ref<InstanceType<typeof VideoPlay> | null>() | |
41 | - | |
42 | -// 针对萤石云或者海康威视,根据视频id获取播放流地址 | |
43 | -const getVideoUrlById = async (id: string) => { | |
44 | - const res = await getVideoUrl(id) | |
45 | - if (!res) return | |
46 | - const { url } = res.data | |
47 | - return url | |
48 | -} | |
49 | - | |
50 | -// 针对gbt28181,根据设备id和通道号获取播放流地址 | |
51 | -const getVideoControlList = async (deviceId: string, channelId: string) => { | |
52 | - const { | |
53 | - data: { flv } | |
54 | - } = await getVideoControlStart({ | |
55 | - deviceId, | |
56 | - channelId | |
57 | - }) | |
58 | - return flv | |
59 | -} | |
60 | - | |
61 | -// 针对自定义地址,直接获取地址 | |
62 | -const getCustomUrl = (url: string) => { | |
63 | - return url | |
64 | -} | |
38 | +const playType = ref<StreamType>() | |
65 | 39 | |
66 | 40 | |
67 | 41 | async function getPlaySource(params: Dataset) { |
68 | - try { | |
69 | - showLoading.value = true | |
70 | - | |
71 | - const { accessMode, id, deviceId, channelId, customUrl } = params | |
72 | - if (accessMode === AccessMode.Streaming && id) { | |
73 | - return await getVideoUrlById(id!) | |
74 | - } else if (accessMode === AccessMode.GBT28181 && deviceId && channelId) { | |
75 | - return await getVideoControlList(deviceId!, channelId!) | |
76 | - } else { | |
77 | - return getCustomUrl(customUrl!) | |
78 | - } | |
79 | - } finally { | |
80 | - showLoading.value = false | |
81 | - } | |
42 | + const {id, channelId, deviceId, accessMode, customUrl, playProtocol} = params | |
43 | + if (!accessMode) return | |
44 | + const {type, url} = await getPlayUrl({ | |
45 | + id: id, | |
46 | + accessMode: accessMode, | |
47 | + playProtocol: playProtocol, | |
48 | + videoUrl: customUrl, | |
49 | + params: { | |
50 | + deviceId: deviceId, | |
51 | + channelNo: channelId, | |
52 | + }, | |
53 | + } as unknown as CameraRecord) || {} | |
54 | + sourceUrl.value = url! | |
55 | + playType.value = type | |
82 | 56 | } |
83 | 57 | |
84 | 58 | watch( |
85 | - () => dataset?.value, | |
86 | - async (newData) => { | |
87 | - videoPlayerRef.value?.dispose() | |
88 | - sourceUrl.value = await getPlaySource(newData) | |
89 | - videoPlayerRef.value?.anewInit() | |
90 | - }, | |
91 | - { | |
92 | - immediate: true | |
93 | - } | |
94 | -) | |
95 | - | |
96 | -watch( | |
97 | - () => customVideoUrl.value, | |
98 | - (newData) => { | |
99 | - if(newData){ | |
100 | - videoPlayerRef.value?.dispose() | |
101 | - sourceUrl.value = newData; | |
102 | - videoPlayerRef.value?.anewInit() | |
59 | + () => dataset?.value, | |
60 | + async (newData) => { | |
61 | + getPlaySource(newData) | |
62 | + }, | |
63 | + { | |
64 | + immediate: true | |
103 | 65 | } |
104 | - }, | |
105 | - { | |
106 | - immediate: true | |
107 | - } | |
108 | 66 | ) |
109 | 67 | |
110 | -watch( | |
111 | - () => [poster.value, autoplay.value], | |
112 | - newData => { | |
113 | - option.poster = newData.at(-2) as string | |
114 | - option.autoplay = newData.at(-1) as boolean | |
115 | - }, | |
116 | - { | |
117 | - immediate: true | |
118 | - } | |
119 | -) | |
120 | 68 | </script> |
121 | 69 | |
122 | 70 | <style lang="scss" scoped> |
71 | +.player-spin { | |
72 | + width: 100%; | |
73 | + height: 100%; | |
74 | + @include deep() { | |
75 | + .n-spin-content { | |
76 | + width: 100%; | |
77 | + height: 100%; | |
78 | + } | |
79 | + } | |
80 | +} | |
123 | 81 | </style> | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | -import { NFormItem, NGi, NGrid, NSelect, NDatePicker, NInputNumber } from 'naive-ui'; | |
3 | -import { defaultIntervalOptions, aggergationOptions } from '../DynamicForm/timeInterval' | |
4 | -import { unref, computed, ref, watch } from 'vue'; | |
5 | -import { isObject } from '@/utils/external/is'; | |
2 | +import {NFormItem, NGi, NGrid, NSelect, NDatePicker, NInputNumber} from 'naive-ui'; | |
3 | +import {defaultIntervalOptions, aggergationOptions} from '../DynamicForm/timeInterval' | |
4 | +import {unref, computed, ref, watch} from 'vue'; | |
5 | +import {isObject} from '@/utils/external/is'; | |
6 | +import dayjs from "dayjs"; | |
6 | 7 | |
7 | 8 | interface Value { |
8 | 9 | agg?: Nullable<string> |
... | ... | @@ -13,12 +14,12 @@ interface Value { |
13 | 14 | } |
14 | 15 | |
15 | 16 | const props = withDefaults( |
16 | - defineProps<{ | |
17 | - value?: Value | |
18 | - }>(), | |
19 | - { | |
20 | - value: () => ({}) | |
21 | - } | |
17 | + defineProps<{ | |
18 | + value?: Value | |
19 | + }>(), | |
20 | + { | |
21 | + value: () => ({}) | |
22 | + } | |
22 | 23 | ) |
23 | 24 | |
24 | 25 | const emit = defineEmits<{ |
... | ... | @@ -31,6 +32,26 @@ const timePeriod = ref<Nullable<[number, number]>>(null) |
31 | 32 | const agg = ref() |
32 | 33 | const interval = ref() |
33 | 34 | const limit = ref(7) |
35 | +const rangeShortcuts = { | |
36 | + 昨天: () => { | |
37 | + return [ | |
38 | + dayjs().startOf('day').valueOf(), | |
39 | + dayjs().endOf('day').valueOf() | |
40 | + ] as const | |
41 | + }, | |
42 | + 最近7天: () => { | |
43 | + return [ | |
44 | + dayjs().subtract(6, 'day').startOf('day').valueOf(), | |
45 | + dayjs().endOf('day').valueOf() | |
46 | + ] as const | |
47 | + }, | |
48 | + 最近30天: () => { | |
49 | + return [ | |
50 | + dayjs().subtract(29, 'day').startOf('day').valueOf(), | |
51 | + dayjs().endOf('day').valueOf() | |
52 | + ] as const | |
53 | + } | |
54 | +} | |
34 | 55 | |
35 | 56 | const getRangeOptions = (number: number) => { |
36 | 57 | for (let i = 0; i < defaultIntervalOptions.length; i++) { |
... | ... | @@ -55,32 +76,32 @@ const getIntervalTimeOptions = computed(() => { |
55 | 76 | |
56 | 77 | const handleTimePerionChange = (value: number[]) => { |
57 | 78 | const [startTs, endTs] = value || [] |
58 | - emit('update:value', { ...props.value, startTs, endTs, interval: null }) | |
59 | - emit('change', { ...props.value || {}, startTs, endTs, interval: null }) | |
79 | + emit('update:value', {...props.value, startTs, endTs, interval: null}) | |
80 | + emit('change', {...props.value || {}, startTs, endTs, interval: null}) | |
60 | 81 | } |
61 | 82 | |
62 | 83 | const handleAggChange = (value: string) => { |
63 | - const _value = { ...props.value, agg: value, ...(value === 'NONE' ? { limit: 7 } : {}) } | |
84 | + const _value = {...props.value, agg: value, ...(value === 'NONE' ? {limit: 7} : {})} | |
64 | 85 | Reflect.deleteProperty(_value, value === 'NONE' ? 'interval' : 'limit') |
65 | 86 | emit('update:value', _value) |
66 | 87 | emit('change', _value) |
67 | 88 | } |
68 | 89 | |
69 | 90 | const handleIntervalChange = (value: number) => { |
70 | - const _value = { ...props.value, interval: value } | |
91 | + const _value = {...props.value, interval: value} | |
71 | 92 | emit('update:value', _value) |
72 | 93 | emit('change', _value) |
73 | 94 | } |
74 | 95 | |
75 | 96 | const handleLimitChange = (value: Nullable<number>) => { |
76 | - const _value = { ...props.value, limit: value } | |
97 | + const _value = {...props.value, limit: value} | |
77 | 98 | emit('update:value', _value) |
78 | 99 | emit('change', _value) |
79 | 100 | } |
80 | 101 | |
81 | 102 | watch(() => props.value, (target) => { |
82 | 103 | if (target && isObject(target)) { |
83 | - const { agg: _agg, interval: _interval, startTs, endTs, limit: _limit } = target || {} | |
104 | + const {agg: _agg, interval: _interval, startTs, endTs, limit: _limit} = target || {} | |
84 | 105 | if (startTs && endTs) { |
85 | 106 | timePeriod.value = [startTs!, endTs!] |
86 | 107 | } else { |
... | ... | @@ -97,26 +118,29 @@ watch(() => props.value, (target) => { |
97 | 118 | <NGrid :cols="24"> |
98 | 119 | <NGi :span="16"> |
99 | 120 | <NFormItem :show-label="false"> |
100 | - <NDatePicker v-model:value="timePeriod" type="datetimerange" placeholder="请选择时间范围" | |
101 | - @update-value="handleTimePerionChange" clearable :default-time="['00:00:00', '23:59:59']"></NDatePicker> | |
121 | + <NDatePicker :shortcuts="rangeShortcuts" v-model:value="timePeriod" type="datetimerange" | |
122 | + placeholder="请选择时间范围" | |
123 | + @update-value="handleTimePerionChange" clearable | |
124 | + :default-time="['00:00:00', '23:59:59']"></NDatePicker> | |
102 | 125 | </NFormItem> |
103 | 126 | </NGi> |
104 | 127 | <NGi :span="4"> |
105 | 128 | <NFormItem :show-label="false"> |
106 | 129 | <NSelect v-model:value="agg" @update:value="handleAggChange" :options="aggergationOptions" label-field="name" |
107 | - value-field="id" placeholder="聚合方式" clearable></NSelect> | |
130 | + value-field="id" placeholder="聚合方式" clearable></NSelect> | |
108 | 131 | </NFormItem> |
109 | 132 | </NGi> |
110 | 133 | <NGi v-if="!getShowLimit" :span="4"> |
111 | 134 | <NFormItem :show-label="false"> |
112 | 135 | <NSelect v-model:value="interval" @update:value="handleIntervalChange" :options="getIntervalTimeOptions" |
113 | - label-field="name" value-field="id" placeholder="间隔时间" clearable></NSelect> | |
136 | + label-field="name" value-field="id" placeholder="间隔时间" clearable></NSelect> | |
114 | 137 | </NFormItem> |
115 | 138 | </NGi> |
116 | 139 | <NGi v-if="getShowLimit" :span="4"> |
117 | 140 | <NFormItem :show-label="false"> |
118 | 141 | <NInputNumber v-model:value="limit" :default-value="7" @update:value="handleLimitChange" |
119 | - :parse="(input: string) => parseInt(input)" :min="7" :max="50000" :step="1" placeholder="请输入最大条数" /> | |
142 | + :parse="(input: string) => parseInt(input)" :min="7" :max="50000" :step="1" | |
143 | + placeholder="请输入最大条数"/> | |
120 | 144 | </NFormItem> |
121 | 145 | </NGi> |
122 | 146 | </NGrid> | ... | ... |
... | ... | @@ -109,6 +109,7 @@ const getSharePageContentData = async () => { |
109 | 109 | // chartEditStore.requestGlobalConfig = requestGlobalConfig |
110 | 110 | // chartEditStore.componentList = componentList |
111 | 111 | chartEditStore.setPageConfig(content.pageConfig) |
112 | + chartEditStore.setCurrentPageSelectId(content.pageConfig.pageList[0].id) | |
112 | 113 | // handleRegisterComponent(componentList) |
113 | 114 | content.pageConfig.pageList.forEach(pageItem=>{ |
114 | 115 | handleRegisterComponent(pageItem.componentList) | ... | ... |