Compare commits

...

4 Commits

Author SHA1 Message Date
阿斌
8a4beed0c7
Pre Merge pull request !36 from 阿斌/N/A 2025-06-06 01:59:42 +00:00
lin
bae2b2524b 移除过时的web_src 2025-06-06 09:59:25 +08:00
lin
8d97ee613e 修复移动位置订阅无法关闭的BUG 2025-06-06 09:26:36 +08:00
阿斌
da98101aac
update src/main/resources/civilCode.csv.
行政规划错误。江苏南通海门市,修改为海门区,浙江杭州删除下城区、江干区,新增钱塘区,临平区

Signed-off-by: 阿斌 <38912748@qq.com>
2024-12-15 08:58:42 +00:00
160 changed files with 20 additions and 40285 deletions

View File

@ -518,8 +518,8 @@ public class DeviceServiceImpl implements IDeviceService, CommandLineRunner {
log.info("[目录订阅]成功: {}", device.getDeviceId());
if (!subscribeTaskRunner.containsKey(SubscribeTaskForCatalog.getKey(device))) {
SIPResponse response = (SIPResponse) event.getResponse();
SipTransactionInfo transactionInfoForResonse = new SipTransactionInfo(response);
SubscribeTask subscribeTask = SubscribeTaskForCatalog.getInstance(device, this::catalogSubscribeExpire, transactionInfoForResonse);
SipTransactionInfo transactionInfoForResponse = new SipTransactionInfo(response);
SubscribeTask subscribeTask = SubscribeTaskForCatalog.getInstance(device, this::catalogSubscribeExpire, transactionInfoForResponse);
if (subscribeTask != null) {
subscribeTaskRunner.addSubscribe(subscribeTask);
}
@ -582,13 +582,13 @@ public class DeviceServiceImpl implements IDeviceService, CommandLineRunner {
log.info("[移动位置订阅]成功: {}", device.getDeviceId());
if (!subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) {
SIPResponse response = (SIPResponse) event.getResponse();
SipTransactionInfo transactionInfoForResonse = new SipTransactionInfo(response);
SubscribeTask subscribeTask = SubscribeTaskForMobilPosition.getInstance(device, this::catalogSubscribeExpire, transactionInfoForResonse);
SipTransactionInfo transactionInfoForResponse = new SipTransactionInfo(response);
SubscribeTask subscribeTask = SubscribeTaskForMobilPosition.getInstance(device, this::mobilPositionSubscribeExpire, transactionInfoForResponse);
if (subscribeTask != null) {
subscribeTaskRunner.addSubscribe(subscribeTask);
}
}else {
subscribeTaskRunner.updateDelay(SubscribeTaskForMobilPosition.getKey(device), (device.getSubscribeCycleForCatalog() * 1000L - 500L) + System.currentTimeMillis());
subscribeTaskRunner.updateDelay(SubscribeTaskForMobilPosition.getKey(device), (device.getSubscribeCycleForMobilePosition() * 1000L - 500L) + System.currentTimeMillis());
}
},eventResult -> {
@ -874,7 +874,16 @@ public class DeviceServiceImpl implements IDeviceService, CommandLineRunner {
public void subscribeMobilePosition(int id, int cycle, int interval) {
Device device = deviceMapper.query(id);
Assert.notNull(device, "未找到设备");
Assert.isTrue(device.isOnLine(), "设备已离线");
if (!device.isOnLine()) {
// 开启订阅
device.setSubscribeCycleForMobilePosition(cycle);
device.setMobilePositionSubmissionInterval(interval);
updateDevice(device);
if (subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) {
subscribeTaskRunner.removeSubscribe(SubscribeTaskForMobilPosition.getKey(device));
}
throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备已离线");
}
if (device.getSubscribeCycleForMobilePosition() == cycle) {
return;
@ -890,6 +899,7 @@ public class DeviceServiceImpl implements IDeviceService, CommandLineRunner {
// 开启订阅
device.setSubscribeCycleForMobilePosition(cycle);
device.setMobilePositionSubmissionInterval(interval);
updateDevice(device);
if (cycle > 0) {
addMobilePositionSubscribe(device, null);
}

View File

@ -12,7 +12,7 @@ public class SubscribeTaskForMobilPosition extends SubscribeTask {
public static final String name = "mobilPosition";
public static SubscribeTask getInstance(Device device, SubscribeCallback callback, SipTransactionInfo transactionInfo) {
if (device.getSubscribeCycleForCatalog() <= 0) {
if (device.getSubscribeCycleForMobilePosition() <= 0) {
return null;
}
SubscribeTaskForMobilPosition subscribeTaskForMobilPosition = new SubscribeTaskForMobilPosition();

View File

@ -861,7 +861,7 @@
320623,如东县,3206
320681,启东市,3206
320682,如皋市,3206
320684,海门,3206
320684,海门,3206
320685,海安市,3206
3207,连云港市,32
320703,连云区,3207
@ -918,8 +918,6 @@
33,浙江省,
3301,杭州市,33
330102,上城区,3301
330103,下城区,3301
330104,江干区,3301
330105,拱墅区,3301
330106,西湖区,3301
330108,滨江区,3301
@ -927,6 +925,8 @@
330110,余杭区,3301
330111,富阳区,3301
330112,临安区,3301
330113,临平区,3301
330114,钱塘区,3301
330122,桐庐县,3301
330127,淳安县,3301
330182,建德市,3301

1 编号 名称 上级
861 320623 如东县 3206
862 320681 启东市 3206
863 320682 如皋市 3206
864 320684 海门市 海门区 3206
865 320685 海安市 3206
866 3207 连云港市 32
867 320703 连云区 3207
918 33 浙江省
919 3301 杭州市 33
920 330102 上城区 3301
330103 下城区 3301
330104 江干区 3301
921 330105 拱墅区 3301
922 330106 西湖区 3301
923 330108 滨江区 3301
925 330110 余杭区 3301
926 330111 富阳区 3301
927 330112 临安区 3301
928 330113 临平区 3301
929 330114 钱塘区 3301
930 330122 桐庐县 3301
931 330127 淳安县 3301
932 330182 建德市 3301

View File

@ -1,12 +0,0 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}

View File

@ -1,9 +0,0 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

14
web_src/.gitignore vendored
View File

@ -1,14 +0,0 @@
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

View File

@ -1,14 +0,0 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {},
'postcss-pxtorem': {
rootValue: 16,
propList: ['font-size'] // 只转化font-size
}
}
}

View File

@ -1,21 +0,0 @@
# gb_web
> A Vue.js project
## Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
```
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).

View File

@ -1,41 +0,0 @@
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})

View File

@ -1,54 +0,0 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,101 +0,0 @@
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}

View File

@ -1,22 +0,0 @@
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}

View File

@ -1,83 +0,0 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'@static': resolve('static'),
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

View File

@ -1,99 +0,0 @@
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: config.dev.host,
port: config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
]),
new CopyWebpackPlugin([
{ from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'},
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'},
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: config.build.assetsSubDirectory + '/js/'},
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})

View File

@ -1,150 +0,0 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
]),
new CopyWebpackPlugin([
{ from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'},
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'},
{ from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: config.build.assetsSubDirectory + '/js/'},
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

View File

@ -1,8 +0,0 @@
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_API: '"/debug"'
})

View File

@ -1,87 +0,0 @@
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/debug': {
target: 'http://127.0.0.1:18080',
changeOrigin: true,
pathRewrite: {
'^/debug': '/'
}
},
'/static/snap': {
target: 'http://127.0.0.1:18080',
changeOrigin: true,
// pathRewrite: {
// '^/static/snap': '/static/snap'
// }
},
},
// Various Dev Server settings
host:"127.0.0.1",
useLocalIp: false, // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
hot: true,//自动保存
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../../src/main/resources/static/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../../src/main/resources/static/'),
assetsSubDirectory: './static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

View File

@ -1,4 +0,0 @@
'use strict'
module.exports = {
NODE_ENV: '"production"'
}

View File

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>国标28181</title>
<link rel="shortcut icon" href="static/favicon.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="./static/css/iconfont.css">
<link rel="stylesheet" type="text/css" href="./static/css/login.css">
</head>
<body>
<script type="text/javascript" src="./static/js/jessibuca/jessibuca.js"></script>
<script type="text/javascript" src="./static/js/EasyWasmPlayer.js"></script>
<script type="text/javascript" src="./static/js/liveplayer-lib.min.js"></script>
<script type="text/javascript" src="./static/js/ZLMRTCClient.js"></script>
<script type="text/javascript" src="./static/js/config.js"></script>
<script type="text/javascript" src="./static/js/jquery-3.7.1.min.js"></script>
<script type="text/javascript" src="./static/js/h265web/h265webjs-v20221106.js"></script>
<script type="text/javascript" src="./static/js/h265web/missile.js"></script>
<div id="app"></div>
</body>
</html>

View File

@ -1,84 +0,0 @@
{
"name": "gb_web",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "648540858 <648540858@qq.com>",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
},
"dependencies": {
"@babel/runtime": "^7.3.0",
"@femessage/log-viewer": "^1.5.0",
"@liveqing/liveplayer": "^2.7.10",
"@wchbrad/vue-easy-tree": "^1.0.12",
"axios": "^0.24.0",
"byte-weektime-picker": "^1.1.1",
"core-js": "^2.6.5",
"echarts": "^4.9.0",
"element-ui": "^2.15.14",
"fingerprintjs2": "^2.1.2",
"moment": "^2.29.1",
"ol": "^6.14.1",
"postcss-pxtorem": "^5.1.1",
"screenfull": "5.1.0",
"strip-ansi": "^7.1.0",
"uuid": "^8.3.2",
"v-charts": "^1.19.0",
"vue": "^2.6.11",
"vue-clipboard2": "^0.3.1",
"vue-clipboards": "^1.3.0",
"vue-contextmenujs": "^1.3.13",
"vue-cookies": "^1.8.3",
"vue-router": "^3.1.6",
"vue-ztree-2.0": "^1.0.4"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.6.0",
"css-loader": "^0.28.11",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.8.5",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

View File

@ -1,98 +0,0 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
import userService from './components/service/UserService'
export default {
name: 'app',
data(){
return {
isLogin: false,
excludeLoginCheck: ["/play/wasm", "/play/rtc"],
userInfo: { //
nick: null,
ulevel: null,
uid: null,
portrait: null
}
}
},
created() {
if (userService.getToken() == null){
console.log(22222)
console.log(this.$route.path)
try {
if (this.excludeLoginCheck && this.excludeLoginCheck.length > 0) {
for (let i = 0; i < this.excludeLoginCheck.length; i++) {
if (this.$route.path.startsWith(this.excludeLoginCheck[i])){
return;
}
}
}
}catch (e) {
console.error(e)
}
//
this.$router.push('/login');
}
},
mounted(){
//
// this.getUserInfo();
},
methods: {
},
components: {}
};
</script>
<style>
html,
body,
#app {
margin: 0 0;
background-color: #e9eef3;
height: 100%;
}
.el-header,
.el-footer {
/* background-color: #b3c0d1; */
color: #333;
text-align: center;
line-height: 60px;
}
.el-main {
background-color: #f0f2f5;
color: #333;
text-align: center;
padding: 0 20px;
}
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/*定义滚动条轨道 内阴影+圆角*/
::-webkit-scrollbar-track {
border-radius: 4px;
background-color: #F5F5F5;
}
/*定义滑块 内阴影+圆角*/
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: #c8c8c8;
box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
}
.table-header {
color: #727272;
font-weight: 600;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,35 +0,0 @@
<template>
<div id="ChannelEdit" v-loading="locading" style="width: 100%">
<div class="page-header">
<div class="page-title">
<el-page-header @back="close" content="编辑通道"></el-page-header>
</div>
<div class="page-header-btn">
<div style="display: inline;">
<el-button icon="el-icon-close" size="mini" style="font-size: 20px; color: #000;" type="text" @click="close" ></el-button>
</div>
</div>
</div>
<CommonChannelEdit ref="commonChannelEdit" :id="id" :saveSuccess="close" :cancel="close"></CommonChannelEdit>
</div>
</template>
<script>
import CommonChannelEdit from './common/CommonChannelEdit'
export default {
name: "channelEdit",
props: [ 'id', 'closeEdit'],
components: {
CommonChannelEdit,
},
data() {
return {};
},
methods: {
close: function () {
this.closeEdit()
},
},
};
</script>

View File

@ -1,307 +0,0 @@
<template>
<div id="app" style="width: 100%">
<div class="page-header">
<div class="page-title">
<div >云端录像</div>
</div>
<div class="page-header-btn">
搜索:
<el-input @input="initData" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="search" clearable></el-input>
开始时间:
<el-date-picker
v-model="startTime"
type="datetime"
size="mini"
value-format="yyyy-MM-dd HH:mm:ss"
@change="initData"
placeholder="选择日期时间">
</el-date-picker>
结束时间:
<el-date-picker
v-model="endTime"
type="datetime"
size="mini"
value-format="yyyy-MM-dd HH:mm:ss"
@change="initData"
placeholder="选择日期时间">
</el-date-picker>
节点选择:
<el-select size="mini" @change="initData" style="width: 16rem; margin-right: 1rem;"
v-model="mediaServerId" placeholder="请选择" >
<el-option label="全部" value=""></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
<!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord()">批量删除</el-button>-->
<el-button icon="el-icon-refresh-right" circle size="mini" :loading="loading"
@click="initData()"></el-button>
</div>
</div>
<!--设备列表-->
<el-table size="medium" :data="recordList" style="width: 100%" :height="$tableHeght">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column prop="app" label="应用名">
</el-table-column>
<el-table-column prop="stream" label="流ID" width="380">
</el-table-column>
<el-table-column label="开始时间">
<template v-slot:default="scope">
{{formatTimeStamp(scope.row.startTime)}}
</template>
</el-table-column>
<el-table-column label="结束时间">
<template v-slot:default="scope">
{{formatTimeStamp(scope.row.endTime)}}
</template>
</el-table-column>
<el-table-column label="时长">
<template v-slot:default="scope">
<el-tag v-if="Vue.prototype.$myServerId !== scope.row.serverId" style="border-color: #ecf1af">{{formatTime(scope.row.timeLen)}}</el-tag>
<el-tag v-if="Vue.prototype.$myServerId === scope.row.serverId">{{formatTime(scope.row.timeLen)}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="fileName" label="文件名称">
</el-table-column>
<el-table-column prop="mediaServerId" label="流媒体">
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template v-slot:default="scope">
<el-button size="medium" icon="el-icon-video-play" type="text" @click="play(scope.row)">播放
</el-button>
<el-button size="medium" icon="el-icon-download" type="text" @click="downloadFile(scope.row)">下载
</el-button>
<!-- <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c"-->
<!-- @click="deleteRecord(scope.row)">删除-->
<!-- </el-button>-->
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
<el-dialog
:title="playerTitle"
:visible.sync="showPlayer"
width="50%">
<easyPlayer ref="recordVideoPlayer" :videoUrl="videoUrl" :height="false" ></easyPlayer>
</el-dialog>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import MediaServer from './service/MediaServer'
import easyPlayer from './common/easyPlayer.vue'
import moment from 'moment'
import Vue from "vue";
export default {
name: 'app',
components: {
uiHeader,easyPlayer
},
data() {
return {
search: '',
startTime: '',
endTime: '',
showPlayer: false,
playerTitle: '',
videoUrl: '',
playerStyle: {
"margin": "auto",
"margin-bottom": "20px",
"width": window.innerWidth/2 + "px",
"height": this.winHeight/2 + "px",
},
mediaServerList: [], //
mediaServerId: "", //
mediaServerPath: null, //
recordList: [], //
chooseRecord: null, //
updateLooper: 0, //
winHeight: window.innerHeight - 250,
currentPage: 1,
count: 15,
total: 0,
loading: false,
mediaServerObj: new MediaServer(),
};
},
computed: {
Vue() {
return Vue
},
},
mounted() {
this.initData();
this.getMediaServerList();
},
destroyed() {
this.$destroy('recordVideoPlayer');
},
methods: {
initData: function () {
this.getRecordList();
},
currentChange: function (val) {
this.currentPage = val;
this.getRecordList();
},
handleSizeChange: function (val) {
this.count = val;
this.getRecordList();
},
getMediaServerList: function () {
let that = this;
that.mediaServerObj.getOnlineMediaServerList((data) => {
that.mediaServerList = data.data;
})
},
setMediaServerPath: function (serverId) {
let that = this;
let i;
for (i = 0; i < that.mediaServerList.length; i++) {
if (serverId === that.mediaServerList[i].id) {
break;
}
}
let port = that.mediaServerList[i].httpPort;
if (location.protocol === "https:" && that.mediaServerList[i].httpSSlPort) {
port = that.mediaServerList[i].httpSSlPort
}
that.mediaServerPath = location.protocol + "//" + that.mediaServerList[i].streamIp + ":" + port
console.log(that.mediaServerPath)
},
getRecordList: function () {
this.$axios({
method: 'get',
url: `/api/cloud/record/list`,
params: {
app: '',
stream: '',
query: this.search,
startTime: this.startTime,
endTime: this.endTime,
mediaServerId: this.mediaServerId,
page: this.currentPage,
count: this.count
}
}).then((res) => {
console.log(res)
if (res.data.code === 0) {
this.total = res.data.data.total;
this.recordList = res.data.data.list;
}
this.loading = false;
}).catch((error) => {
console.log(error);
this.loading = false;
});
},
play(row) {
console.log(row)
this.chooseRecord = row;
this.showPlayer = true;
this.$axios({
method: 'get',
url: `/api/cloud/record/play/path`,
params: {
recordId: row.id,
}
}).then((res) => {
console.log(res)
if (res.data.code === 0) {
if (location.protocol === "https:") {
this.videoUrl = res.data.data.httpsPath;
}else {
this.videoUrl = res.data.data.httpPath;
}
console.log(222 )
console.log(this.videoUrl )
}
}).catch((error) => {
console.log(error);
});
},
downloadFile(file){
console.log(file)
this.$axios({
method: 'get',
url: `/api/cloud/record/play/path`,
params: {
recordId: file.id,
}
}).then((res) => {
console.log(res)
const link = document.createElement('a');
link.target = "_blank";
if (res.data.code === 0) {
if (location.protocol === "https:") {
link.href = res.data.data.httpsPath + "&save_name=" + file.fileName;
}else {
link.href = res.data.data.httpPath + "&save_name=" + file.fileName;
}
link.click();
}
}).catch((error) => {
console.log(error);
});
},
deleteRecord() {
// TODO
let that = this;
this.$axios({
method: 'delete',
url: `/record_proxy/api/record/delete`,
params: {
page: that.currentPage,
count: that.count
}
}).then(function (res) {
console.log(res)
if (res.data.code === 0) {
that.total = res.data.data.total;
that.recordList = res.data.data.list;
}
}).catch(function (error) {
console.log(error);
});
},
formatTime(time) {
const h = parseInt(time / 3600 / 1000)
const minute = parseInt((time - h * 3600 * 1000) / 60 / 1000)
let second = Math.ceil((time - h * 3600 * 1000 - minute * 60 * 1000) / 1000)
if (second < 0) {
second = 0;
}
return (h > 0 ? h + `小时` : '') + (minute > 0 ? minute + '分' : '') + (second > 0 ? second + '秒' : '')
},
formatTimeStamp(time) {
return moment.unix(time/1000).format('yyyy-MM-DD HH:mm:ss')
}
}
};
</script>
<style>
</style>

View File

@ -1,710 +0,0 @@
<template>
<div id="recordDetail" style="width: 100%">
<div class="page-header" style="margin-bottom: 0">
<div class="page-title">
<el-page-header @back="backToList" content="云端录像"></el-page-header>
</div>
<div class="page-header-btn" v-if="!this.$route.params.mediaServerId" style="padding-right: 1rem">
<!-- 节点选择:-->
<!-- <el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" >-->
<!-- <el-option-->
<!-- key="undefined"-->
<!-- label="全部"-->
<!-- value="undefined">-->
<!-- </el-option>-->
<!-- <el-option-->
<!-- v-for="item in mediaServerList"-->
<!-- :key="item"-->
<!-- :label="item"-->
<!-- :value="item">-->
<!-- </el-option>-->
<!-- </el-select>-->
<b>节点</b> {{ mediaServerId }}
</div>
<div v-if="this.$route.params.mediaServerId" style="margin-right: 1rem;">
<span>流媒体{{ this.$route.params.mediaServerId }}</span>
</div>
</div>
<el-container>
<el-aside width="260px">
<div class="record-list-box-box">
<div style="margin-top: 20px">
<el-date-picker size="mini" v-model="chooseDate" :picker-options="pickerOptions" type="date"
value-format="yyyy-MM-dd" placeholder="日期" @change="dateChange()"></el-date-picker>
<!-- <el-button :disabled="!mediaServerId" size="mini" type="primary" icon="fa fa-cloud-download" style="margin: auto; margin-left: 12px " title="裁剪合并" @click="drawerOpen"></el-button>-->
</div>
<div class="record-list-box" :style="recordListStyle">
<ul v-if="detailFiles.length >0" class="infinite-list record-list" v-infinite-scroll="infiniteScroll" >
<li v-for="(item,index) in detailFiles" :key="index" class="infinite-list-item record-list-item" >
<el-tag v-if="choosedFile !== item.fileName" @click="chooseFile(item)">
<i class="el-icon-video-camera" ></i>
{{ getFileShowName(item) }}
</el-tag>
<el-tag type="danger" v-if="choosedFile === item.fileName">
<i class="el-icon-video-camera" ></i>
{{ getFileShowName(item) }}
</el-tag>
<a class="el-icon-download" @click="downloadFile(item)" style="color: #409EFF;font-weight: 600;margin-left: 10px;"
target="_blank"/>
</li>
</ul>
</div>
<div v-if="detailFiles.length === 0" class="record-list-no-val">暂无数据</div>
</div>
</el-aside>
<el-main style="padding: 22px">
<div class="playBox" :style="playerStyle">
<player ref="recordVideoPlayer" :videoUrl="videoUrl" :height="true" style="width: 100%" ></player>
</div>
<div class="player-option-box" >
<el-slider
class="playtime-slider"
v-model="playTime"
id="playtimeSlider"
:disabled="detailFiles.length === 0"
:min="sliderMIn"
:max="sliderMax"
:format-tooltip="playTimeFormat"
@change="playTimeChange"
:marks="playTimeSliderMarks">
</el-slider>
<div class="slider-val-box">
<div class="slider-val" v-for="(item,index) of detailFiles" :key="index" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div>
</div>
</div>
</el-main>
</el-container>
<el-drawer
title="录像下载"
:visible.sync="drawer"
:direction="direction"
:before-close="drawerClose">
<div class="drawer-box">
<el-button icon="el-icon-plus" size="mini" type="primary" @click="addTask"></el-button>
<el-tabs type="border-card" style="height: 100%" v-model="tabVal" @tab-click="tabClick">
<el-tab-pane name="running">
<span slot="label"><i class="el-icon-scissors"></i>进行中</span>
<ul class="task-list">
<li class="task-list-item" v-for="(item,index) in taskListForRuning" :key="index">
<div class="task-list-item-box">
<span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
<el-progress :percentage="(parseFloat(item.percentage)*100).toFixed(1)"></el-progress>
</div>
</li>
</ul>
</el-tab-pane>
<el-tab-pane name="ended">
<span slot="label"><i class="el-icon-finished"></i>已完成</span>
<ul class="task-list">
<li class="task-list-item" v-for="(item, index) in taskListEnded" :key="index">
<div class="task-list-item-box" style="height: 2rem;line-height: 2rem;">
<span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
<a class="el-icon-download download-btn" :href="getFileBasePath() + '/download.html?url=download/' "
target="_blank">
</a>
</div>
</li>
</ul>
</el-tab-pane>
</el-tabs>
</div>
</el-drawer>
<el-dialog title="选择时间段" :visible.sync="showTaskBox">
<el-date-picker
type="datetimerange"
v-model="taskTimeRange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="HH:mm:ss"
placeholder="选择时间范围">
</el-date-picker>
<el-button size="mini" type="primary" @click="addTaskToServer">确认</el-button>
</el-dialog>
</div>
</template>
<script>
// TODO
import uiHeader from '../layout/UiHeader.vue'
import player from './common/easyPlayer.vue'
import moment from 'moment'
import axios from "axios";
export default {
name: 'app',
components: {
uiHeader, player
},
// props: [ 'mediaServerId',],
data() {
return {
app: this.$route.params.app,
stream: this.$route.params.stream,
mediaServerId: this.$route.params.mediaServerId,
dateFilesObj: [],
mediaServerList: [],
detailFiles: [],
loading: false,
chooseDate: null,
videoUrl: null,
choosedFile: null,
queryDate: new Date(),
currentPage: 1,
count: 1000000, // TODO
total: 0,
direction: "ltr",
drawer: false,
showTaskBox: false,
taskTimeRange: [],
taskListEnded: [],
taskListForRuning: [],
sliderMIn: 0,
sliderMax: 86400,
autoPlay: true,
taskUpdate: null,
tabVal: "running",
recordListStyle: {
height: this.winHeight + "px",
overflow: "auto",
margin: "10px auto 10px auto"
},
playerStyle: {
"margin": "auto",
"margin-bottom": "20px",
"height": this.winHeight + "px",
},
timeFormat:'00:00:00',
winHeight: window.innerHeight - 240,
playTime: 0,
playTimeSliderMarks: {
0: "00:00",
3600: "01:00",
7200: "02:00",
10800: "03:00",
14400: "04:00",
18000: "05:00",
21600: "06:00",
25200: "07:00",
28800: "08:00",
32400: "09:00",
36000: "10:00",
39600: "11:00",
43200: "12:00",
46800: "13:00",
50400: "14:00",
54000: "15:00",
57600: "16:00",
61200: "17:00",
64800: "18:00",
68400: "19:00",
72000: "20:00",
75600: "21:00",
79200: "22:00",
82800: "23:00",
86400: "24:00",
},
pickerOptions:{
cellClassName:(date) =>{
//
let time = moment(date).format('YYYY-MM-DD')
if (this.dateFilesObj[time]){
return "data-picker-true"
}else {
return "data-picker-false"
}
}
}
};
},
computed: {
},
mounted() {
this.recordListStyle.height = this.winHeight + "px";
this.playerStyle["height"] = this.winHeight + "px";
//
this.getDateInYear(()=>{
if (Object.values(this.dateFilesObj).length > 0){
this.chooseDate = Object.values(this.dateFilesObj)[Object.values(this.dateFilesObj).length -1];
this.dateChange();
}
})
},
destroyed() {
this.$destroy('recordVideoPlayer');
},
methods: {
dateChange(){
this.playTime = 0;
this.detailFiles = [];
this.currentPage = 1;
this.sliderMIn= 0;
this.sliderMax= 86400;
let chooseFullDate = new Date(this.chooseDate +" " + this.timeFormat);
if (chooseFullDate.getFullYear() !== this.queryDate.getFullYear()
|| chooseFullDate.getMonth() !== this.queryDate.getMonth()){
this.queryDate = chooseFullDate;
this.getDateInYear()
}
this.queryRecordDetails(()=>{
if (this.detailFiles.length > 0){
console.log(this.detailFiles)
let timeForFile = this.getTimeForFile(this.detailFiles[0]);
let lastTimeForFile = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1]);
let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
console.log(timeNum)
let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
this.playTime = parseInt(timeNum/1000)
this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60))
console.log(this.sliderMIn )
this.sliderMax = parseInt(lastTimeNum/1000 - lastTimeNum/1000%(60*60)) + 60*60
console.log(this.sliderMax )
}
});
},
infiniteScroll(){
if (this.total > this.detailFiles.length) {
this.currentPage ++;
this.queryRecordDetails();
}
},
queryRecordDetails: function (callback){
this.$axios({
method: 'get',
url: `/api/cloud/record/list`,
params: {
app: this.app,
stream: this.stream,
startTime: this.chooseDate + " 00:00:00",
endTime: this.chooseDate + " 23:59:59",
page: this.currentPage,
count: this.count,
mediaServerId: this.mediaServerId
}
}).then((res) => {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.detailFiles = this.detailFiles.concat(res.data.data.list);
let temp = new Set()
for (let i = 0; i < this.detailFiles.length; i++) {
temp.add(this.detailFiles[i].mediaServerId)
}
this.mediaServerList = Array.from(temp)
if (this.mediaServerList.length === 1) {
this.mediaServerId = this.mediaServerList[0]
}
}
this.loading = false;
if (callback) callback();
}).catch((error) => {
console.log(error);
this.loading = false;
});
},
chooseFile(file){
console.log(file)
if (file == null) {
this.videoUrl = "";
this.choosedFile = "";
}else {
this.choosedFile = file.fileName;
this.$axios({
method: 'get',
url: `/api/cloud/record/play/path`,
params: {
recordId: file.id,
}
}).then((res) => {
console.log(res)
if (res.data.code === 0) {
if (location.protocol === "https:") {
this.videoUrl = res.data.data.httpsPath;
}else {
this.videoUrl = res.data.data.httpPath;
}
}
}).catch((error) => {
console.log(error);
});
}
},
downloadFile(file){
console.log(file)
this.$axios({
method: 'get',
url: `/api/cloud/record/play/path`,
params: {
recordId: file.id,
}
}).then((res) => {
console.log(res)
const link = document.createElement('a');
link.target = "_blank";
if (res.data.code === 0) {
if (location.protocol === "https:") {
link.href = res.data.data.httpsPath + "&save_name=" + file.fileName;
}else {
link.href = res.data.data.httpPath + "&save_name=" + file.fileName;
}
link.click();
}
}).catch((error) => {
console.log(error);
});
},
backToList() {
this.$router.back()
},
getFileShowName(item) {
return moment(item.startTime).format('HH:mm:ss') + "-" + moment(item.endTime).format('HH:mm:ss')
},
chooseMediaChange() {
},
getRecordList() {
},
getFileBasePath(item) {
let basePath = ""
if (axios.defaults.baseURL.startsWith("http")) {
basePath = `${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
}else {
basePath = `${window.location.origin}${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
}
return basePath;
},
getDataWidth(item){
let timeForFile = this.getTimeForFile(item);
let result = (timeForFile[2])/((this.sliderMax - this.sliderMIn)*1000)
return result*100
},
getDataLeft(item){
let timeForFile = this.getTimeForFile(item);
let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100 ;
},
playTimeChange(val){
let minTime = this.getTimeForFile(this.detailFiles[0])[0]
let maxTime = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1])[1];
this.chooseFile(null);
let timeMilli = new Date(this.chooseDate + " " + this.timeFormat).getTime() + val*1000
if (timeMilli >= minTime.getTime() && timeMilli <= maxTime.getTime()){
for (let i = 0; i < this.detailFiles.length; i++) {
let timeForFile = this.getTimeForFile(this.detailFiles[i]);
if (timeMilli >= timeForFile[0].getTime() && timeMilli <= timeForFile[1].getTime()){
// TODO
this.chooseFile(this.detailFiles[i])
return;
}
}
}
},
getTimeForFile(file){
console.log(file)
let starTime = new Date(file.startTime * 1000);
let endTime = new Date(file.endTime * 1000);
if(this.checkIsOver24h(starTime,endTime)){
endTime = new Date(this.chooseDate + " " + "23:59:59");
}
return [starTime, endTime, endTime.getTime() - starTime.getTime()];
},
checkIsOver24h(starTime,endTime){
return starTime > endTime;
},
playTimeFormat(val){
let h = parseInt(val/3600);
let m = parseInt((val - h*3600)/60);
let s = parseInt(val - h*3600 - m*60);
let hStr = h;
let mStr = m;
let sStr = s;
if (h < 10) {
hStr = "0" + hStr;
}
if (m < 10) {
mStr = "0" + mStr;s
}
if (s < 10) {
sStr = "0" + sStr;
}
return hStr + ":" + mStr + ":" + sStr
},
deleteRecord(){
// TODO
let that = this;
this.$axios({
method: 'delete',
url:`/record_proxy/${that.mediaServerId}/api/record/delete`,
params: {
page: that.currentPage,
count: that.count
}
}).then(function (res) {
if (res.data.code === 0) {
that.total = res.data.data.total;
that.recordList = res.data.data.list;
}
}).catch(function (error) {
console.log(error);
});
},
getDateInYear(callback){
this.dateFilesObj = {};
this.$axios({
method: 'get',
url: `/api/cloud/record/date/list`,
params: {
app: this.app,
stream: this.stream,
year: this.queryDate.getFullYear(),
month: this.queryDate.getMonth() + 1,
mediaServerId: this.mediaServerId,
}
}).then((res) => {
console.log(res)
if (res.data.code === 0) {
if (res.data.data.length > 0) {
for (let i = 0; i < res.data.data.length; i++) {
this.dateFilesObj[res.data.data[i]] = res.data.data[i]
}
console.log(this.dateFilesObj)
}
}
if(callback)callback();
}).catch((error) => {
console.log(error);
});
},
tabClick(){
this.getTaskList(this.tabVal === "ended")
},
drawerClose(){
this.drawer = false;
if (this.taskUpdate != null) {
window.clearInterval(this.taskUpdate)
}
},
drawerOpen(){
this.drawer = true;
if (this.taskUpdate != null) {
window.clearInterval(this.taskUpdate)
}
this.taskUpdate = setInterval(()=>{
this.getTaskList(this.tabVal === "ended")
}, 1000)
},
addTask(){
this.showTaskBox = true;
let startTimeStr = this.chooseDate + " " + this.detailFiles[0].fileName.substring(0, 8);
let endTimeStr = this.chooseDate + " " + this.detailFiles[this.detailFiles.length - 1].fileName.substring(9, 17);
this.taskTimeRange[0] = new Date(startTimeStr)
this.taskTimeRange[1] = new Date(endTimeStr)
},
addTaskToServer(){
let that = this;
this.$axios({
method: 'get',
url:`/api/cloud/record/task/add`,
params: {
app: this.app,
stream: this.stream,
mediaServerId: this.mediaServerId,
startTime: moment(this.taskTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
endTime: moment(this.taskTimeRange[1]).format('YYYY-MM-DD HH:mm:ss'),
}
}).then(function (res) {
if (res.data.code === 0 ) {
that.showTaskBox = false
that.getTaskList(false);
}else {
that.$message.error(res.data.msg);
}
}).catch(function (error) {
console.log(error);
});
},
handleTabClick() {
this.getTaskList(this.tabVal === "ended")
},
getTaskList(isEnd){
let that = this;
this.$axios({
method: 'get',
url:`/api/cloud/record/task/list`,
params: {
mediaServerId: this.mediaServerId,
isEnd: isEnd,
}
}).then(function (res) {
if (res.data.code === 0) {
if (isEnd){
that.taskListEnded = res.data.data;
}else {
that.taskListForRuning = res.data.data;
}
}
}).catch(function (error) {
console.log(error);
});
},
goBack(){
this.$router.push('/cloudRecord');
}
}
};
</script>
<style>
.el-slider__runway {
background-color:rgba(206, 206, 206, 0.47) !important;
}
.el-slider__bar {
background-color: rgba(153, 153, 153, 0) !important;
}
.playtime-slider {
position: relative;
z-index: 100;
}
.data-picker-true{
}
.data-picker-true:after{
content: "";
position: absolute;
width: 4px;
height: 4px;
background-color: #606060;
border-radius: 4px;
left: 45%;
top: 74%;
}
.data-picker-false{
}
.slider-val-box{
height: 6px;
position: relative;
top: -22px;
}
.slider-val{
height: 6px;
background-color: #007CFF;
position: absolute;
}
.record-list-box-box{
width: 250px;
float: left;
}
.record-list-box{
overflow: auto;
width: 220px;
list-style: none;
padding: 0;
margin: 0;
margin-top: 0px;
padding: 1rem 0;
background-color: #FFF;
margin-top: 10px;
}
.record-list{
list-style: none;
padding: 0;
margin: 0;
background-color: #FFF;
}
.record-list-no-val {
position: absolute;
color: #9f9f9f;
top: 50%;
left: 110px;
}
.record-list-item{
padding: 0;
margin: 0;
margin: 0.5rem 0;
cursor: pointer;
}
.record-list-option {
width: 10px;
float: left;
margin-top: 39px;
}
.drawer-box{
height: 100%;
}
.task-list{
list-style: none;
padding: 0;
margin: 0;
background-color: #FFF;
}
.task-list-item{
padding: 0;
margin: 0;
margin: 1.5rem 0;
}
.task-list-item-box{
text-align: left;
font-size: 13px;
color: #555;
}
.download-btn{
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #FFF;
background-color: rgb(255, 255, 255);
border: 1px solid #DCDFE6;
border-top-color: rgb(220, 223, 230);
border-right-color: rgb(220, 223, 230);
border-bottom-color: rgb(220, 223, 230);
border-left-color: rgb(220, 223, 230);
border-top-color: rgb(220, 223, 230);
border-right-color: rgb(220, 223, 230);
border-bottom-color: rgb(220, 223, 230);
border-left-color: rgb(220, 223, 230);
-webkit-appearance: none;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
margin: 0;
-webkit-transition: .1s;
transition: .1s;
font-weight: 500;
padding: 7px 14px;
font-size: 0.875rem;
border-radius: 4px;
font-size: 0.75rem;
border-radius: 3px;
color: #FFF;
background-color: #409EFF;
border-color: #409EFF;
float: right;
}
.download-btn:hover{
background: #66b1ff;
border-color: #66b1ff;
color: #FFF;
}
.time-box{
}
</style>

View File

@ -1,557 +0,0 @@
<template>
<div id="app" style="width: 100%">
<div class="page-header">
<div class="page-title">设备列表</div>
<div class="page-header-btn">
搜索:
<el-input @input="initData" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
在线状态:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="initData" v-model="online" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="add">添加设备
</el-button>
<el-button icon="el-icon-info" size="mini" style="margin-right: 1rem;" type="primary" @click="showInfo()">平台信息
</el-button>
<el-button icon="el-icon-refresh-right" circle size="mini" :loading="getDeviceListLoading"
@click="getDeviceList()"></el-button>
</div>
</div>
<!--设备列表-->
<el-table size="medium" :data="deviceList" style="width: 100%;font-size: 12px;" :height="$tableHeght" header-row-class-name="table-header">
<el-table-column prop="name" label="名称" min-width="160">
</el-table-column>
<el-table-column prop="deviceId" label="设备编号" min-width="160" >
</el-table-column>
<el-table-column label="地址" min-width="160" >
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag v-if="scope.row.hostAddress" size="medium">{{ scope.row.hostAddress }}</el-tag>
<el-tag v-if="!scope.row.hostAddress" size="medium">未知</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="manufacturer" label="厂家" min-width="100" >
</el-table-column>
<el-table-column prop="transport" label="信令传输模式" min-width="100" >
</el-table-column>
<el-table-column label="流传输模式" min-width="160" >
<template v-slot:default="scope">
<el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择" style="width: 120px">
<el-option key="UDP" label="UDP" value="UDP"></el-option>
<el-option key="TCP-ACTIVE" label="TCP主动模式" value="TCP-ACTIVE"></el-option>
<el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="通道数" min-width="100" >
<template v-slot:default="scope">
<span style="font-size: 1rem">{{scope.row.channelCount}}</span>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.onLine && Vue.prototype.$myServerId !== scope.row.serverId" style="border-color: #ecf1af">在线</el-tag>
<el-tag size="medium" v-if="scope.row.onLine && Vue.prototype.$myServerId === scope.row.serverId">在线</el-tag>
<el-tag size="medium" type="info" v-if="!scope.row.onLine">离线</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="订阅" min-width="260" >
<template v-slot:default="scope">
<el-checkbox label="目录" :checked="scope.row.subscribeCycleForCatalog > 0" @change="(e)=>subscribeForCatalog(scope.row.id, e)"></el-checkbox>
<el-checkbox label="位置" :checked="scope.row.subscribeCycleForMobilePosition > 0" @change="(e)=>subscribeForMobilePosition(scope.row.id, e)"></el-checkbox>
<el-checkbox label="报警" disabled :checked="scope.row.subscribeCycleForAlarm > 0"></el-checkbox>
</template>
</el-table-column>
<el-table-column prop="keepaliveTime" label="最近心跳" min-width="140" >
</el-table-column>
<el-table-column prop="registerTime" label="最近注册" min-width="140">
</el-table-column>
<el-table-column label="操作" min-width="300" fixed="right">
<template v-slot:default="scope">
<el-button type="text" size="medium" v-bind:disabled="scope.row.online===0" icon="el-icon-refresh" @click="refDevice(scope.row)"
@mouseover="getTooltipContent(scope.row.deviceId)">刷新
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text" size="medium" icon="el-icon-video-camera"
@click="showChannelList(scope.row)">通道
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">编辑</el-button>
<el-divider direction="vertical"></el-divider>
<el-dropdown @command="(command)=>{moreClick(command, scope.row)}">
<el-button size="medium" type="text" >
操作<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu>
<el-dropdown-item command="delete" style="color: #f56c6c">
删除</el-dropdown-item>
<el-dropdown-item command="setGuard" v-bind:disabled="!scope.row.onLine">
布防</el-dropdown-item>
<el-dropdown-item command="resetGuard" v-bind:disabled="!scope.row.onLine">
撤防</el-dropdown-item>
<el-dropdown-item command="syncBasicParam" v-bind:disabled="!scope.row.onLine">
基础配置同步</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
<deviceEdit ref="deviceEdit"></deviceEdit>
<syncChannelProgress ref="syncChannelProgress"></syncChannelProgress>
<configInfo ref="configInfo"></configInfo>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import deviceEdit from './dialog/deviceEdit.vue'
import syncChannelProgress from './dialog/SyncChannelProgress.vue'
import configInfo from "./dialog/configInfo.vue";
import Vue from "vue";
export default {
name: 'app',
components: {
configInfo,
uiHeader,
deviceEdit,
syncChannelProgress,
},
data() {
return {
deviceList: [], //
currentDevice: {}, //
searchSrt: "",
online: null,
videoComponentList: [],
updateLooper: 0, //
currentDeviceChannelsLength: 0,
currentPage: 1,
count: 15,
total: 0,
getDeviceListLoading: false,
};
},
computed: {
Vue() {
return Vue
},
getcurrentDeviceChannels: function () {
let data = this.currentDevice['channelMap'];
let channels = null;
if (data) {
channels = Object.keys(data).map(key => {
return data[key];
});
this.currentDeviceChannelsLength = channels.length;
}
return channels;
}
},
mounted() {
this.initData();
this.updateLooper = setInterval(this.getDeviceList, 10000);
},
destroyed() {
this.$destroy('videojs');
clearTimeout(this.updateLooper);
},
methods: {
initData: function () {
this.currentPage = 1;
this.total= 0;
this.getDeviceList();
},
currentChange: function (val) {
this.currentPage = val;
this.getDeviceList();
},
handleSizeChange: function (val) {
this.count = val;
this.getDeviceList();
},
getDeviceList: function () {
this.getDeviceListLoading = true;
this.$axios({
method: 'get',
url: `/api/device/query/devices`,
params: {
page: this.currentPage,
count: this.count,
query: this.searchSrt,
status: this.online,
}
}).then( (res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.deviceList = res.data.data.list;
}
this.getDeviceListLoading = false;
}).catch( (error)=> {
console.error(error);
this.getDeviceListLoading = false;
});
},
deleteDevice: function (row) {
let msg = "确定删除此设备?"
if (row.online !== 0) {
msg = "在线设备删除后仍可通过注册再次上线。<br/>如需彻底删除请先将设备离线。<br/><strong>确定删除此设备?</strong>"
}
this.$confirm(msg, '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
center: true,
type: 'warning'
}).then(() => {
this.$axios({
method: 'delete',
url: `/api/device/query/devices/${row.deviceId}/delete`
}).then((res) => {
this.getDeviceList();
}).catch((error) => {
console.error(error);
});
}).catch(() => {
});
},
showChannelList: function (row) {
this.$router.push(`/channelList/${row.deviceId}/0`);
},
showDevicePosition: function (row) {
this.$router.push(`/map?deviceId=${row.deviceId}`);
},
//gb28181
//
refDevice: function (itemData) {
console.log("刷新对应设备:" + itemData.deviceId);
let that = this;
this.$axios({
method: 'get',
url: '/api/device/query/devices/' + itemData.deviceId + '/sync'
}).then((res) => {
console.log("刷新设备结果:" + JSON.stringify(res));
if (res.data.code !== 0) {
that.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
} else {
if (res.data.data && res.data.data.errorMsg) {
that.$message({
showClose: true,
message: res.data.data.errorMsg,
type: 'error'
});
return;
}
this.$refs.syncChannelProgress.openDialog(itemData.deviceId, ()=>{
console.log(32322)
this.initData()
})
}
that.initData()
}).catch((e) => {
console.error(e)
that.$message({
showClose: true,
message: e,
type: 'error'
});
});
},
getTooltipContent: async function (deviceId) {
let result = "";
await this.$axios({
method: 'get',
async: false,
url: `/api/device/query/${deviceId}/sync_status/`,
}).then((res) => {
if (res.data.code == 0) {
if (res.data.data.errorMsg !== null) {
result = res.data.data.errorMsg
} else if (res.data.msg !== null) {
result = res.data.msg
} else {
result = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
}
}
})
return result;
},
transportChange: function (row) {
console.log(`修改传输方式为 ${row.streamMode}${row.deviceId} `);
let that = this;
this.$axios({
method: 'post',
url: '/api/device/query/transport/' + row.deviceId + '/' + row.streamMode
}).then(function (res) {
}).catch(function (e) {
});
},
edit: function (row) {
this.$refs.deviceEdit.openDialog(row, () => {
this.$refs.deviceEdit.close();
this.$message({
showClose: true,
message: "设备修改成功,通道字符集将在下次更新生效",
type: "success",
});
setTimeout(this.getDeviceList, 200)
})
},
add: function () {
this.$refs.deviceEdit.openDialog(null, () => {
this.$refs.deviceEdit.close();
this.$message({
showClose: true,
message: "添加成功",
type: "success",
});
setTimeout(this.getDeviceList, 200)
})
},
showInfo: function (){
this.$axios({
method: 'get',
url: `/api/server/system/configInfo`,
}).then( (res)=> {
if (res.data.code === 0) {
this.serverId = res.data.data.addOn.serverId;
this.$refs.configInfo.openDialog(res.data.data)
}
}).catch( (error)=> {
});
},
moreClick: function (command, itemData) {
if (command === "setGuard") {
this.setGuard(itemData)
}else if (command === "resetGuard") {
this.resetGuard(itemData)
}else if (command === "delete") {
this.deleteDevice(itemData)
}else if (command === "syncBasicParam") {
this.syncBasicParam(itemData)
}
},
setGuard: function (itemData) {
this.$axios({
method: 'get',
url: `/api/device/control/guard/${itemData.deviceId}/SetGuard`,
}).then( (res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "布防成功"
})
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch( (error)=> {
this.$message.error({
showClose: true,
message: error.message
})
});
},
resetGuard: function (itemData) {
this.$axios({
method: 'get',
url: `/api/device/control/guard/${itemData.deviceId}/ResetGuard`,
}).then( (res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "撤防成功"
})
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch( (error)=> {
this.$message.error({
showClose: true,
message: error.message
})
});
},
subscribeForCatalog: function (data, value) {
console.log(data)
console.log(value)
this.$axios({
method: 'get',
url: `/api/device/query/subscribe/catalog`,
params: {
id: data,
cycle: value?60:0
}
}).then( (res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: value?"订阅成功":"取消订阅成功"
})
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch( (error)=> {
this.$message.error({
showClose: true,
message: error.message
})
});
},
subscribeForMobilePosition: function (data, value) {
console.log(data)
console.log(value)
this.$axios({
method: 'get',
url: `/api/device/query/subscribe/mobile-position`,
params: {
id: data,
cycle: value?60:0,
interval: value?5:0
}
}).then( (res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: value?"订阅成功":"取消订阅成功"
})
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch( (error)=> {
this.$message.error({
showClose: true,
message: error.message
})
});
},
syncBasicParam: function (data) {
console.log(data)
this.$axios({
method: 'get',
url: `/api/device/config/query/${data.deviceId}/BasicParam`,
params: {
// channelId: data.deviceId
}
}).then( (res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: `配置已同步,当前心跳间隔: ${res.data.data.BasicParam.HeartBeatInterval} 心跳间隔:${res.data.data.BasicParam.HeartBeatCount}`
})
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch( (error)=> {
this.$message.error({
showClose: true,
message: error.message
})
});
},
}
};
</script>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
</style>

View File

@ -1,547 +0,0 @@
<template>
<div style="width: 100%">
<div class="page-header" >
<div class="page-title">
<el-page-header @back="goBack" content="国标录像"></el-page-header>
</div>
</div>
<el-container>
<el-aside width="300px">
<div class="record-list-box-box">
<el-date-picker size="mini" v-model="chooseDate" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="dateChange()"></el-date-picker>
<div class="record-list-box" v-loading="recordsLoading" :style="recordListStyle">
<ul v-if="detailFiles.length >0" class="infinite-list record-list" >
<li v-for="item in detailFiles" class="infinite-list-item record-list-item" >
<el-tag v-if="chooseFile != item" @click="checkedFile(item)">
<i class="el-icon-video-camera" ></i>
{{ moment(item.startTime).format('HH:mm:ss')}}-{{ moment(item.endTime).format('HH:mm:ss')}}
</el-tag>
<el-tag v-if="chooseFile == item" type="danger" >
<i class="el-icon-video-camera" ></i>
{{ moment(item.startTime).format('HH:mm:ss')}}-{{ moment(item.endTime).format('HH:mm:ss')}}
</el-tag>
<i style="color: #409EFF;margin-left: 5px;" class="el-icon-download" @click="downloadRecord(item)" ></i>
</li>
</ul>
</div>
<div size="mini" v-if="detailFiles.length ==0" class="record-list-no-val" >暂无数据</div>
</div>
</el-aside>
<el-main style="padding-bottom: 10px;">
<div class="playBox" :style="playerBoxStyle">
<player ref="recordVideoPlayer"
:videoUrl="videoUrl"
:error="videoError"
:message="videoError"
:hasAudio="hasAudio"
style="max-height: 100%"
:height="playerHeight"
fluent autoplay live ></player>
</div>
<div class="player-option-box">
<div>
<el-button-group >
<el-time-picker
size="mini"
is-range
align="left"
v-model="timeRange"
value-format="yyyy-MM-dd HH:mm:ss"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
@change="timePickerChange"
placeholder="选择时间范围">
</el-time-picker>
</el-button-group>
<el-button-group >
<el-button size="mini" class="iconfont icon-zanting" title="暂停" @click="gbPause()"></el-button>
<el-button size="mini" class="iconfont icon-kaishi" title="开始" @click="gbPlay()"></el-button>
<el-dropdown size="mini" title="播放倍速" @command="gbScale">
<el-button size="mini">
倍速 <i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu>
<el-dropdown-item v-for="(item,index) in downloadSpeedArray" :key="index" :command="item">{{item}}倍速</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button size="mini" class="iconfont icon-xiazai1" title="下载选定录像" @click="downloadRecord()"></el-button>
<el-button v-if="sliderMIn === 0 && sliderMax === 86400" size="mini" class="iconfont icon-slider" title="放大滑块" @click="setSliderFit()"></el-button>
<el-button v-if="sliderMIn !== 0 || sliderMax !== 86400" size="mini" class="iconfont icon-slider-right" title="恢复滑块" @click="setSliderFit()"></el-button>
</el-button-group>
</div>
<el-slider
class="playtime-slider"
v-model="playTime"
id="playtimeSlider"
:disabled="detailFiles.length === 0"
:min="sliderMIn"
:max="sliderMax"
:range="true"
:format-tooltip="playTimeFormat"
@change="playTimeChange"
:marks="playTimeSliderMarks">
</el-slider>
<div class="slider-val-box">
<div class="slider-val" v-for="item of detailFiles" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div>
</div>
</div>
</el-main>
</el-container>
<recordDownload ref="recordDownload"></recordDownload>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import player from './common/jessibuca.vue'
import moment from 'moment'
import recordDownload from './dialog/recordDownload.vue'
export default {
name: 'app',
components: {
uiHeader, player,recordDownload
},
data() {
return {
deviceId: this.$route.params.deviceId,
channelId: this.$route.params.channelId,
downloadSpeedArray: [0.25, 0.5, 1, 2, 4],
recordsLoading: false,
streamId: "",
hasAudio: false,
detailFiles: [],
chooseDate: null,
videoUrl: null,
chooseFile: null,
streamInfo: null,
app: null,
mediaServerId: null,
ssrc: null,
sliderMIn: 0,
sliderMax: 86400,
autoPlay: true,
taskUpdate: null,
tabVal: "running",
recordListStyle: {
height: this.winHeight + "px",
overflow: "auto",
margin: "10px auto 10px auto"
},
playerBoxStyle: {
"margin": "0 auto 20px auto",
"height": this.winHeight + "px",
},
playerHeight: this.winHeight,
winHeight: window.innerHeight - 240,
playTime: null,
timeRange: null,
startTime: null,
endTime: null,
playTimeSliderMarks: {
0: "00:00",
3600: "01:00",
7200: "02:00",
10800: "03:00",
14400: "04:00",
18000: "05:00",
21600: "06:00",
25200: "07:00",
28800: "08:00",
32400: "09:00",
36000: "10:00",
39600: "11:00",
43200: "12:00",
46800: "13:00",
50400: "14:00",
54000: "15:00",
57600: "16:00",
61200: "17:00",
64800: "18:00",
68400: "19:00",
72000: "20:00",
75600: "21:00",
79200: "22:00",
82800: "23:00",
86400: "24:00",
},
};
},
computed: {
},
mounted() {
this.recordListStyle.height = this.winHeight + "px";
this.playerBoxStyle["height"] = this.winHeight + "px";
this.chooseDate = moment().format('YYYY-MM-DD')
this.dateChange();
this.getDownloadSpeedArray()
window.addEventListener('beforeunload', this.stopPlayRecord)
},
destroyed() {
this.$destroy('recordVideoPlayer');
window.removeEventListener('beforeunload', this.stopPlayRecord)
},
methods: {
dateChange(){
if (!this.chooseDate) {
return;
}
this.setTime(this.chooseDate + " 00:00:00", this.chooseDate + " 23:59:59");
this.recordsLoading = true;
this.detailFiles = [];
this.$axios({
method: 'get',
url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' + this.endTime
}).then((res)=>{
this.recordsLoading = false;
if(res.data.code === 0) {
//
this.detailFiles = res.data.data.recordList;
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
}).catch((e)=> {
this.recordsLoading = false;
// that.videoHistory.searchHistoryResult = falsificationData.recordData;
});
},
moment: function (v) {
return moment(v)
},
setTime: function (startTime, endTime){
this.startTime = startTime;
this.endTime = endTime;
let start = (new Date(this.startTime).getTime() - new Date(this.chooseDate + " 00:00:00").getTime())/1000;
let end = (new Date(this.endTime).getTime() - new Date(this.chooseDate + " 00:00:00").getTime())/1000;
console.log(start)
console.log(end)
this.playTime = [start, end];
this.timeRange = [startTime, endTime];
},
videoError: function (e) {
console.log("播放器错误:" + JSON.stringify(e));
},
checkedFile(file){
this.chooseFile = file;
this.setTime(file.startTime, file.endTime);
//
this.playRecord()
},
playRecord: function () {
if (this.streamId !== "") {
this.stopPlayRecord(()=> {
this.streamId = "";
this.playRecord();
})
} else {
this.$axios({
method: 'get',
url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' +
this.endTime
}).then((res)=> {
if (res.data.code === 0) {
this.streamInfo = res.data.data;
this.app = this.streamInfo.app;
this.streamId = this.streamInfo.stream;
this.mediaServerId = this.streamInfo.mediaServerId;
this.ssrc = this.streamInfo.ssrc;
this.videoUrl = this.getUrlByStreamInfo();
this.hasAudio = this.streamInfo.tracks && this.streamInfo.tracks.length > 1
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
});
}
},
getDownloadSpeedArray(){
this.$axios({
method: 'get',
url: '/api/device/query/channel/one',
params: {
deviceId: this.deviceId,
channelDeviceId: this.channelId,
}
}).then((res)=> {
if (res.data.code === 0 && res.data.data.downloadSpeed) {
let speedArray = res.data.data.downloadSpeed.split('/');
speedArray.forEach(item => {
if (parseInt(item) > 4) {
this.downloadSpeedArray.push(parseInt(item));
console.log(this.downloadSpeedArray);
}
})
}
})
},
gbPlay(){
console.log('前端控制:播放');
this.$axios({
method: 'get',
url: '/api/playback/resume/' + this.streamId
}).then((res)=> {
this.$refs["recordVideoPlayer"].play(this.videoUrl)
});
},
gbPause(){
console.log('前端控制:暂停');
this.$axios({
method: 'get',
url: '/api/playback/pause/' + this.streamId
}).then(function (res) {});
},
gbScale(command){
console.log('前端控制:倍速 ' + command);
this.$axios({
method: 'get',
url: `/api/playback/speed/${this.streamId }/${command}`
}).then(function (res) {});
},
downloadRecord: function (row) {
if (!row) {
let startTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + this.playTime[0]*1000).format("YYYY-MM-DD HH:mm:ss");
let endTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + this.playTime[1]*1000).format("YYYY-MM-DD HH:mm:ss");
console.log(startTimeStr);
console.log(endTimeStr);
row = {
startTime: startTimeStr,
endTime: endTimeStr
}
}
if (this.streamId !== "") {
this.stopPlayRecord(()=> {
this.streamId = "";
this.downloadRecord(row);
})
}else {
this.$axios({
method: 'get',
url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
row.endTime + '&downloadSpeed='+ this.downloadSpeedArray[this.downloadSpeedArray.length - 1]
}).then( (res)=> {
if (res.data.code === 0) {
let streamInfo = res.data.data;
this.$refs.recordDownload.openDialog(this.deviceId, this.channelId, streamInfo.app, streamInfo.stream, streamInfo.mediaServerId);
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
});
}
},
stopDownloadRecord: function (callback) {
this.$refs["recordVideoPlayer"].pause();
this.videoUrl = '';
this.$axios({
method: 'get',
url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
}).then((res)=> {
if (callback) callback(res)
});
},
stopPlayRecord: function (callback) {
console.log("停止录像回放")
if (this.streamId !== "") {
this.$refs["recordVideoPlayer"].pause();
this.videoUrl = '';
this.$axios({
method: 'get',
url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
}).then(function (res) {
if (callback) callback()
});
}
},
getDataWidth(item){
let timeForFile = this.getTimeForFile(item);
let result = (timeForFile[2])/((this.sliderMax - this.sliderMIn)*1000)
return result*100
},
getDataLeft(item){
let timeForFile = this.getTimeForFile(item);
let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " 00:00:00").getTime()
return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100 ;
},
getUrlByStreamInfo(){
if (location.protocol === "https:") {
this.videoUrl = this.streamInfo["wss_flv"]
}else {
this.videoUrl = this.streamInfo["ws_flv"]
}
return this.videoUrl;
},
timePickerChange: function (val){
this.setTime(val[0], val[1])
},
playTimeChange(val){
console.log(val)
let startTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + val[0]*1000).format("YYYY-MM-DD HH:mm:ss");
let endTimeStr = moment(new Date(this.chooseDate + " 00:00:00").getTime() + val[1]*1000).format("YYYY-MM-DD HH:mm:ss");
this.setTime(startTimeStr, endTimeStr)
this.playRecord();
},
setSliderFit() {
if (this.sliderMIn === 0 && this.sliderMax === 86400) {
if (this.detailFiles.length > 0){
let timeForFile = this.getTimeForFile(this.detailFiles[0]);
let lastTimeForFile = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1]);
let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime()
let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime()
this.playTime = parseInt(timeNum/1000)
this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60))
this.sliderMax = parseInt(lastTimeNum/1000 - lastTimeNum/1000%(60*60)) + 60*60
this.playTime = [this.sliderMIn, this.sliderMax];
}
}else {
this.sliderMIn = 0;
this.sliderMax = 86400;
}
},
getTimeForFile(file){
let startTime = new Date(file.startTime);
let endTime = new Date(file.endTime);
return [startTime, endTime, endTime.getTime() - startTime.getTime()];
},
playTimeFormat(val){
let h = parseInt(val/3600);
let m = parseInt((val - h*3600)/60);
let s = parseInt(val - h*3600 - m*60);
let hStr = h;
let mStr = m;
let sStr = s;
if (h < 10) {
hStr = "0" + hStr;
}
if (m < 10) {
mStr = "0" + mStr;s
}
if (s < 10) {
sStr = "0" + sStr;
}
return hStr + ":" + mStr + ":" + sStr
},
goBack(){
//
if (this.streamId !== "") {
this.stopPlayRecord(()=> {
this.streamId = "";
})
}
window.history.go(-1);
},
}
};
</script>
<style>
.el-slider__runway {
background-color:rgba(206, 206, 206, 0.47) !important;
}
.el-slider__bar {
background-color: rgba(153, 153, 153, 0) !important;
}
.playtime-slider {
position: relative;
z-index: 100;
}
.data-picker-true{
}
.data-picker-true:after{
content: "";
position: absolute;
width: 4px;
height: 4px;
background-color: #606060;
border-radius: 4px;
left: 45%;
top: 74%;
}
.data-picker-false{
}
.slider-val-box{
height: 6px;
position: relative;
top: -22px;
}
.slider-val{
height: 6px;
background-color: #007CFF;
position: absolute;
}
.record-list-box-box{
width: 250px;
float: left;
}
.record-list-box{
overflow: auto;
width: 220px;
list-style: none;
padding: 0;
margin: 0;
margin-top: 0px;
padding: 1rem 0;
background-color: #FFF;
margin-top: 10px;
}
.record-list{
list-style: none;
padding: 0;
margin: 0;
background-color: #FFF;
}
.record-list-no-val {
position: absolute;
color: #9f9f9f;
top: 50%;
left: 110px;
}
.record-list-item{
padding: 0;
margin: 0;
margin: 0.5rem 0;
cursor: pointer;
}
.record-list-option {
width: 10px;
float: left;
margin-top: 39px;
}
.player-option-box{
padding: 0 20px;
}
</style>

View File

@ -1,250 +0,0 @@
/**
* 经纬度转换
*/
export default {
PI: 3.1415926535897932384626,
//PI: 3.14159265358979324,
x_pi: (3.1415926535897932384626 * 3000.0) / 180.0,
delta: function (lat, lng) {
// Krasovsky 1940
//
// a = 6378245.0, 1/f = 298.3
// b = a * (1 - f)
// ee = (a^2 - b^2) / a^2;
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
var dLat = this.transformLat(lng - 105.0, lat - 35.0);
var dLng = this.transformLng(lng - 105.0, lat - 35.0);
var radLat = (lat / 180.0) * this.PI;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * this.PI);
dLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * this.PI);
return {
lat: dLat,
lng: dLng
};
},
/**
* WGS-84 to GCJ-02 GPS坐标转中国坐标
* @param {number} wgsLat GPS纬度
* @param {number} wgsLng GPS经度
* @return {object} 返回中国坐标经纬度对象
*/
GPSToChina: function (wgsLat, wgsLng) {
if (this.outOfChina(wgsLat, wgsLng)) return {
lat: wgsLat,
lng: wgsLng
};
var d = this.delta(wgsLat, wgsLng);
return {
lat: Number(wgsLat) + Number(d.lat),
lng: Number(wgsLng) + Number(d.lng)
};
},
/**
* GCJ-02 to WGS-84 中国标准坐标转GPS坐标
* @param {number} gcjLat 中国标准坐标纬度
* @param {number} gcjLng 中国标准坐标经度
* @return {object} 返回GPS经纬度对象
*/
chinaToGPS: function (gcjLat, gcjLng) {
if (this.outOfChina(gcjLat, gcjLng)) return {
lat: gcjLat,
lng: gcjLng
};
var d = this.delta(gcjLat, gcjLng);
return {
lat: Number(gcjLat) - Number(d.lat),
lng: Number(gcjLng) - Number(d.lng)
};
},
/**
* GCJ-02 to WGS-84 exactly 中国标准坐标转GPS坐标(精确)
* @param {number} gcjLat 中国标准坐标纬度
* @param {number} gcjLng 中国标准坐标经度
* @return {object} 返回GPS经纬度对象(精确)
*/
chinaToGPSExact: function (gcjLat, gcjLng) {
var initDelta = 0.01;
var threshold = 0.000000001;
var dLat = initDelta,
dLng = initDelta;
var mLat = gcjLat - dLat,
mLng = gcjLng - dLng;
var pLat = gcjLat + dLat,
pLng = gcjLng + dLng;
var wgsLat,
wgsLng,
i = 0;
while (1) {
wgsLat = (mLat + pLat) / 2;
wgsLng = (mLng + pLng) / 2;
var tmp = this.gcj_encrypt(wgsLat, wgsLng);
dLat = tmp.lat - gcjLat;
dLng = tmp.lng - gcjLng;
if (Math.abs(dLat) < threshold && Math.abs(dLng) < threshold) break;
if (dLat > 0) pLat = wgsLat;
else mLat = wgsLat;
if (dLng > 0) pLng = wgsLng;
else mLng = wgsLng;
if (++i > 10000) break;
}
//console.log(i);
return {
lat: wgsLat,
lng: wgsLng
};
},
/**
* GCJ-02 to BD-09 中国标准坐标转百度坐标(精确)
* @param {number} gcjLat 中国标准坐标纬度
* @param {number} gcjLng 中国标准坐标经度
* @return {object} 返回百度经纬度对象
*/
chinaToBaidu: function (gcjLat, gcjLng) {
var x = gcjLng,
y = gcjLat;
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
var bdLng = z * Math.cos(theta) + 0.0065;
var bdLat = z * Math.sin(theta) + 0.006;
return {
lat: bdLat,
lng: bdLng
};
},
/**
* BD-09 to GCJ-02 百度坐标转中国标准坐标
* @param {number} bdLat 百度坐标纬度
* @param {number} bdLng 百度坐标经度
* @return {object} 返回中国标准经纬度对象
*/
baiduToChina: function (bdLat, bdLng) {
var x = bdLng - 0.0065,
y = bdLat - 0.006;
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
var gcjLng = z * Math.cos(theta);
var gcjLat = z * Math.sin(theta);
return {
lat: gcjLat,
lng: gcjLng
};
},
/**
* BD-09 to GCJ-02 百度坐标转gps坐标
* @param {number} bdLat 百度坐标纬度
* @param {number} bdLng 百度坐标经度
* @return {object} 返回gps经纬度对象
*/
baiduToGPS: function (bdLat, bdLng) {
let china = this.baiduToChina(bdLat, bdLng);
return this.chinaToGPS(china.lat, china.lng);
},
/**
* WGS-84 to to BD-09 GPS坐标转Baidu坐标
* @param {number} gpsLat GPS纬度
* @param {number} gpsLng GPS经度
* @return {object} 返回百度经纬度对象
*/
GPSToBaidu: function (gpsLat, gpsLng) {
var china = this.GPSToChina(gpsLat, gpsLng);
return this.chinaToBaidu(china.lat, china.lng);
},
/**
* WGS-84 to Web mercator GPS坐标转墨卡托坐标
* @param {number} wgsLat GPS纬度
* @param {number} wgsLng GPS经度
* @return {object} 返回墨卡托经纬度对象
*/
GPSToMercator: function (wgsLat, wgsLng) {
var x = (wgsLng * 20037508.34) / 180;
var y = Math.log(Math.tan(((90 + wgsLat) * this.PI) / 360)) / (this.PI / 180);
y = (y * 20037508.34) / 180;
return {
lat: y,
lng: x
};
/*
if ((Math.abs(wgsLng) > 180 || Math.abs(wgsLat) > 90))
return null;
var x = 6378137.0 * wgsLng * 0.017453292519943295;
var a = wgsLat * 0.017453292519943295;
var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
return {'lat' : y, 'lng' : x};
//*/
},
/**
* Web mercator to WGS-84 墨卡托坐标转GPS坐标
* @param {number} mercatorLat 墨卡托纬度
* @param {number} mercatorLng 墨卡托经度
* @return {object} 返回GPS经纬度对象
*/
mercatorToGPS: function (mercatorLat, mercatorLng) {
var x = (mercatorLng / 20037508.34) * 180;
var y = (mercatorLat / 20037508.34) * 180;
y = (180 / this.PI) * (2 * Math.atan(Math.exp((y * this.PI) / 180)) - this.PI / 2);
return {
lat: y,
lng: x
};
/*
if (Math.abs(mercatorLng) < 180 && Math.abs(mercatorLat) < 90)
return null;
if ((Math.abs(mercatorLng) > 20037508.3427892) || (Math.abs(mercatorLat) > 20037508.3427892))
return null;
var a = mercatorLng / 6378137.0 * 57.295779513082323;
var x = a - (Math.floor(((a + 180.0) / 360.0)) * 360.0);
var y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * mercatorLat) / 6378137.0)))) * 57.295779513082323;
return {'lat' : y, 'lng' : x};
//*/
},
/**
* 两点之间的距离
* @param {number} latA 起点纬度
* @param {number} lngA 起点经度
* @param {number} latB 终点纬度
* @param {number} lngB 终点经度
* @return {number} 返回距离()
*/
distance: function (latA, lngA, latB, lngB) {
var earthR = 6371000;
var x = Math.cos((latA * this.PI) / 180) * Math.cos((latB * this.PI) / 180) * Math.cos(((lngA - lngB) * this.PI) / 180);
var y = Math.sin((latA * this.PI) / 180) * Math.sin((latB * this.PI) / 180);
var s = x + y;
if (s > 1) s = 1;
if (s < -1) s = -1;
var alpha = Math.acos(s);
var distance = alpha * earthR;
return distance;
},
/**
* 是否在中国之外
* @param {number} lat 纬度
* @param {number} lng 经度
* @return {boolean]} 返回结果真或假
*/
outOfChina: function (lat, lng) {
if (lat < 72.004 || lat > 137.8347) return true;
if (lng < 0.8293 || lng > 55.8271) return true;
return false;
},
transformLat: function (x, y) {
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += ((20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0) / 3.0;
ret += ((20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin((y / 3.0) * this.PI)) * 2.0) / 3.0;
ret += ((160.0 * Math.sin((y / 12.0) * this.PI) + 320 * Math.sin((y * this.PI) / 30.0)) * 2.0) / 3.0;
return ret;
},
transformLng: function (x, y) {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += ((20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0) / 3.0;
ret += ((20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin((x / 3.0) * this.PI)) * 2.0) / 3.0;
ret += ((150.0 * Math.sin((x / 12.0) * this.PI) + 300.0 * Math.sin((x / 30.0) * this.PI)) * 2.0) / 3.0;
return ret;
}
};

View File

@ -1,122 +0,0 @@
<template>
<div class="login" id="login">
<div class="limiter">
<div class="container-login100">
<div class="wrap-login100">
<span class="login100-form-title p-b-26">WVP视频平台</span>
<span class="login100-form-title p-b-48">
<i class="fa fa-video-camera"></i>
</span>
<div class="wrap-input100 validate-input" data-validate = "Valid email is: a@b.c">
<input :class="'input100 ' + (username==''?'':'has-val')" type="text" v-model="username" name="username">
<span class="focus-input100" data-placeholder="用户名"></span>
</div>
<div class="wrap-input100 validate-input" data-validate="Enter password">
<span class="btn-show-pass">
<i :class="'fa ' + (!showPassword?'fa-eye':'fa-eye-slash')" @click="showPassword = !showPassword"></i>
</span>
<input :class="'input100 ' + (password==''?'':'has-val')" :type="(!showPassword?'password':'text')" v-model="password" name="password">
<span class="focus-input100" data-placeholder="密码"></span>
</div>
<div class="container-login100-form-btn">
<div class="wrap-login100-form-btn" :class="{'login-loading': isLoging}" v-loading="isLoging" element-loading-background="rgb(0 0 0 / 0%);" element-loading-custom-class="login-loading-class">
<div class="login100-form-bgbtn"></div>
<button class="login100-form-btn" @click="login">登录</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import crypto from 'crypto'
import userService from "./service/UserService";
export default {
name: 'Login',
data(){
return {
isLoging: false,
showPassword: false,
loginLoading: false,
username: '',
password: ''
}
},
created(){
var that = this;
document.onkeydown = function(e) {
var key = window.event.keyCode;
if (key == 13) {
that.login();
}
}
},
methods:{
//
login(){
if(this.username!='' && this.password!=''){
this.toLogin();
}
},
//
toLogin(){
//
let loginParam = {
username: this.username,
password: crypto.createHash('md5').update(this.password, "utf8").digest('hex')
}
var that = this;
//
this.isLoging = true;
let timeoutTask = setTimeout(()=>{
that.$message.error("登录超时");
that.isLoging = false;
}, 1000)
this.$axios({
method: 'get',
url:"/api/user/login",
params: loginParam
}).then(function (res) {
window.clearTimeout(timeoutTask)
console.log(res);
console.log("登录成功");
if (res.data.code === 0 ) {
userService.setUser(res.data.data)
//
that.cancelEnterkeyDefaultAction();
that.$router.push('/');
}else{
that.isLoging = false;
that.$message({
showClose: true,
message: '登录失败,用户名或密码错误',
type: 'error'
});
}
}).catch(function (error) {
console.log(error)
window.clearTimeout(timeoutTask)
that.$message.error(error.response.data.msg);
that.isLoging = false;
});
},
cancelEnterkeyDefaultAction: function() {
document.onkeydown = function(e) {
var key = window.event.keyCode;
if (key == 13) {
return false;
}
}
}
}
}
</script>

View File

@ -1,189 +0,0 @@
<template>
<div id="mediaServerManger" style="width: 100%">
<div class="page-header">
<div class="page-title">节点列表</div>
<div class="page-header-btn">
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="add">添加节点</el-button>
</div>
</div>
<el-row :gutter="12">
<el-col :span="num" v-for="item in mediaServerList" :key="item.id">
<el-card shadow="hover" :body-style="{ padding: '0px'}" class="server-card">
<div v-if="item.type === 'zlm'" class="card-img-zlm"></div>
<div v-if="item.type === 'abl'" class="card-img-abl"></div>
<div style="padding: 14px;text-align: left">
<span style="font-size: 16px">{{item.id}}</span>
<el-button v-if="!item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">编辑</el-button>
<el-button v-if="item.defaultServer" icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">查看</el-button>
<el-button v-if="!item.defaultServer" icon="el-icon-delete" style="margin-right: 10px;padding: 0;float: right;" type="text" @click="del(item)">移除</el-button>
<div style="margin-top: 13px; line-height: 12px; ">
<span style="font-size: 14px; color: #999; margin-top: 5px; ">{{item.ip}}</span>
<span style="font-size: 14px; color: #999; margin-top: 5px; float: right;">{{item.createTime}}</span>
</div>
</div>
<i v-if="item.status" class="iconfont icon-online server-card-status-online" title="在线"></i>
<i v-if="!item.status" class="iconfont icon-online server-card-status-offline" title="离线"></i>
<i v-if="item.defaultServer" class="server-card-default" >默认</i>
</el-card>
</el-col>
</el-row>
<mediaServerEdit ref="mediaServerEdit" ></mediaServerEdit>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import MediaServer from './service/MediaServer'
import mediaServerEdit from './dialog/MediaServerEdit'
export default {
name: 'mediaServerManger',
components: {
uiHeader,mediaServerEdit
},
data() {
return {
mediaServerObj : new MediaServer(),
mediaServerList: [], //
winHeight: window.innerHeight - 200,
updateLooper: false,
currentPage:1,
count:15,
num: this.getNumberByWidth(),
total:0,
};
},
computed: {
},
mounted() {
this.initData();
this.updateLooper = setInterval(this.initData, 2000);
},
destroyed() {
clearTimeout(this.updateLooper);
},
methods: {
initData: function() {
this.getServerList()
},
currentChange: function(val){
this.currentPage = val;
this.getServerList();
},
handleSizeChange: function(val){
this.count = val;
this.getServerList();
},
getServerList: function(){
this.mediaServerObj.getMediaServerList((data)=>{
this.mediaServerList = data.data;
})
},
add: function (){
this.$refs.mediaServerEdit.openDialog(null, this.initData)
},
edit: function (row){
this.$refs.mediaServerEdit.openDialog(row, this.initData)
},
del: function (row){
this.$confirm('确认删除此节点?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.mediaServerObj.delete(row.id, (data)=>{
if (data.code === 0) {
this.$message({
type: 'success',
message: '删除成功!'
});
}
})
}).catch(() => {
});
},
getNumberByWidth(){
let candidateNums = [1, 2, 3, 4, 6, 8, 12, 24]
let clientWidth = window.innerWidth - 30;
let interval = 20;
let itemWidth = 360;
let num = (clientWidth + interval)/(itemWidth + interval)
let result = Math.ceil(24/num);
let resultVal = 24;
for (let i = 0; i < candidateNums.length; i++) {
let value = candidateNums[i]
if (i + 1 >= candidateNums.length) {
return 24;
}
if (value <= result && candidateNums[i + 1] > result ) {
return value;
}
}
return resultVal;
},
dateFormat: function(/** timestamp=0 **/) {
var ts = arguments[0] || 0;
var t,y,m,d,h,i,s;
t = ts ? new Date(ts*1000) : new Date();
y = t.getFullYear();
m = t.getMonth()+1;
d = t.getDate();
h = t.getHours();
i = t.getMinutes();
s = t.getSeconds();
//
return y+'-'+(m<10?'0'+m:m)+'-'+(d<10?'0'+d:d)+' '+(h<10?'0'+h:h)+':'+(i<10?'0'+i:i)+':'+(s<10?'0'+s:s);
}
}
};
</script>
<style>
.server-card{
position: relative;
margin-bottom: 20px;
}
.card-img-zlm{
width: 200px; height: 200px;
background: url('~@static/images/zlm-logo.png') no-repeat center;
background-position: center;
background-size: contain;
margin: 0 auto;
}
.card-img-abl{
width: 200px; height: 200px;
background: url('~@static/images/abl-logo.jpg') no-repeat center;
background-position: center;
background-size: contain;
margin: 0 auto;
}
.server-card-status-online{
position: absolute;
right: 20px;
top: 20px;
color: #3caf36;
font-size: 18px;
}
.server-card-status-offline{
position: absolute;
right: 20px;
top: 20px;
color: #808080;
font-size: 18px;
}
.server-card-default{
position: absolute;
left: 20px;
top: 20px;
color: #808080;
font-size: 18px;
}
.server-card:hover {
border: 1px solid #adadad;
}
</style>

View File

@ -1,314 +0,0 @@
<template>
<div id="PlatformEdit" style="width: 100%">
<div class="page-header">
<div class="page-title">
<el-page-header @back="close" content="添加上级平台"></el-page-header>
</div>
<div class="page-header-btn">
<div style="display: inline;">
<el-button icon="el-icon-close" size="mini" style="font-size: 20px; color: #000;" type="text" @click="close" ></el-button>
</div>
</div>
</div>
<div id="shared" style="text-align: right; margin-top: 1rem; background-color: #FFFFFF; padding-top: 2rem;">
<el-row :gutter="24">
<el-col :span="11">
<el-form ref="platform1" :rules="rules" :model="value" size="medium" label-width="160px">
<el-form-item label="名称" prop="name">
<el-input v-model="value.name"></el-input>
</el-form-item>
<el-form-item label="SIP服务国标编码" prop="serverGBId">
<el-input v-model="value.serverGBId" clearable @input="serverGBIdChange"></el-input>
</el-form-item>
<el-form-item label="SIP服务国标域" prop="serverGBDomain">
<el-input v-model="value.serverGBDomain" clearable></el-input>
</el-form-item>
<el-form-item label="SIP服务IP" prop="serverIp">
<el-input v-model="value.serverIp" clearable></el-input>
</el-form-item>
<el-form-item label="SIP服务端口" prop="serverPort">
<el-input v-model="value.serverPort" clearable type="number"></el-input>
</el-form-item>
<el-form-item label="设备国标编号" prop="deviceGBId">
<el-input v-model="value.deviceGBId" clearable @input="deviceGBIdChange"></el-input>
</el-form-item>
<el-form-item label="本地IP" prop="deviceIp">
<el-select v-model="value.deviceIp" placeholder="请选择与上级相通的网卡" style="width: 100%">
<el-option
v-for="ip in deviceIps"
:key="ip"
:label="ip"
:value="ip">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="本地端口" prop="devicePort">
<el-input v-model="value.devicePort" :disabled="true" type="number"></el-input>
</el-form-item>
<el-form-item label="SIP认证用户名" prop="username">
<el-input v-model="value.username"></el-input>
</el-form-item>
<el-form-item label="SIP认证密码" prop="password">
<el-input v-model="value.password"></el-input>
</el-form-item>
<el-form-item label="注册周期(秒)" prop="expires">
<el-input v-model="value.expires"></el-input>
</el-form-item>
<el-form-item label="心跳周期(秒)" prop="keepTimeout">
<el-input v-model="value.keepTimeout"></el-input>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12">
<el-form ref="platform2" :rules="rules" :model="value" size="medium" label-width="160px">
<el-form-item label="SDP发流IP" prop="sendStreamIp">
<el-input v-model="value.sendStreamIp"></el-input>
</el-form-item>
<el-form-item label="信令传输" prop="transport">
<el-select
v-model="value.transport"
style="width: 100%"
placeholder="请选择信令传输方式"
>
<el-option label="UDP" value="UDP"></el-option>
<el-option label="TCP" value="TCP"></el-option>
</el-select>
</el-form-item>
<el-form-item label="保密属性" >
<el-select v-model="value.secrecy" style="width: 100%" placeholder="请选择保密属性">
<el-option label="不涉密" :value="0"></el-option>
<el-option label="涉密" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="目录分组" prop="catalogGroup">
<el-select
v-model="value.catalogGroup"
style="width: 100%"
placeholder="请选择目录分组"
>
<el-option label="1" value="1"></el-option>
<el-option label="2" value="2"></el-option>
<el-option label="4" value="4"></el-option>
<el-option label="8" value="8"></el-option>
</el-select>
</el-form-item>
<el-form-item label="字符集" prop="characterSet">
<el-select
v-model="value.characterSet"
style="width: 100%"
placeholder="请选择字符集"
>
<el-option label="GB2312" value="GB2312"></el-option>
<el-option label="UTF-8" value="UTF-8"></el-option>
</el-select>
</el-form-item>
<el-form-item label="行政区划" prop="civilCode">
<el-input v-model="value.civilCode" clearable></el-input>
</el-form-item>
<el-form-item label="平台厂商" prop="manufacturer">
<el-input v-model="value.manufacturer" clearable></el-input>
</el-form-item>
<el-form-item label="平台型号" prop="model">
<el-input v-model="value.model" clearable></el-input>
</el-form-item>
<el-form-item label="平台安装地址" prop="address">
<el-input v-model="value.address" clearable></el-input>
</el-form-item>
<el-form-item label="其他选项" >
<div style="text-align: left">
<el-checkbox label="启用" v-model="value.enable" @change="checkExpires"></el-checkbox>
<!-- <el-checkbox label="云台控制" v-model="value.ptz"></el-checkbox>-->
<el-checkbox label="RTCP保活" v-model="value.rtcp" @change="rtcpCheckBoxChange"></el-checkbox>
<el-checkbox label="消息通道" v-model="value.asMessageChannel"></el-checkbox>
<el-checkbox label="主动推送通道" v-model="value.autoPushChannel"></el-checkbox>
<el-checkbox label="推送平台信息" :true-label="1" :false-label="0" v-model="value.catalogWithPlatform"></el-checkbox>
<el-checkbox label="推送分组信息" :true-label="1" :false-label="0" v-model="value.catalogWithGroup"></el-checkbox>
<el-checkbox label="推送行政区划" :true-label="1" :false-label="0" v-model="value.catalogWithRegion"></el-checkbox>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">{{
onSubmit_text
}}
</el-button>
<el-button @click="close">取消</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: "platformEdit",
props: [ 'value', 'closeEdit', 'deviceIps'],
components: {
},
created() {
},
watch: {
value(newValue, oldValue){
this.streamProxy = newValue;
}
},
data() {
var deviceGBIdRules = async (rule, value, callback) => {
console.log(value);
if (value === "") {
callback(new Error("请输入设备国标编号"));
} else {
var exit = await this.deviceGBIdExit(value);
if (exit) {
callback(new Error("设备国标编号格式错误或已存在"));
} else {
callback();
}
}
}
return {
listChangeCallback: null,
showDialog: false,
isLoging: false,
onSubmit_text: "保存",
rules: {
name: [{required: true, message: "请输入平台名称", trigger: "blur"}],
serverGBId: [
{required: true, message: "请输入SIP服务国标编码", trigger: "blur"},
],
serverGBDomain: [
{required: true, message: "请输入SIP服务国标域", trigger: "blur"},
],
serverIp: [{required: true, message: "请输入SIP服务IP", trigger: "blur"}],
serverPort: [{required: true, message: "请输入SIP服务端口", trigger: "blur"}],
deviceGBId: [{validator: deviceGBIdRules, trigger: "blur"}],
username: [{required: false, message: "请输入SIP认证用户名", trigger: "blur"}],
password: [{required: false, message: "请输入SIP认证密码", trigger: "blur"}],
expires: [{required: true, message: "请输入注册周期", trigger: "blur"}],
keepTimeout: [{required: true, message: "请输入心跳周期", trigger: "blur"}],
transport: [{required: true, message: "请选择信令传输", trigger: "blur"}],
characterSet: [{required: true, message: "请选择编码字符集", trigger: "blur"}],
deviceIp: [{required: true, message: "请选择本地IP", trigger: "blur"}],
},
saveLoading: false,
};
},
methods: {
onSubmit: function () {
this.saveLoading = true;
if (this.value.id) {
this.$axios({
method: 'post',
url: "/api/platform/update",
data: this.value
}).then((res) => {
this.saveLoading = false;
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: "success",
});
this.showDialog = false;
if (this.closeEdit) {
this.closeEdit();
}
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
}).catch((error) => {
this.saveLoading = false;
console.log(error);
});
}else {
this.$axios({
method: 'post',
url: "/api/platform/add",
data: this.value
}).then((res) => {
this.saveLoading = false;
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: "success",
});
if (this.closeEdit) {
this.closeEdit();
}
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
}).catch((error) => {
this.saveLoading = false;
console.log(error);
});
}
},
serverGBIdChange: function () {
if (this.value.serverGBId.length > 10) {
this.value.serverGBDomain = this.value.serverGBId.substr(0, 10);
}
},
deviceGBIdChange: function () {
this.value.username = this.value.deviceGBId;
},
checkExpires: function () {
if (this.value.enable && this.value.expires === "0") {
this.value.expires = "3600";
}
},
rtcpCheckBoxChange: function (result) {
if (result) {
this.$message({
showClose: true,
message: "开启RTCP保活需要上级平台支持可以避免无效推流",
type: "warning",
});
}
},
deviceGBIdExit: async function (deviceGbId) {
let result = false;
await this.$axios({
method:"get",
url:`/api/platform/exit/${deviceGbId}`
}).then((res)=> {
result = res.data;
}).catch((error)=> {
console.log(error);
});
return result;
},
close: function () {
this.closeEdit()
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,306 +0,0 @@
<template>
<div id="app" style="width: 100%">
<div v-if="!platform">
<div class="page-header">
<div class="page-title">上级平台列表</div>
<div class="page-header-btn">
搜索:
<el-input @input="getPlatformList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary"
@click="addParentPlatform">添加
</el-button>
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
</div>
</div>
<!--设备列表-->
<el-table size="medium" :data="platformList" style="width: 100%" :height="$tableHeght" :loading="loading">
<el-table-column prop="name" label="名称"></el-table-column>
<el-table-column prop="serverGBId" label="平台编号" min-width="200"></el-table-column>
<el-table-column label="是否启用" min-width="80">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.enable && Vue.prototype.$myServerId !== scope.row.serverId" style="border-color: #ecf1af">已启用</el-tag>
<el-tag size="medium" v-if="scope.row.enable && Vue.prototype.$myServerId === scope.row.serverId">已启用</el-tag>
<el-tag size="medium" type="info" v-if="!scope.row.enable">未启用</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="状态" min-width="80">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.status">在线</el-tag>
<el-tag size="medium" type="info" v-if="!scope.row.status">离线</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="地址" min-width="160">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.serverIp }}:{{ scope.row.serverPort }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="deviceGBId" label="设备国标编号" min-width="200"></el-table-column>
<el-table-column prop="transport" label="信令传输模式" min-width="120"></el-table-column>
<el-table-column prop="channelCount" label="通道数" min-width="120"></el-table-column>
<el-table-column label="订阅信息" min-width="120" fixed="right">
<template v-slot:default="scope">
<i v-if="scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅"
class="iconfont icon-gbaojings subscribe-on "></i>
<i v-if="!scope.row.alarmSubscribe" style="font-size: 20px" title="报警订阅"
class="iconfont icon-gbaojings subscribe-off "></i>
<i v-if="scope.row.catalogSubscribe" title="目录订阅" class="iconfont icon-gjichus subscribe-on"></i>
<i v-if="!scope.row.catalogSubscribe" title="目录订阅" class="iconfont icon-gjichus subscribe-off"></i>
<i v-if="scope.row.mobilePositionSubscribe" title="位置订阅"
class="iconfont icon-gxunjians subscribe-on"></i>
<i v-if="!scope.row.mobilePositionSubscribe" title="位置订阅"
class="iconfont icon-gxunjians subscribe-off"></i>
</template>
</el-table-column>
<el-table-column label="操作" min-width="240" fixed="right">
<template v-slot:default="scope">
<el-button size="medium" icon="el-icon-edit" type="text" @click="editPlatform(scope.row)">编辑</el-button>
<el-button size="medium" icon="el-icon-share" type="text" @click="chooseChannel(scope.row)">通道共享
</el-button>
<el-button size="medium" icon="el-icon-top" type="text" :loading="pushChannelLoading"
@click="pushChannel(scope.row)">推送通道
</el-button>
<el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c"
@click="deletePlatform(scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
<platformEdit ref="platformEdit" v-if="platform" v-model="platform" :closeEdit="closeEdit"
:device-ips="deviceIps"></platformEdit>
<shareChannel ref="shareChannel"></shareChannel>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import shareChannel from './dialog/shareChannel.vue'
import platformEdit from './PlatformEdit.vue'
import streamProxyEdit from "./dialog/StreamProxyEdit.vue";
import Vue from "vue";
export default {
name: 'app',
components: {
streamProxyEdit,
uiHeader,
shareChannel,
platformEdit
},
data() {
return {
loading: false,
platformList: [], //
deviceIps: [], //
defaultPlatform: null,
platform: null,
pushChannelLoading: false,
searchSrt: "",
currentPage: 1,
count: 15,
total: 0
};
},
computed: {
Vue() {
return Vue
},
},
mounted() {
this.initData();
this.updateLooper = setInterval(this.initData, 10000);
},
destroyed() {
clearTimeout(this.updateLooper);
},
methods: {
addParentPlatform: function () {
this.platform = this.defaultPlatform;
},
editPlatform: function (platform) {
this.platform = platform;
},
closeEdit: function () {
this.platform = null;
this.getPlatformList()
},
deletePlatform: function (platform) {
this.$confirm('确认删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.deletePlatformCommit(platform)
})
},
deletePlatformCommit: function (platform) {
this.loading = true;
this.$axios({
method: 'delete',
url: `/api/platform/delete/`,
params: {
id: platform.id
}
}).then((res) => {
this.loading = false;
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: '删除成功',
});
this.initData()
}
}).catch((error) => {
this.loading = false;
this.$message.error({
showClose: true,
message: error,
});
});
},
chooseChannel: function (platform) {
this.$refs.shareChannel.openDialog(platform.id, this.initData)
},
pushChannel: function (row) {
this.pushChannelLoading = true;
this.$axios({
method: 'get',
url: `/api/platform/channel/push`,
params: {
id: row.id,
}
}).then((res) => {
this.pushChannelLoading = false;
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: '推送成功',
});
} else {
this.$message.error({
showClose: true,
message: res.data.msg,
});
}
}).catch((error) => {
console.log(error);
this.pushChannelLoading = false;
this.$message.error({
showClose: true,
message: error,
});
});
},
initData: function () {
this.$axios({
method: 'get',
url: `/api/platform/server_config`
}).then((res) => {
if (res.data.code === 0) {
this.deviceIps = res.data.data.deviceIp.split(',');
this.defaultPlatform = {
id: null,
enable: true,
ptz: true,
rtcp: false,
asMessageChannel: false,
autoPushChannel: false,
name: null,
serverGBId: null,
serverGBDomain: null,
serverIp: null,
serverPort: null,
deviceGBId: res.data.data.username,
deviceIp: this.deviceIps[0],
devicePort: res.data.data.devicePort,
username: res.data.data.username,
password: res.data.data.password,
expires: 3600,
keepTimeout: 60,
transport: "UDP",
characterSet: "GB2312",
startOfflinePush: false,
customGroup: false,
catalogWithPlatform: 0,
catalogWithGroup: 0,
catalogWithRegion: 0,
manufacturer: null,
model: null,
address: null,
secrecy: 1,
catalogGroup: 1,
civilCode: null,
sendStreamIp: res.data.data.sendStreamIp,
}
}
}).catch(function (error) {
console.log(error);
});
this.getPlatformList();
},
currentChange: function (val) {
this.currentPage = val;
this.getPlatformList();
},
handleSizeChange: function (val) {
this.count = val;
this.getPlatformList();
},
getPlatformList: function () {
this.$axios({
method: 'get',
url: `/api/platform/query`,
params: {
count: this.count,
page: this.currentPage,
query: this.searchSrt
}
}).then((res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.platformList = res.data.data.list;
}
}).catch(function (error) {
console.log(error);
});
},
refresh: function () {
this.initData();
}
}
};
</script>
<style>
.subscribe-on {
color: #409EFF;
font-size: 18px;
}
.subscribe-off {
color: #afafb3;
font-size: 18px;
}
</style>

View File

@ -1,235 +0,0 @@
<template>
<div id="recordPLan" style="width: 100%">
<div class="page-header">
<div class="page-title">
<div >录像计划</div>
</div>
<div class="page-header-btn">
<div style="display: inline;">
搜索:
<el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
<el-button size="mini" type="primary" @click="add()">
添加
</el-button>
<el-button icon="el-icon-refresh-right" circle size="mini" @click="getRecordPlanList()"></el-button>
</div>
</div>
</div>
<el-table size="medium" ref="recordPlanListTable" :data="recordPlanList" :height="$tableHeght" style="width: 100%"
header-row-class-name="table-header" >
<el-table-column type="selection" width="55" >
</el-table-column>
<el-table-column prop="name" label="名称" >
</el-table-column>
<el-table-column prop="channelCount" label="关联通道" >
</el-table-column>
<el-table-column prop="updateTime" label="更新时间">
</el-table-column>
<el-table-column prop="createTime" label="创建时间">
</el-table-column>
<el-table-column label="操作" width="300" fixed="right">
<template v-slot:default="scope">
<el-button size="medium" icon="el-icon-link" type="text" @click="link(scope.row)">关联通道</el-button>
<el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">编辑</el-button>
<el-button size="medium" icon="el-icon-delete" style="color: #f56c6c" type="text" @click="deletePlan(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
<editRecordPlan ref="editRecordPlan"></editRecordPlan>
<LinkChannelRecord ref="linkChannelRecord"></LinkChannelRecord>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import EditRecordPlan from "./dialog/editRecordPlan.vue";
import LinkChannelRecord from "./dialog/linkChannelRecord.vue";
export default {
name: 'recordPLan',
components: {
EditRecordPlan,
LinkChannelRecord,
uiHeader,
},
data() {
return {
recordPlanList: [],
searchSrt: "",
currentPage: 1,
count: 15,
total: 0,
loading: false,
};
},
created() {
this.initData();
},
destroyed() {
},
methods: {
initData: function () {
this.getRecordPlanList();
},
currentChange: function (val) {
this.currentPage = val;
this.initData();
},
handleSizeChange: function (val) {
this.count = val;
this.getRecordPlanList();
},
getRecordPlanList: function () {
this.$axios({
method: 'get',
url: `/api/record/plan/query`,
params: {
page: this.currentPage,
count: this.count,
query: this.searchSrt,
}
}).then((res) => {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.recordPlanList = res.data.data.list;
//
this.$nextTick(() => {
this.$refs.recordPlanListTable.doLayout();
})
}
}).catch((error) => {
console.log(error);
});
},
getSnap: function (row) {
let baseUrl = window.baseUrl ? window.baseUrl : "";
return ((process.env.NODE_ENV === 'development') ? process.env.BASE_API : baseUrl) + '/api/device/query/snap/' + this.deviceId + '/' + row.deviceId;
},
search: function () {
this.currentPage = 1;
this.total = 0;
this.initData();
},
refresh: function () {
this.initData();
},
add: function () {
this.$refs.editRecordPlan.openDialog(null, ()=>{
this.initData()
})
},
edit: function (plan) {
this.$refs.editRecordPlan.openDialog(plan, ()=>{
this.initData()
})
},
link: function (plan) {
this.$refs.linkChannelRecord.openDialog(plan.id, ()=>{
this.initData()
})
},
deletePlan: function (plan) {
this.$confirm('确定删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios({
method: 'delete',
url: "/api/record/plan/delete",
params: {
planId: plan.id,
}
}).then((res) => {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '删除成功',
type: 'success',
});
this.initData();
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error) => {
console.error(error)
});
}).catch(() => {
});
},
}
};
</script>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
</style>

View File

@ -1,260 +0,0 @@
<template>
<div id="StreamProxyEdit" style="width: 100%">
<div class="page-header">
<div class="page-title">
<el-page-header @back="close" content="编辑拉流代理信息"></el-page-header>
</div>
<div class="page-header-btn">
<div style="display: inline;">
<el-button icon="el-icon-close" size="mini" style="font-size: 20px; color: #000;" type="text" @click="close" ></el-button>
</div>
</div>
</div>
<el-tabs tab-position="left" style="background-color: #FFFFFF; padding-top: 1rem">
<el-tab-pane label="拉流代理信息">
<el-form ref="streamProxy" :rules="rules" :model="streamProxy" label-width="140px" style="width: 50%; margin: 0 auto">
<el-form-item label="类型" prop="type">
<el-select
v-model="streamProxy.type"
style="width: 100%"
placeholder="请选择代理类型"
>
<el-option key="默认" label="默认" value="default"></el-option>
<el-option key="FFmpeg" label="FFmpeg" value="ffmpeg"></el-option>
</el-select>
</el-form-item>
<el-form-item label="应用名" prop="app">
<el-input v-model="streamProxy.app" clearable></el-input>
</el-form-item>
<el-form-item label="流ID" prop="stream">
<el-input v-model="streamProxy.stream" clearable></el-input>
</el-form-item>
<el-form-item label="拉流地址" prop="url">
<el-input v-model="streamProxy.srcUrl" clearable></el-input>
</el-form-item>
<el-form-item label="超时时间(秒)" prop="timeoutMs">
<el-input v-model="streamProxy.timeout" clearable></el-input>
</el-form-item>
<el-form-item label="节点选择" prop="rtpType">
<el-select
v-model="streamProxy.relatesMediaServerId"
@change="mediaServerIdChange"
style="width: 100%"
placeholder="请选择拉流节点"
>
<el-option key="auto" label="自动选择" value=""></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="FFmpeg命令模板" prop="ffmpegCmdKey" v-if="streamProxy.type=='ffmpeg'">
<el-select
v-model="streamProxy.ffmpegCmdKey"
style="width: 100%"
placeholder="请选择FFmpeg命令模板"
>
<el-option
v-for="item in Object.keys(ffmpegCmdList)"
:key="item"
:label="ffmpegCmdList[item]"
:value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="拉流方式(RTSP)" prop="rtspType">
<el-select
v-model="streamProxy.rtspType"
style="width: 100%"
placeholder="请选择拉流方式"
>
<el-option label="TCP" value="0"></el-option>
<el-option label="UDP" value="1"></el-option>
<el-option label="组播" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="无人观看" prop="noneReader" >
<el-radio-group v-model="streamProxy.noneReader">
<el-radio :label="0">不做处理</el-radio>
<el-radio :label="1">停用</el-radio>
<el-radio :label="2">移除</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="其他选项">
<div style="float: left;">
<el-checkbox label="启用" v-model="streamProxy.enable" ></el-checkbox>
<el-checkbox label="开启音频" v-model="streamProxy.enableAudio" ></el-checkbox>
<el-checkbox label="录制" v-model="streamProxy.enableMp4" ></el-checkbox>
</div>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit" :loading="saveLoading" >保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="国标通道配置" v-if="streamProxy.id">
<CommonChannelEdit ref="commonChannelEdit" :dataForm="streamProxy" :cancel="close"></CommonChannelEdit>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import CommonChannelEdit from './common/CommonChannelEdit'
import MediaServer from "./service/MediaServer";
export default {
name: "channelEdit",
props: [ 'value', 'closeEdit'],
components: {
CommonChannelEdit,
},
created() {
console.log(this.streamProxy)
this.mediaServer.getOnlineMediaServerList((data)=>{
this.mediaServerList = data.data;
})
},
watch: {
value(newValue, oldValue){
this.streamProxy = newValue;
}
},
data() {
return {
saveLoading: false,
streamProxy: this.value,
mediaServer: new MediaServer(),
mediaServerList:{},
ffmpegCmdList:{},
rules: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
app: [{ required: true, message: "请输入应用名", trigger: "blur" }],
stream: [{ required: true, message: "请输入流ID", trigger: "blur" }],
srcUrl: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],
timeout: [{ required: true, message: "请输入FFmpeg推流成功超时时间", trigger: "blur" }],
ffmpegCmdKey: [{ required: false, message: "请输入FFmpeg命令参数模板可选", trigger: "blur" }],
},
};
},
methods: {
onSubmit: function () {
console.log(typeof this.streamProxy.noneReader)
this.saveLoading = true;
this.noneReaderHandler();
if (this.streamProxy.id) {
this.$axios({
method: 'post',
url:`/api/proxy/update`,
data: this.streamProxy
}).then((res)=> {
this.saveLoading = false;
if (typeof (res.data.code) != "undefined" && res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
});
this.streamProxy = res.data.data
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
this.saveLoading = false;
}).catch((error) =>{
this.$message.error({
showClose: true,
message: error
});
this.saveLoading = false;
}).finally(()=>{
console.log("finally==finally")
this.saveLoading = false;
})
}else {
this.$axios({
method: 'post',
url:`/api/proxy/add`,
data: this.streamProxy
}).then((res)=> {
this.saveLoading = false;
if (typeof (res.data.code) != "undefined" && res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
});
this.streamProxy = res.data.data
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) =>{
this.$message.error({
showClose: true,
message: error
})
this.saveLoading = false;
}).finally(()=>{
this.saveLoading = false;
})
}
},
close: function () {
this.closeEdit()
},
mediaServerIdChange:function (){
if (this.streamProxy.relatesMediaServerId !== "auto"){
this.$axios({
method: 'get',
url:`/api/proxy/ffmpeg_cmd/list`,
params: {
mediaServerId: this.streamProxy.relatesMediaServerId
}
}).then((res)=> {
this.ffmpegCmdList = res.data.data;
this.streamProxy.ffmpegCmdKey = Object.keys(res.data.data)[0];
}).catch(function (error) {
console.log(error);
});
}
},
noneReaderHandler: function() {
console.log(this.streamProxy)
if (!this.streamProxy.noneReader || this.streamProxy.noneReader === 0 ) {
this.streamProxy.enableDisableNoneReader = false;
this.streamProxy.enableRemoveNoneReader = false;
}else if (this.streamProxy.noneReader === 1){
this.streamProxy.enableDisableNoneReader = true;
this.streamProxy.enableRemoveNoneReader = false;
}else if (this.streamProxy.noneReader ===2){
this.streamProxy.enableDisableNoneReader = false;
this.streamProxy.enableRemoveNoneReader = true;
}
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,405 +0,0 @@
<template>
<div id="streamProxyList" style="width: 100%">
<div v-if="!streamProxy">
<div class="page-header">
<div class="page-title">拉流代理列表</div>
<div class="page-header-btn">
搜索:
<el-input @input="getStreamProxyList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
流媒体:
<el-select size="mini" @change="getStreamProxyList" style="margin-right: 1rem;" v-model="mediaServerId"
placeholder="请选择" default-first-option>
<el-option label="全部" value=""></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
拉流状态:
<el-select size="mini" style="margin-right: 1rem;" @change="getStreamProxyList" v-model="pulling" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="正在拉流" value="true"></el-option>
<el-option label="尚未拉流" value="false"></el-option>
</el-select>
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button>
<el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">搜索ONVIF</el-button>
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
</div>
</div>
<devicePlayer ref="devicePlayer"></devicePlayer>
<el-table size="medium" :data="streamProxyList" style="width: 100%" :height="$tableHeght" >
<el-table-column prop="app" label="流应用名" min-width="120" show-overflow-tooltip/>
<el-table-column prop="stream" label="流ID" min-width="120" show-overflow-tooltip/>
<el-table-column label="流地址" min-width="250" show-overflow-tooltip >
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium">
<i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="scope.row.srcUrl" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
{{scope.row.srcUrl}}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="mediaServerId" label="流媒体" min-width="180" ></el-table-column>
<el-table-column label="代理方式" width="100" >
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
{{scope.row.type === "default"? "默认":"FFMPEG代理"}}
</div>
</template>
</el-table-column>
<el-table-column prop="gbDeviceId" label="国标编码" min-width="180" show-overflow-tooltip/>
<el-table-column label="拉流状态" min-width="120" >
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.pulling && Vue.prototype.$myServerId !== scope.row.serverId" style="border-color: #ecf1af">正在拉流</el-tag>
<el-tag size="medium" v-if="scope.row.pulling && Vue.prototype.$myServerId === scope.row.serverId">正在拉流</el-tag>
<el-tag size="medium" type="info" v-if="!scope.row.pulling">尚未拉流</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="启用" min-width="120" >
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.enable && Vue.prototype.$myServerId !== scope.row.serverId" style="border-color: #ecf1af">已启用</el-tag>
<el-tag size="medium" v-if="scope.row.enable && Vue.prototype.$myServerId === scope.row.serverId">已启用</el-tag>
<el-tag size="medium" type="info" v-if="!scope.row.enable">未启用</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="150" show-overflow-tooltip/>
<el-table-column label="操作" width="400" fixed="right">
<template v-slot:default="scope">
<el-button size="medium" :loading="scope.row.playLoading" icon="el-icon-video-play" type="text" @click="play(scope.row)">播放</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-switch-button" style="color: #f56c6c" type="text" v-if="scope.row.pulling" @click="stopPlay(scope.row)">停止</el-button>
<el-divider direction="vertical" v-if="scope.row.pulling" ></el-divider>
<el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">
编辑
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-cloudy" type="text" @click="queryCloudRecords(scope.row)">云端录像</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c" @click="deleteStreamProxy(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
<streamProxyEdit ref="streamProxyEdit" ></streamProxyEdit>
<onvifEdit ref="onvifEdit" ></onvifEdit>
<StreamProxyEdit v-if="streamProxy" v-model="streamProxy" :closeEdit="closeEdit" ></StreamProxyEdit>
</div>
</template>
<script>
import streamProxyEdit from './dialog/StreamProxyEdit.vue'
import onvifEdit from './dialog/onvifEdit.vue'
import devicePlayer from './dialog/devicePlayer.vue'
import uiHeader from '../layout/UiHeader.vue'
import StreamProxyEdit from "./StreamProxyEdit";
import MediaServer from "./service/MediaServer";
import Vue from "vue";
export default {
name: 'streamProxyList',
components: {
devicePlayer,
streamProxyEdit,
onvifEdit,
StreamProxyEdit,
uiHeader
},
data() {
return {
streamProxyList: [],
currentPusher: {}, //
updateLooper: 0, //
currentDeviceChannelsLenth:0,
currentPage:1,
count:15,
total:0,
streamProxy: null,
searchSrt: "",
mediaServerId: "",
pulling: "",
mediaServerObj: new MediaServer(),
mediaServerList: [],
};
},
computed: {
Vue() {
return Vue
},
},
mounted() {
this.initData();
this.startUpdateList()
},
destroyed() {
this.$destroy('videojs');
clearTimeout(this.updateLooper);
},
methods: {
initData: function() {
this.getStreamProxyList();
this.mediaServerObj.getOnlineMediaServerList((data) => {
this.mediaServerList = data.data;
})
},
startUpdateList: function (){
this.updateLooper = setInterval(()=>{
if (!this.streamProxy) {
this.getStreamProxyList()
}
}, 1000);
},
currentChange: function(val){
this.currentPage = val;
this.getStreamProxyList();
},
handleSizeChange: function(val){
this.count = val;
this.getStreamProxyList();
},
getStreamProxyList: function() {
let that = this;
this.$axios({
method: 'get',
url:`/api/proxy/list`,
params: {
page: that.currentPage,
count: that.count,
query: this.searchSrt,
pulling: this.pulling,
mediaServerId: this.mediaServerId,
}
}).then(function (res) {
if (res.data.code === 0) {
that.total = res.data.data.total;
for (let i = 0; i < res.data.data.list.length; i++) {
res.data.data.list[i]["playLoading"] = false;
}
that.streamProxyList = res.data.data.list;
}
}).catch(function (error) {
console.log(error);
});
},
addStreamProxy: function(){
// this.$refs.streamProxyEdit.openDialog(null, this.initData)
this.streamProxy = {
type: "default",
dataType: 3,
noneReader: 1,
enable: true,
enableAudio: true,
mediaServerId: "",
timeout: 10,
}
},
addOnvif: function(){
this.$axios({
method: 'get',
url:`/api/onvif/search?timeout=3000`,
}).then((res) =>{
if (res.data.code === 0 ){
if (res.data.data.length > 0) {
this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{
if (url != null) {
this.$refs.onvifEdit.close();
this.$refs.streamProxyEdit.openDialog({type: "default", url: url, srcUrl: url}, this.initData())
}
})
}else {
this.$message.success({
showClose: true,
message: "未找到可用设备"
});
}
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error)=> {
this.$message.error({
showClose: true,
message: error
})
});
},
edit: function(row){
if (row.enableDisableNoneReader) {
this.$set(row, "noneReader", 1)
}else if (row.enableRemoveNoneReader) {
this.$set(row, "noneReader", 2)
}else {
this.$set(row, "noneReader", 0)
}
this.streamProxy = row
this.$set(this.streamProxy, "rtspType", row.rtspType)
},
closeEdit: function(row){
this.streamProxy = null
},
play: function(row){
row.playLoading = true;
this.$axios({
method: 'get',
url:`/api/proxy/start`,
params: {
id: row.id,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$refs.devicePlayer.openDialog("streamPlay", null, null, {
streamInfo: res.data.data,
hasAudio: true
});
}else {
this.$message({
showClose: true,
message: "获取地址失败:" + res.data.msg,
type: "error",
});
}
}).catch(function (error) {
console.log(error);
}).finally(()=>{
row.playLoading = false;
})
},
stopPlay: function(row){
let that = this;
this.$axios({
method: 'get',
url:`/api/proxy/stop`,
params: {
id: row.id,
}
}).then(function (res) {
if (res.data.code === 0) {
}else {
that.$message.error(res.data.msg);
}
}).catch(function (error) {
console.log(error);
});
},
queryCloudRecords: function (row) {
this.$router.push(`/cloudRecordDetail/${row.app}/${row.stream}`)
},
deleteStreamProxy: function(row){
this.$confirm('确定删除此代理吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios({
method:"delete",
url:"/api/proxy/delete",
params:{
id: row.id,
}
}).then((res)=>{
this.$message.success({
showClose: true,
message: "删除成功"
})
this.initData()
}).catch((error) =>{
console.log(error);
});
}).catch(() => {
});
},
refresh: function (){
this.initData();
}
}
};
</script>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
.cpoy-btn {
cursor: pointer;
margin-right: 10px;
}
</style>

View File

@ -1,136 +0,0 @@
<template>
<div id="ChannelEdit" style="width: 100%">
<div class="page-header">
<div class="page-title">
<el-page-header @back="close" content="编辑推流信息"></el-page-header>
</div>
<div class="page-header-btn">
<div style="display: inline;">
<el-button icon="el-icon-close" size="mini" style="font-size: 20px; color: #000;" type="text" @click="close" ></el-button>
</div>
</div>
</div>
<el-tabs tab-position="left">
<el-tab-pane label="推流信息编辑" style="background-color: #FFFFFF; padding: 1rem">
<el-divider content-position="center">基础信息</el-divider>
<el-form ref="streamPushForm" status-icon label-width="160px" class="channel-form" v-loading="locading">
<el-form-item label="应用名" >
<el-input v-model="streamPush.app" placeholder="请输入应用名"></el-input>
</el-form-item>
<el-form-item label="流ID" >
<el-input v-model="streamPush.stream" placeholder="请输入流ID"></el-input>
</el-form-item>
</el-form>
<el-divider content-position="center">策略</el-divider>
<el-form ref="streamPushForm" status-icon label-width="160px" v-loading="locading">
<el-form-item style="text-align: left">
<el-checkbox v-model="streamPush.startOfflinePush">拉起离线推流</el-checkbox>
</el-form-item>
</el-form>
<el-form style="text-align: right">
<el-form-item >
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="国标通道配置" v-if="streamPush.id">
<CommonChannelEdit ref="commonChannelEdit" :dataForm="streamPush" :cancel="close"></CommonChannelEdit>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import CommonChannelEdit from './common/CommonChannelEdit'
export default {
name: "channelEdit",
props: [ 'streamPush', 'closeEdit'],
components: {
CommonChannelEdit,
},
created() {
console.log(this.streamPush)
},
data() {
return {
locading: false,
};
},
methods: {
onSubmit: function () {
console.log(this.streamPush)
this.locading = true
if (this.streamPush.id) {
this.$axios({
method: 'post',
url: "/api/push/update",
data: this.streamPush
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: '保存成功',
});
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
}).finally(()=>{
this.locading = false
})
}else {
this.$axios({
method: 'post',
url: "/api/push/add",
data: this.streamPush
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: '保存成功',
});
this.streamPush = res.data.data
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
}).finally(()=>{
this.locading = false
})
}
},
close: function () {
this.closeEdit()
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,384 +0,0 @@
<template>
<div id="streamPushList" style="width: 100%">
<div v-if="!streamPush">
<div class="page-header">
<div class="page-title">推流列表</div>
<div class="page-header-btn">
搜索:
<el-input @input="getPushList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
流媒体:
<el-select size="mini" @change="getPushList" style="margin-right: 1rem;" v-model="mediaServerId"
placeholder="请选择" default-first-option>
<el-option label="全部" value=""></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
推流状态:
<el-select size="mini" style="margin-right: 1rem;" @change="getPushList" v-model="pushing" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="推流中" value="true"></el-option>
<el-option label="已停止" value="false"></el-option>
</el-select>
<el-button icon="el-icon-upload2" size="mini" style="margin-right: 1rem;" type="primary" @click="importChannel">
通道导入
</el-button>
<el-button icon="el-icon-download" size="mini" style="margin-right: 1rem;" type="primary">
<a style="color: #FFFFFF; text-align: center; text-decoration: none" href="/static/file/推流通道导入.zip"
download='推流通道导入.zip'>下载模板</a>
</el-button>
<el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;"
:disabled="multipleSelection.length === 0" type="danger" @click="batchDel">移除
</el-button>
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStream">添加
</el-button>
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
</div>
</div>
<el-table size="medium" ref="pushListTable" :data="pushList" style="width: 100%" :height="$tableHeght" :loading="loading"
@selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream">
<el-table-column type="selection" :reserve-selection="true" min-width="55">
</el-table-column>
<el-table-column prop="gbName" label="名称" min-width="200">
</el-table-column>
<el-table-column prop="app" label="应用名" min-width="100">
</el-table-column>
<el-table-column prop="stream" label="流ID" min-width="200">
</el-table-column>
<el-table-column label="推流状态" min-width="100">
<template v-slot:default="scope">
<el-tag size="medium" v-if="scope.row.pushing && Vue.prototype.$myServerId !== scope.row.serverId" style="border-color: #ecf1af">推流中</el-tag>
<el-tag size="medium" v-if="scope.row.pushing && Vue.prototype.$myServerId === scope.row.serverId">推流中</el-tag>
<el-tag size="medium" type="info" v-if="!scope.row.pushing">已停止</el-tag>
</template>
</el-table-column>
<el-table-column prop="gbDeviceId" label="国标编码" min-width="200" >
</el-table-column>
<el-table-column label="位置信息" min-width="200">
<template v-slot:default="scope">
<span size="medium" v-if="scope.row.gbLongitude && scope.row.gbLatitude">{{scope.row.gbLongitude}}<br/>{{scope.row.gbLatitude}}</span>
<span size="medium" v-if="!scope.row.gbLongitude || !scope.row.gbLatitude"></span>
</template>
</el-table-column>
<el-table-column prop="mediaServerId" label="流媒体" min-width="200" >
</el-table-column>
<el-table-column label="开始时间" min-width="200">
<template v-slot:default="scope">
<el-button-group>
{{ scope.row.pushTime == null? "-":scope.row.pushTime }}
</el-button-group>
</template>
</el-table-column>
<el-table-column label="操作" min-width="360" fixed="right">
<template v-slot:default="scope">
<el-button size="medium" :loading="scope.row.playLoading" icon="el-icon-video-play" @click="playPush(scope.row)" type="text">播放
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-delete" type="text" @click="deletePush(scope.row.id)" style="color: #f56c6c" >删除</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-position" type="text" @click="edit(scope.row)">
编辑
</el-button>
<el-button size="medium" icon="el-icon-cloudy" type="text" @click="queryCloudRecords(scope.row)">云端录像
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
<devicePlayer ref="devicePlayer"></devicePlayer>
<addStreamTOGB ref="addStreamTOGB"></addStreamTOGB>
<importChannel ref="importChannel"></importChannel>
<stream-push-edit v-if="streamPush" :streamPush="streamPush" :closeEdit="closeEdit"></stream-push-edit>
</div>
</template>
<script>
import devicePlayer from './dialog/devicePlayer.vue'
import addStreamTOGB from './dialog/pushStreamEdit.vue'
import uiHeader from '../layout/UiHeader.vue'
import importChannel from './dialog/importChannel.vue'
import MediaServer from './service/MediaServer'
import StreamPushEdit from "./StreamPushEdit";
import ChannelEdit from "./ChannelEdit.vue";
import Vue from "vue";
export default {
name: 'streamPushList',
components: {
ChannelEdit,
StreamPushEdit,
devicePlayer,
addStreamTOGB,
uiHeader,
importChannel,
},
data() {
return {
pushList: [], //
currentPusher: {}, //
updateLooper: 0, //
currentDeviceChannelsLenth: 0,
mediaServerObj: new MediaServer(),
currentPage: 1,
count: 15,
total: 0,
searchSrt: "",
pushing: "",
mediaServerId: "",
mediaServerList: [],
multipleSelection: [],
loading: false,
streamPush: null,
};
},
computed: {
Vue() {
return Vue
},
},
mounted() {
this.initData();
this.updateLooper = setInterval(this.getPushList, 2000);
},
destroyed() {
clearTimeout(this.updateLooper);
},
methods: {
initData: function () {
this.loading = true;
this.mediaServerObj.getOnlineMediaServerList((data) => {
this.mediaServerList = data.data;
})
this.getPushList();
},
currentChange: function (val) {
this.currentPage = val;
this.getPushList();
},
handleSizeChange: function (val) {
this.count = val;
this.getPushList();
},
getPushList: function () {
let that = this;
this.$axios({
method: 'get',
url: `/api/push/list`,
params: {
page: that.currentPage,
count: that.count,
query: that.searchSrt,
pushing: that.pushing,
mediaServerId: that.mediaServerId,
}
}).then(function (res) {
if (res.data.code === 0) {
that.total = res.data.data.total;
that.pushList = res.data.data.list;
that.pushList.forEach(e => {
that.$set(e, "location", "");
that.$set(e, "playLoading", false);
if (e.gbLongitude && e.gbLatitude) {
that.$set(e, "location", e.gbLongitude + "," + e.gbLatitude);
}
});
}
}).catch(function (error) {
console.error(error);
}).finally(()=>{
this.loading = false;
})
},
playPush: function (row) {
row.playLoading = true;
this.$axios({
method: 'get',
url: '/api/push/start',
params: {
id: row.id
}
}).then((res) =>{
if (res.data.code === 0 ) {
this.$refs.devicePlayer.openDialog("streamPlay", null, null, {
streamInfo: res.data.data,
hasAudio: true
});
}else {
this.$message.error(res.data.msg);
}
}).catch(function (error) {
console.error(error);
}).finally(()=>{
row.playLoading = false;
})
},
deletePush: function (id) {
this.$confirm(`确定删除通道?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.loading = true;
this.$axios({
method: "post",
url: "/api/push/remove",
params: {
id: id,
}
}).then((res) => {
if (res.data.code === 0) {
this.initData()
}
}).catch(function (error) {
console.error(error);
});
}).catch(() => {
});
},
edit: function (row) {
this.streamPush = row
},
//
closeEdit: function (){
this.streamPush = null
this.getPushList()
},
removeFromGB: function (row) {
let that = this;
that.$axios({
method: "delete",
url: "/api/push/remove_form_gb",
data: row
}).then((res) => {
if (res.data.code === 0) {
that.initData()
}
}).catch(function (error) {
console.error(error);
});
},
queryCloudRecords: function (row) {
this.$router.push(`/cloudRecordDetail/${row.app}/${row.stream}`)
},
importChannel: function () {
this.$refs.importChannel.openDialog(() => {
})
},
addStream: function (){
// this.$refs.addStreamTOGB.openDialog(null, this.initData);
this.streamPush = {}
},
batchDel: function () {
this.$confirm(`确定删除选中的${this.multipleSelection.length}个通道?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let ids = []
for (let i = 0; i < this.multipleSelection.length; i++) {
ids.push(this.multipleSelection[i].id)
}
let that = this;
that.$axios({
method: "delete",
url: "/api/push/batchRemove",
data: {
ids: ids
}
}).then((res) => {
this.initData();
this.$refs.pushListTable.clearSelection();
}).catch(function (error) {
console.error(error);
});
}).catch(() => {
});
},
handleSelectionChange: function (val) {
this.multipleSelection = val;
},
refresh: function () {
this.initData();
},
}
};
</script>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
</style>

View File

@ -1,303 +0,0 @@
<template>
<div id="app" style="width: 100%">
<div class="page-header" style="margin-bottom: 0">
<div class="page-title">
<el-page-header @back="goBack" content="ApiKey列表"></el-page-header>
</div>
<div class="page-header-btn">
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addUserApiKey">
添加ApiKey
</el-button>
</div>
</div>
<!--ApiKey列表-->
<el-table size="medium" :data="userList" style="width: 100%;font-size: 12px;" :height="winHeight"
header-row-class-name="table-header">
<el-table-column prop="user.username" label="用户名" min-width="120"/>
<el-table-column prop="app" label="应用名" min-width="160"/>
<el-table-column label="ApiKey" :show-overflow-tooltip="true" min-width="300">
<template #default="scope">
<!-- <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="scope.row.apiKey" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>-->
<i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="scope.row.apiKey" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
<span>{{scope.row.apiKey}}</span>
</template>
</el-table-column>
<el-table-column prop="enable" label="启用" width="120">
<template #default="scope">
<el-tag v-if="scope.row.enable">
启用
</el-tag>
<el-tag v-else type="info">
停用
</el-tag>
</template>
</el-table-column>
<el-table-column prop="expiredAt" label="过期时间" width="160"/>
<el-table-column prop="remark" label="备注信息" min-width="160"/>
<el-table-column label="操作" min-width="260" fixed="right">
<template #default="scope">
<el-button v-if="scope.row.enable"
size="medium" icon="el-icon-circle-close" type="text" @click="disableUserApiKey(scope.row)">
停用
</el-button>
<el-button v-else
size="medium" icon="el-icon-circle-check" type="text" @click="enableUserApiKey(scope.row)">
启用
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-refresh" type="text" @click="resetUserApiKey(scope.row)">
重置
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-edit" type="text" @click="remarkUserApiKey(scope.row)">
备注
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-delete" type="text" @click="deleteUserApiKey(scope.row)"
style="color: #f56c6c">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<addUserApiKey ref="addUserApiKey"></addUserApiKey>
<remarkUserApiKey ref="remarkUserApiKey"></remarkUserApiKey>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import addUserApiKey from "./dialog/addUserApiKey.vue";
import remarkUserApiKey from './dialog/remarkUserApiKey.vue'
export default {
name: 'userApiKeyManager',
components: {
uiHeader,
addUserApiKey,
remarkUserApiKey
},
data() {
return {
userList: [], //
currentUser: {}, //
winHeight: window.innerHeight - 200,
currentPage: 1,
count: 15,
total: 0,
getUserApiKeyListLoading: false
};
},
mounted() {
this.initParam();
this.initData();
},
methods: {
goBack() {
this.$router.back()
},
initParam() {
this.userId = this.$route.params.userId;
},
initData() {
this.getUserApiKeyList();
},
currentChange(val) {
this.currentPage = val;
this.getUserApiKeyList();
},
handleSizeChange(val) {
this.count = val;
this.getUserApiKeyList();
},
getUserApiKeyList() {
let that = this;
this.getUserApiKeyListLoading = true;
this.$axios({
method: 'get',
url: `/api/userApiKey/userApiKeys`,
params: {
page: that.currentPage,
count: that.count
}
}).then((res) => {
if (res.data.code === 0) {
that.total = res.data.data.total;
that.userList = res.data.data.list;
}
that.getUserApiKeyListLoading = false;
}).catch((error) => {
that.getUserApiKeyListLoading = false;
});
},
addUserApiKey() {
this.$refs.addUserApiKey.openDialog(this.userId, () => {
this.$refs.addUserApiKey.close();
this.$message({
showClose: true,
message: "ApiKey添加成功",
type: "success",
});
setTimeout(this.getUserApiKeyList, 200)
})
},
remarkUserApiKey(row) {
this.$refs.remarkUserApiKey.openDialog(row.id, () => {
this.$refs.remarkUserApiKey.close();
this.$message({
showClose: true,
message: "备注修改成功",
type: "success",
});
setTimeout(this.getUserApiKeyList, 200)
})
},
enableUserApiKey(row) {
let msg = "确定启用此ApiKey"
if (row.online !== 0) {
msg = "<strong>确定启用此ApiKey</strong>"
}
this.$confirm(msg, '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
center: true,
type: 'warning'
}).then(() => {
this.$axios({
method: 'post',
url: `/api/userApiKey/enable?id=${row.id}`
}).then((res) => {
this.$message({
showClose: true,
message: '启用成功',
type: 'success'
});
this.getUserApiKeyList();
}).catch((error) => {
this.$message({
showClose: true,
message: '启用失败',
type: 'error'
});
console.error(error);
});
}).catch(() => {
});
},
disableUserApiKey(row) {
let msg = "确定停用此ApiKey"
if (row.online !== 0) {
msg = "<strong>确定停用此ApiKey</strong>"
}
this.$confirm(msg, '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
center: true,
type: 'warning'
}).then(() => {
this.$axios({
method: 'post',
url: `/api/userApiKey/disable?id=${row.id}`
}).then((res) => {
this.$message({
showClose: true,
message: '停用成功',
type: 'success'
});
this.getUserApiKeyList();
}).catch((error) => {
this.$message({
showClose: true,
message: '停用失败',
type: 'error'
});
console.error(error);
});
}).catch(() => {
});
},
resetUserApiKey(row) {
let msg = "确定重置此ApiKey"
if (row.online !== 0) {
msg = "<strong>确定重置此ApiKey</strong>"
}
this.$confirm(msg, '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
center: true,
type: 'warning'
}).then(() => {
this.$axios({
method: 'post',
url: `/api/userApiKey/reset?id=${row.id}`
}).then((res) => {
this.$message({
showClose: true,
message: '重置成功',
type: 'success'
});
this.getUserApiKeyList();
}).catch((error) => {
this.$message({
showClose: true,
message: '重置失败',
type: 'error'
});
console.error(error);
});
}).catch(() => {
});
},
deleteUserApiKey(row) {
let msg = "确定删除此ApiKey"
if (row.online !== 0) {
msg = "<strong>确定删除此ApiKey</strong>"
}
this.$confirm(msg, '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
center: true,
type: 'warning'
}).then(() => {
this.$axios({
method: 'delete',
url: `/api/userApiKey/delete?id=${row.id}`
}).then((res) => {
this.$message({
showClose: true,
message: '删除成功',
type: 'success'
});
this.getUserApiKeyList();
}).catch((error) => {
this.$message({
showClose: true,
message: '删除失败',
type: 'error'
});
console.error(error);
});
}).catch(() => {
});
},
}
}
</script>
<style>
</style>

View File

@ -1,241 +0,0 @@
<template>
<div id="app" style="width: 100%">
<div class="page-header">
<div class="page-title">用户列表</div>
<div class="page-header-btn">
<el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addUser">
添加用户
</el-button>
</div>
</div>
<!--用户列表-->
<el-table size="medium" :data="userList" style="width: 100%;font-size: 12px;" :height="$tableHeght"
header-row-class-name="table-header">
<el-table-column prop="username" label="用户名" min-width="160"/>
<el-table-column prop="pushKey" label="pushkey" min-width="160"/>
<el-table-column prop="role.name" label="类型" min-width="160"/>
<el-table-column label="操作" min-width="450" fixed="right">
<template v-slot:default="scope">
<el-button size="medium" icon="el-icon-edit" type="text" @click="edit(scope.row)">修改密码</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-edit" type="text" @click="changePushKey(scope.row)">修改pushkey</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-edit" type="text" @click="showUserApiKeyManager(scope.row)">管理ApiKey</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-delete" type="text" @click="deleteUser(scope.row)"
style="color: #f56c6c">删除
</el-button>
</template>
</el-table-column>
</el-table>
<changePasswordForAdmin ref="changePasswordForAdmin"></changePasswordForAdmin>
<changePushKey ref="changePushKey"></changePushKey>
<addUser ref="addUser"></addUser>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import changePasswordForAdmin from './dialog/changePasswordForAdmin.vue'
import changePushKey from './dialog/changePushKey.vue'
import addUser from '../components/dialog/addUser.vue'
export default {
name: 'userManager',
components: {
uiHeader,
changePasswordForAdmin,
changePushKey,
addUser
},
data() {
return {
userList: [], //
currentUser: {}, //
videoComponentList: [],
updateLooper: 0, //
currentUserLenth: 0,
currentPage: 1,
count: 15,
total: 0,
getUserListLoading: false
};
},
mounted() {
this.initData();
this.updateLooper = setInterval(this.initData, 10000);
},
destroyed() {
this.$destroy('videojs');
clearTimeout(this.updateLooper);
},
methods: {
initData: function () {
this.getUserList();
},
currentChange: function (val) {
this.currentPage = val;
this.getUserList();
},
handleSizeChange: function (val) {
this.count = val;
this.getUserList();
},
getUserList: function () {
let that = this;
this.getUserListLoading = true;
this.$axios({
method: 'get',
url: `/api/user/users`,
params: {
page: that.currentPage,
count: that.count
}
}).then(function (res) {
if (res.data.code === 0) {
that.total = res.data.data.total;
that.userList = res.data.data.list;
}
that.getUserListLoading = false;
}).catch(function (error) {
that.getUserListLoading = false;
});
},
edit: function (row) {
this.$refs.changePasswordForAdmin.openDialog(row, () => {
this.$refs.changePasswordForAdmin.close();
this.$message({
showClose: true,
message: "密码修改成功",
type: "success",
});
setTimeout(this.getUserList, 200)
})
},
deleteUser: function (row) {
let msg = "确定删除此用户?"
if (row.online !== 0) {
msg = "<strong>确定删除此用户?</strong>"
}
this.$confirm(msg, '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
center: true,
type: 'warning'
}).then(() => {
this.$axios({
method: 'delete',
url: `/api/user/delete?id=${row.id}`
}).then((res) => {
this.getUserList();
}).catch((error) => {
console.error(error);
});
}).catch(() => {
});
},
changePushKey: function (row) {
this.$refs.changePushKey.openDialog(row, () => {
this.$refs.changePushKey.close();
this.$message({
showClose: true,
message: "pushKey修改成功",
type: "success",
});
setTimeout(this.getUserList, 200)
})
},
addUser: function () {
// this.$refs.addUser.openDialog()
this.$refs.addUser.openDialog( () => {
this.$refs.addUser.close();
this.$message({
showClose: true,
message: "用户添加成功",
type: "success",
});
setTimeout(this.getUserList, 200)
})
},
showUserApiKeyManager: function (row) {
this.$router.push(`/userApiKeyManager/${row.id}`)
},
}
}
</script>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
</style>

View File

@ -1,669 +0,0 @@
<template>
<div id="channelList" style="width: 100%">
<div v-if="!editId">
<div class="page-header">
<div class="page-title">
<el-page-header @back="showDevice" content="通道列表"></el-page-header>
</div>
<div class="page-header-btn">
<div v-if="!showTree" style="display: inline;">
搜索:
<el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
通道类型:
<el-select size="mini" @change="search" style="width: 8rem; margin-right: 1rem;" v-model="channelType" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="设备" value="false"></el-option>
<el-option label="子目录" value="true"></el-option>
</el-select>
在线状态:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="search" v-model="online" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
码流类型重置:
<el-select size="mini" style="width: 16rem; margin-right: 1rem;" @change="subStreamChange" v-model="subStream"
placeholder="请选择码流类型" default-first-option >
<el-option label="stream:0(主码流)" value="stream:0"></el-option>
<el-option label="stream:1(子码流)" value="stream:1"></el-option>
<el-option label="streamnumber:0(主码流-2022)" value="streamnumber:0"></el-option>
<el-option label="streamnumber:1(子码流-2022)" value="streamnumber:1"></el-option>
<el-option label="streamprofile:0(主码流-大华)" value="streamprofile:0"></el-option>
<el-option label="streamprofile:1(子码流-大华)" value="streamprofile:1"></el-option>
<el-option label="streamMode:main(主码流-水星+TP-LINK)" value="streamMode:main"></el-option>
<el-option label="streamMode:sub(子码流-水星+TP-LINK)" value="streamMode:sub"></el-option>
</el-select>
</div>
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
</div>
</div>
<el-table size="medium" ref="channelListTable" :data="deviceChannelList" :height="$tableHeght"
header-row-class-name="table-header">
<el-table-column prop="name" label="名称" min-width="180">
</el-table-column>
<el-table-column prop="deviceId" label="编号" min-width="180">
</el-table-column>
<el-table-column label="快照" min-width="100">
<template v-slot:default="scope">
<el-image
:src="getSnap(scope.row)"
:preview-src-list="getBigSnap(scope.row)"
@error="getSnapErrorEvent(scope.row.deviceId, scope.row.channelId)"
:fit="'contain'"
style="width: 60px">
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</template>
</el-table-column>
<!-- <el-table-column prop="subCount" label="子节点数" min-width="100">-->
<!-- </el-table-column>-->
<el-table-column prop="manufacturer" label="厂家" min-width="100">
</el-table-column>
<el-table-column label="位置信息" min-width="150">
<template v-slot:default="scope">
<span size="medium" v-if="scope.row.longitude && scope.row.latitude">{{scope.row.longitude}}<br/>{{scope.row.latitude}}</span>
<span size="medium" v-if="!scope.row.longitude || !scope.row.latitude"></span>
</template>
</el-table-column>
<el-table-column prop="ptzType" label="云台类型" min-width="100">
<template v-slot:default="scope">
<div >{{ scope.row.ptzTypeText }}</div>
</template>
</el-table-column>
<el-table-column label="开启音频" min-width="100">
<template v-slot:default="scope">
<el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF">
</el-switch>
</template>
</el-table-column>
<el-table-column label="码流类型" min-width="180">
<template v-slot:default="scope">
<el-select size="mini" style="margin-right: 1rem;" @change="channelSubStreamChange(scope.row)" v-model="scope.row.streamIdentification"
placeholder="请选择码流类型" default-first-option >
<el-option label="stream:0(主码流)" value="stream:0"></el-option>
<el-option label="stream:1(子码流)" value="stream:1"></el-option>
<el-option label="streamnumber:0(主码流-2022)" value="streamnumber:0"></el-option>
<el-option label="streamnumber:1(子码流-2022)" value="streamnumber:1"></el-option>
<el-option label="streamprofile:0(主码流-大华)" value="streamprofile:0"></el-option>
<el-option label="streamprofile:1(子码流-大华)" value="streamprofile:1"></el-option>
<el-option label="streamMode:main(主码流-水星+TP-LINK)" value="streamMode:main"></el-option>
<el-option label="streamMode:sub(子码流-水星+TP-LINK)" value="streamMode:sub"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.status === 'ON'">在线</el-tag>
<el-tag size="medium" type="info" v-if="scope.row.status !== 'ON'">离线</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="操作" min-width="340" fixed="right">
<template v-slot:default="scope">
<el-button size="medium" v-bind:disabled="device == null || device.online === 0" icon="el-icon-video-play"
type="text" :loading="scope.row.playLoading" @click="sendDevicePush(scope.row)">播放
</el-button>
<el-button size="medium" v-bind:disabled="device == null || device.online === 0"
icon="el-icon-switch-button"
type="text" style="color: #f56c6c" v-if="!!scope.row.streamId"
@click="stopDevicePush(scope.row)">停止
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button
size="medium"
type="text"
icon="el-icon-edit"
@click="handleEdit(scope.row)"
>
编辑
</el-button>
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-s-open" type="text"
v-if="scope.row.subCount > 0 || scope.row.parental === 1 || scope.row.deviceId.length <= 8"
@click="changeSubchannel(scope.row)">查看
</el-button>
<el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1 || scope.row.deviceId.length <= 8" direction="vertical"></el-divider>
<el-dropdown @command="(command)=>{moreClick(command, scope.row)}">
<el-button size="medium" type="text" >
更多<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu>
<el-dropdown-item command="records" v-bind:disabled="device == null || device.online === 0">
设备录像</el-dropdown-item>
<el-dropdown-item command="cloudRecords" v-bind:disabled="device == null || device.online === 0" >
云端录像</el-dropdown-item>
<el-dropdown-item command="record" v-bind:disabled="device == null || device.online === 0" >
设备录像控制-开始</el-dropdown-item>
<el-dropdown-item command="stopRecord" v-bind:disabled="device == null || device.online === 0" >
设备录像控制-停止</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
<devicePlayer ref="devicePlayer"></devicePlayer>
<channel-edit v-if="editId" :id="editId" :closeEdit="closeEdit"></channel-edit>
</div>
</template>
<script>
import devicePlayer from './dialog/devicePlayer.vue'
import uiHeader from '../layout/UiHeader.vue'
import DeviceService from "./service/DeviceService";
import DeviceTree from "./common/DeviceTree";
import ChannelEdit from "./ChannelEdit";
export default {
name: 'channelList',
components: {
devicePlayer,
uiHeader,
DeviceTree,
ChannelEdit,
},
data() {
return {
deviceService: new DeviceService(),
device: null,
deviceId: this.$route.params.deviceId,
parentChannelId: this.$route.params.parentChannelId,
deviceChannelList: [],
videoComponentList: [],
currentPlayerInfo: {}, //
updateLooper: 0, //
searchSrt: "",
channelType: "",
online: "",
subStream: "",
winHeight: window.innerHeight - 200,
currentPage: 1,
count: 15,
total: 0,
beforeUrl: "/deviceList",
showTree: false,
editId: null,
loadSnap: {},
ptzTypes: {
0: "未知",
1: "球机",
2: "半球",
3: "固定枪机",
4: "遥控枪机"
}
};
},
mounted() {
if (this.deviceId) {
this.deviceService.getDevice(this.deviceId, (result) => {
this.device = result;
}, (error) => {
console.log("获取设备信息失败")
console.error(error)
})
}
this.initData();
},
destroyed() {
this.$destroy('videojs');
clearTimeout(this.updateLooper);
},
methods: {
initData: function () {
if (typeof (this.parentChannelId) == "undefined" || this.parentChannelId == 0) {
this.getDeviceChannelList();
} else {
this.showSubchannels();
}
},
initParam: function () {
this.deviceId = this.$route.params.deviceId;
this.parentChannelId = this.$route.params.parentChannelId;
this.currentPage = 1;
this.count = 15;
if (this.parentChannelId == "" || this.parentChannelId == 0) {
this.beforeUrl = "/deviceList"
}
},
currentChange: function (val) {
this.currentPage = val;
this.initData();
},
handleSizeChange: function (val) {
this.count = val;
this.getDeviceChannelList();
},
getDeviceChannelList: function () {
let that = this;
if (typeof (this.$route.params.deviceId) == "undefined") return;
this.$axios({
method: 'get',
url: `/api/device/query/devices/${this.$route.params.deviceId}/channels`,
params: {
page: that.currentPage,
count: that.count,
query: that.searchSrt,
online: that.online,
channelType: that.channelType
}
}).then(function (res) {
if (res.data.code === 0) {
that.total = res.data.data.total;
that.deviceChannelList = res.data.data.list;
that.deviceChannelList.forEach(e => {
e.ptzType = e.ptzType + "";
that.$set(e, "playLoading", false);
});
//
that.$nextTick(() => {
that.$refs.channelListTable.doLayout();
})
}
}).catch(function (error) {
console.log(error);
});
},
//
sendDevicePush: function (itemData) {
let deviceId = this.deviceId;
let channelId = itemData.deviceId;
itemData.playLoading = true;
console.log("通知设备推流1" + deviceId + " : " + channelId);
this.$axios({
method: 'get',
url: '/api/play/start/' + deviceId + '/' + channelId,
params: {
isSubStream: this.isSubStream
}
}).then((res) =>{
console.log(res)
if (res.data.code === 0) {
setTimeout(() => {
let snapId = deviceId + "_" + channelId;
this.loadSnap[deviceId + channelId] = 0;
this.getSnapErrorEvent(snapId)
}, 5000)
itemData.streamId = res.data.data.stream;
this.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
streamInfo: res.data.data,
hasAudio: itemData.hasAudio
});
setTimeout(() => {
this.initData();
}, 1000)
} else {
this.$message.error(res.data.msg);
}
}).catch(function (e) {
console.error(e)
// that.$message.error("");
}).finally(()=>{
itemData.playLoading = false;
})
},
moreClick: function (command, itemData) {
if (command === "records") {
this.queryRecords(itemData)
}else if (command === "cloudRecords") {
this.queryCloudRecords(itemData)
}else if (command === "record") {
this.startRecord(itemData)
}else if (command === "stopRecord") {
this.stopRecord(itemData)
}
},
queryRecords: function (itemData) {
let deviceId = this.deviceId;
let channelId = itemData.deviceId;
this.$router.push(`/gbRecordDetail/${deviceId}/${channelId}`)
},
queryCloudRecords: function (itemData) {
let deviceId = this.deviceId;
let channelId = itemData.deviceId;
this.$router.push(`/cloudRecordDetail/rtp/${deviceId}_${channelId}`)
},
startRecord: function (itemData) {
this.$axios({
method: 'get',
url: `/api/device/control/record`,
params: {
deviceId: this.deviceId,
channelId: itemData.deviceId,
recordCmdStr: "Record"
}
}).then( (res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "开始录像成功"
})
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch( (error)=> {
this.$message.error({
showClose: true,
message: error.message
})
});
},
stopRecord: function (itemData) {
this.$axios({
method: 'get',
url: `/api/device/control/record`,
params: {
deviceId: this.deviceId,
channelId: itemData.deviceId,
recordCmdStr: "StopRecord"
}
}).then( (res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "停止录像成功"
})
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch( (error)=> {
this.$message.error({
showClose: true,
message: error.message
})
});
},
stopDevicePush: function (itemData) {
var that = this;
this.$axios({
method: 'get',
url: '/api/play/stop/' + this.deviceId + "/" + itemData.deviceId,
params: {
isSubStream: this.isSubStream
}
}).then(function (res) {
that.initData();
}).catch(function (error) {
if (error.response.status === 402) { //
that.initData();
} else {
console.log(error)
}
});
},
getSnap: function (row) {
let baseUrl = window.baseUrl ? window.baseUrl : "";
return ((process.env.NODE_ENV === 'development') ? process.env.BASE_API : baseUrl) + '/api/device/query/snap/' + this.deviceId + '/' + row.deviceId;
},
getBigSnap: function (row) {
return [this.getSnap(row)]
},
getSnapErrorEvent: function (deviceId, channelId) {
if (typeof (this.loadSnap[deviceId + channelId]) != "undefined") {
console.log("下载截图" + this.loadSnap[deviceId + channelId])
if (this.loadSnap[deviceId + channelId] > 5) {
delete this.loadSnap[deviceId + channelId];
return;
}
setTimeout(() => {
let url = (process.env.NODE_ENV === 'development' ? "debug" : "") + '/api/device/query/snap/' + deviceId + '/' + channelId
this.loadSnap[deviceId + channelId]++
document.getElementById(deviceId + channelId).setAttribute("src", url + '?' + new Date().getTime())
}, 1000)
}
},
showDevice: function () {
this.$router.push(this.beforeUrl).then(() => {
this.initParam();
this.initData();
})
},
changeSubchannel(itemData) {
this.beforeUrl = this.$router.currentRoute.path;
var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.deviceId}`
this.$router.push(url).then(() => {
this.searchSrt = "";
this.channelType = "";
this.online = "";
this.initParam();
this.initData();
})
},
showSubchannels: function (channelId) {
if (!this.showTree) {
this.$axios({
method: 'get',
url: `/api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`,
params: {
page: this.currentPage,
count: this.count,
query: this.searchSrt,
online: this.online,
channelType: this.channelType
}
}).then((res) => {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.deviceChannelList = res.data.data.list;
this.deviceChannelList.forEach(e => {
e.ptzType = e.ptzType + "";
});
//
this.$nextTick(() => {
this.$refs.channelListTable.doLayout();
})
}
}).catch(function (error) {
console.log(error);
});
} else {
this.$axios({
method: 'get',
url: `/api/device/query/tree/channel/${this.deviceId}`,
params: {
parentId: this.parentChannelId,
page: this.currentPage,
count: this.count,
}
}).then((res) => {
if (res.data.code === 0) {
this.total = res.data.total;
this.deviceChannelList = res.data.list;
//
this.$nextTick(() => {
this.$refs.channelListTable.doLayout();
})
}
}).catch(function (error) {
console.log(error);
});
}
},
search: function () {
this.currentPage = 1;
this.total = 0;
this.initData();
},
updateChannel: function (row) {
this.$axios({
method: 'post',
url: `/api/device/query/channel/audio`,
params: {
channelId: row.id,
audio: row.hasAudio
}
}).then(function (res) {
console.log(JSON.stringify(res));
});
},
subStreamChange: function () {
this.$confirm('确定重置所有通道的码流类型?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios({
method: 'post',
url: `/api/device/query/channel/stream/identification/update/`,
params: {
deviceDbId: this.device.id,
streamIdentification: this.subStream
}
}).then((res)=> {
console.log(JSON.stringify(res));
this.initData()
}).finally(()=>{
this.subStream = ""
})
}).catch(() => {
this.subStream = ""
});
},
channelSubStreamChange: function (row) {
this.$axios({
method: 'post',
url: `/api/device/query/channel/stream/identification/update/`,
params: {
deviceDbId: row.deviceDbId,
id: row.id,
streamIdentification: row.streamIdentification
}
}).then(function (res) {
console.log(JSON.stringify(res));
});
},
refresh: function () {
this.initData();
},
switchTree: function () {
this.showTree = true;
this.deviceChannelList = [];
this.parentChannelId = 0;
this.currentPage = 1;
},
switchList: function () {
this.showTree = false;
this.deviceChannelList = [];
this.parentChannelId = 0;
this.currentPage = 1;
this.initData();
},
treeNodeClickEvent: function (device, data, isCatalog) {
console.log(device)
if (!!!data.channelId) {
this.parentChannelId = device.deviceId;
} else {
this.parentChannelId = data.channelId;
}
this.initData();
},
//
handleEdit(row) {
this.editId = row.id
},
//
closeEdit: function (){
this.editId = null
this.getDeviceChannelList()
}
}
};
</script>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
</style>

View File

@ -1,425 +0,0 @@
<template>
<div id="CommonChannelEdit" v-loading="locading" style="width: 100%">
<el-form ref="passwordForm" status-icon label-width="160px" class="channel-form">
<div class="form-box">
<el-form-item label="名称" >
<el-input v-model="form.gbName" placeholder="请输入通道名称"></el-input>
</el-form-item>
<el-form-item label="编码" >
<el-input v-model="form.gbDeviceId" placeholder="请输入通道编码">
<template v-slot:append>
<el-button @click="buildDeviceIdCode(form.gbDeviceId)">生成</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="设备厂商" >
<el-input v-model="form.gbManufacturer" placeholder="请输入设备厂商"></el-input>
</el-form-item>
<el-form-item label="设备型号" >
<el-input v-model="form.gbModel" placeholder="请输入设备型号"></el-input>
</el-form-item>
<el-form-item label="行政区域" >
<el-input v-model="form.gbCivilCode" placeholder="请输入行政区域">
<template v-slot:append>
<el-button @click="chooseCivilCode()">选择</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="安装地址" >
<el-input v-model="form.gbAddress" placeholder="请输入安装地址"></el-input>
</el-form-item>
<el-form-item label="子设备" >
<el-select v-model="form.gbParental" style="width: 100%" placeholder="请选择是否有子设备">
<el-option label="有" :value="1"></el-option>
<el-option label="无" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item label="父节点编码" >
<el-input v-model="form.gbParentId" placeholder="请输入父节点编码或选择所属虚拟组织">
<template v-slot:append>
<el-button @click="chooseGroup()">选择</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="设备状态" >
<el-select v-model="form.gbStatus" style="width: 100%" placeholder="请选择设备状态">
<el-option label="在线" value="ON"></el-option>
<el-option label="离线" value="OFF"></el-option>
</el-select>
</el-form-item>
<el-form-item label="经度" >
<el-input v-model="form.gbLongitude" placeholder="请输入经度"></el-input>
</el-form-item>
<el-form-item label="纬度" >
<el-input v-model="form.gbLatitude" placeholder="请输入纬度"></el-input>
</el-form-item>
<el-form-item label="云台类型" >
<el-select v-model="form.gbPtzType" style="width: 100%" placeholder="请选择云台类型">
<el-option label="球机" :value="1"></el-option>
<el-option label="半球" :value="2"></el-option>
<el-option label="固定枪机" :value="3"></el-option>
<el-option label="遥控枪机" :value="4"></el-option>
<el-option label="遥控半球" :value="5"></el-option>
<el-option label="多目设备的全景/拼接通道" :value="6"></el-option>
<el-option label="多目设备的分割通道" :value="7"></el-option>
</el-select>
</el-form-item>
</div>
<div>
<el-form-item label="警区" >
<el-input v-model="form.gbBlock" placeholder="请输入警区"></el-input>
</el-form-item>
<el-form-item label="设备归属" >
<el-input v-model="form.gbOwner" placeholder="请输入设备归属"></el-input>
</el-form-item>
<el-form-item label="信令安全模式" >
<el-select v-model="form.gbSafetyWay" style="width: 100%" placeholder="请选择信令安全模式">
<el-option label="不采用" :value="0"></el-option>
<el-option label="S/MIME签名" :value="2"></el-option>
<el-option label="S/MIME加密签名同时采用" :value="3"></el-option>
<el-option label="数字摘要" :value="4"></el-option>
</el-select>
</el-form-item>
<el-form-item label="注册方式" >
<el-select v-model="form.gbRegisterWay" style="width: 100%" placeholder="请选择注册方式">
<el-option label="IETFRFC3261标准" :value="1"></el-option>
<el-option label="基于口令的双向认证" :value="2"></el-option>
<el-option label="基于数字证书的双向认证注册" :value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证书序列号" >
<el-input type="number" v-model="form.gbCertNum" placeholder="请输入证书序列号"></el-input>
</el-form-item>
<el-form-item label="证书有效标识" >
<el-select v-model="form.gbCertifiable" style="width: 100%" placeholder="请选择证书有效标识">
<el-option label="有效" :value="1"></el-option>
<el-option label="无效" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item label="无效原因码" >
<el-input type="errCode" v-model="form.gbCertNum" placeholder="请输入无效原因码"></el-input>
</el-form-item>
<el-form-item label="证书终止有效期" >
<el-date-picker
v-model="form.gbEndTime"
type="datetime"
placeholder="选择日期时间"
style="width: 100%">
</el-date-picker>
</el-form-item>
<el-form-item label="保密属性" >
<el-select v-model="form.gbSecrecy" style="width: 100%" placeholder="请选择保密属性">
<el-option label="不涉密" :value="0"></el-option>
<el-option label="涉密" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="IP地址" >
<el-input v-model="form.gbIpAddress" placeholder="请输入IP地址"></el-input>
</el-form-item>
<el-form-item label="端口" >
<el-input type="number" v-model="form.gbPort" placeholder="请输入端口"></el-input>
</el-form-item>
<el-form-item label="设备口令" >
<el-input v-model="form.gbPassword" placeholder="请输入设备口令"></el-input>
</el-form-item>
</div>
<div>
<el-form-item label="业务分组编号" >
<el-input v-model="form.gbBusinessGroupId" placeholder="请输入业务分组编号"></el-input>
</el-form-item>
<el-form-item label="位置类型" >
<el-select v-model="form.gbPositionType" style="width: 100%" placeholder="请选择位置类型">
<el-option label="省际检查站" :value="1"></el-option>
<el-option label="党政机关" :value="2"></el-option>
<el-option label="车站码头" :value="3"></el-option>
<el-option label="中心广场" :value="4"></el-option>
<el-option label="体育场馆" :value="5"></el-option>
<el-option label="商业中心" :value="6"></el-option>
<el-option label="宗教场所" :value="7"></el-option>
<el-option label="校园周边" :value="8"></el-option>
<el-option label="治安复杂区域" :value="9"></el-option>
<el-option label="交通干线" :value="10"></el-option>
</el-select>
</el-form-item>
<el-form-item label="室外/室内" >
<el-select v-model="form.gbRoomType" style="width: 100%" placeholder="请选择位置类型">
<el-option label="室外" :value="1"></el-option>
<el-option label="室内" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="用途" >
<el-select v-model="form.gbUseType" style="width: 100%" placeholder="请选择位置类型">
<el-option label="治安" :value="1"></el-option>
<el-option label="交通" :value="2"></el-option>
<el-option label="重点" :value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item label="补光" >
<el-select v-model="form.gbSupplyLightType" style="width: 100%" placeholder="请选择位置类型">
<el-option label="无补光" :value="1"></el-option>
<el-option label="红外补光" :value="2"></el-option>
<el-option label="白光补光" :value="3"></el-option>
<el-option label="激光补光" :value="4"></el-option>
<el-option label="其他" :value="9"></el-option>
</el-select>
</el-form-item>
<el-form-item label="监视方位" >
<el-select v-model="form.gbDirectionType" style="width: 100%" placeholder="请选择位置类型">
<el-option label="东(西向东)" :value="1"></el-option>
<el-option label="西(东向西)" :value="2"></el-option>
<el-option label="南(北向南)" :value="3"></el-option>
<el-option label="北(南向北)" :value="4"></el-option>
<el-option label="东南(西北到东南)" :value="5"></el-option>
<el-option label="东北(西南到东北)" :value="6"></el-option>
<el-option label="西南(东北到西南)" :value="7"></el-option>
<el-option label="西北(东南到西北)" :value="8"></el-option>
</el-select>
</el-form-item>
<el-form-item label="分辨率" >
<el-input v-model="form.gbResolution" placeholder="请输入分辨率"></el-input>
</el-form-item>
<el-form-item label="下载倍速" >
<el-select v-model="form.gbDownloadSpeedArray" multiple style="width: 100%" placeholder="请选择位置类型">
<el-option label="1倍速" value="1"></el-option>
<el-option label="2倍速" value="2"></el-option>
<el-option label="4倍速" value="4"></el-option>
<el-option label="8倍速" value="8"></el-option>
<el-option label="16倍速" value="16"></el-option>
</el-select>
</el-form-item>
<el-form-item label="空域编码能力" >
<el-select v-model="form.gbSvcSpaceSupportMod" style="width: 100%" placeholder="请选择空域编码能力">
<el-option label="1级增强" value="1"></el-option>
<el-option label="2级增强" value="2"></el-option>
<el-option label="3级增强" value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item label="时域编码能力" >
<el-select v-model="form.gbSvcTimeSupportMode" style="width: 100%" placeholder="请选择空域编码能力">
<el-option label="1级增强" value="1"></el-option>
<el-option label="2级增强" value="2"></el-option>
<el-option label="3级增强" value="3"></el-option>
</el-select>
</el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button v-if="cancel" @click="cancelSubmit">取消</el-button>
<el-button v-if="form.dataType === 1" @click="reset">重置</el-button>
</div>
</div>
</el-form>
<channelCode ref="channelCode"></channelCode>
<chooseCivilCode ref="chooseCivilCode"></chooseCivilCode>
<chooseGroup ref="chooseGroup"></chooseGroup>
</div>
</template>
<script>
import channelCode from './../dialog/channelCode'
import ChooseCivilCode from "../dialog/chooseCivilCode.vue";
import ChooseGroup from "../dialog/chooseGroup.vue";
export default {
name: "CommonChannelEdit",
props: [ 'id', 'dataForm', 'saveSuccess', 'cancel'],
components: {
ChooseCivilCode,
ChooseGroup,
channelCode,
},
created() {
//
if (this.id) {
this.getCommonChannel()
}else {
if(!this.dataForm.gbDeviceId) {
this.dataForm.gbDeviceId = ""
}
console.log(this.dataForm)
this.form = this.dataForm
}
},
data() {
return {
locading: false,
form: {},
};
},
methods: {
onSubmit: function () {
this.locading = true
if (this.form.gbDownloadSpeedArray) {
this.form.gbDownloadSpeed = this.form.gbDownloadSpeedArray.join("/")
}
if (this.form.gbId) {
this.$axios({
method: 'post',
url: "/api/common/channel/update",
data: this.form
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
});
if (this.saveSuccess) {
this.saveSuccess()
}
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
});
}).finally(()=>{
this.locading = false
})
}else {
this.$axios({
method: 'post',
url: "/api/common/channel/add",
data: this.form
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
});
this.form = res.data.data
if (this.saveSuccess) {
this.saveSuccess()
}
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
});
}).finally(()=>{
this.locading = false
})
}
},
reset: function () {
this.$confirm("确定重置为默认内容?", '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.locading = true
this.$axios({
method: 'post',
url: "/api/common/channel/reset",
params: {
id: this.form.gbId
}
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "重置成功 已保存"
});
this.getCommonChannel()
}
}).catch((error) => {
console.error(error)
}).finally(()=>{
this.locading = false
})
}).catch(() => {
});
},
getCommonChannel:function () {
this.locading = true
this.$axios({
method: 'get',
url: "/api/common/channel/one",
params: {
id: this.id
}
}).then((res) => {
if (res.data.code === 0) {
if (res.data.data.gbDownloadSpeed) {
res.data.data.gbDownloadSpeedArray = res.data.data.gbDownloadSpeed.split("/")
}
this.form = res.data.data;
}
}).catch((error) => {
console.error(error)
}).finally(()=>{
this.locading = false
})
},
buildDeviceIdCode: function (deviceId){
this.$refs.channelCode.openDialog(code=>{
console.log(this.form)
console.log("code===> " + code)
this.form.gbDeviceId = code;
console.log("code22===> " + code)
}, deviceId);
},
chooseCivilCode: function (){
this.$refs.chooseCivilCode.openDialog(code=>{
this.form.gbCivilCode = code;
});
},
chooseGroup: function (){
this.$refs.chooseGroup.openDialog((deviceId, businessGroupId)=>{
this.form.gbBusinessGroupId = businessGroupId;
this.form.gbParentId = deviceId;
});
},
cancelSubmit: function (){
if(this.cancel) {
this.cancel()
}
}
// getDeviceChannel:function (callback) {
// this.$axios({
// method: 'get',
// url: "/api/device/query/channel/raw",
// params: {
// id: this.id
// }
// }).then((res) => {
// if (res.data.code === 0) {
// if(callback) {
// callback(res.data.data)
// }
// }
// }).catch((error) => {
// console.error(error)
// }).finally(()=>[
// this.locading = false
// ])
// }
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,82 +0,0 @@
<template>
<div id="DeviceTree" style="width: 100%;height: 100%; background-color: #FFFFFF; overflow: auto">
<el-container>
<el-header>
<div style="display: grid; grid-template-columns: auto auto">
<div >通道列表</div>
<div >
<el-switch
v-model="showRegion"
active-color="#13ce66"
inactive-color="rgb(64, 158, 255)"
active-text="行政区划"
inactive-text="业务分组">
</el-switch>
</div>
</div>
</el-header>
<el-main style="background-color: #ffffff;">
<RegionTree v-if="showRegion" ref="regionTree" :edit="false" :showHeader="false" :hasChannel="true" :clickEvent="treeNodeClickEvent" ></RegionTree>
<GroupTree v-if="!showRegion" ref="groupTree" :edit="false" :showHeader="false" :hasChannel="true" :clickEvent="treeNodeClickEvent" ></GroupTree>
</el-main>
</el-container>
</div>
</template>
<script>
import DeviceService from "../service/DeviceService.js";
import RegionTree from "./RegionTree.vue";
import GroupTree from "./GroupTree.vue";
export default {
name: 'DeviceTree',
components: {GroupTree, RegionTree},
data() {
return {
showRegion: true,
deviceService: new DeviceService(),
defaultProps: {
children: 'children',
label: 'name',
isLeaf: 'isLeaf'
}
};
},
props: ['device', 'onlyCatalog', 'clickEvent', 'contextMenuEvent'],
methods: {
handleClick: function (tab, event){
},
treeNodeClickEvent: function (data){
if (data.leaf) {
console.log(23111)
console.log(data)
if (this.clickEvent){
this.clickEvent(data.id)
}
}
}
},
destroyed() {
// if (this.jessibuca) {
// this.jessibuca.destroy();
// }
// this.playing = false;
// this.loaded = false;
// this.performance = "";
},
}
</script>
<style>
.device-tree-main-box{
text-align: left;
}
.device-online{
color: #252525;
}
.device-offline{
color: #727272;
}
</style>

View File

@ -1,393 +0,0 @@
<template>
<div id="DeviceTree">
<div v-if="showHeader" class="page-header" style="margin-bottom: 1rem;">
<div class="page-title">业务分组</div>
<div class="page-header-btn">
<div style="display: inline;">
<el-input @input="search" style="visibility:hidden; margin-right: 1rem; width: 12rem;" size="mini"
placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
<el-checkbox v-model="showCode">显示编号</el-checkbox>
</div>
</div>
</div>
<div v-if="showHeader" style="height: 2rem; background-color: #FFFFFF"></div>
<div>
<el-alert v-if="showAlert && edit" title="操作提示" description="你可以使用右键菜单管理节点" type="info" style="text-align: left"></el-alert>
<vue-easy-tree
class="flow-tree"
ref="veTree"
node-key="treeId"
:height="treeHeight?treeHeight:'78vh'"
lazy
style="padding: 0 0 2rem 0.5rem"
:load="loadNode"
:data="treeData"
:props="props"
:default-expanded-keys="['']"
@node-contextmenu="contextmenuEventHandler"
@node-click="nodeClickHandler"
>
<template v-slot:default="{ node, data }">
<span class="custom-tree-node">
<span v-if="node.data.type === 0 && chooseId !== node.data.deviceId" style="color: #409EFF" class="iconfont icon-bianzubeifen3"></span>
<span v-if="node.data.type === 0 && chooseId === node.data.deviceId" style="color: #c60135;" class="iconfont icon-bianzubeifen3"></span>
<span v-if="node.data.type === 1 && node.data.status === 'ON'" style="color: #409EFF" class="iconfont icon-shexiangtou2"></span>
<span v-if="node.data.type === 1 && node.data.status !== 'ON'" style="color: #808181" class="iconfont icon-shexiangtou2"></span>
<span style=" padding-left: 1px" v-if="node.data.deviceId !=='' && showCode" :title="node.data.deviceId">{{ node.label }}编号{{ node.data.deviceId }}</span>
<span style=" padding-left: 1px" v-if="node.data.deviceId ==='' || !showCode" :title="node.data.deviceId">{{ node.label }}</span>
</span>
</template>
</vue-easy-tree>
</div>
<groupEdit ref="groupEdit"></groupEdit>
<gbDeviceSelect ref="gbDeviceSelect"></gbDeviceSelect>
<gbChannelSelect ref="gbChannelSelect" dataType="group"></gbChannelSelect>
</div>
</template>
<script>
import VueEasyTree from "@wchbrad/vue-easy-tree";
import groupEdit from './../dialog/groupEdit'
import gbDeviceSelect from './../dialog/GbDeviceSelect'
import GbChannelSelect from "../dialog/GbChannelSelect.vue";
export default {
name: 'DeviceTree',
components: {
GbChannelSelect,
VueEasyTree, groupEdit, gbDeviceSelect
},
data() {
return {
props: {
label: "name",
id: "treeId"
},
showCode: false,
showAlert: true,
searchSrt: "",
chooseId: "",
treeData: [],
}
},
props: ['edit','enableAddChannel', 'clickEvent', 'onChannelChange', 'showHeader', 'hasChannel', 'addChannelToGroup', 'treeHeight'],
created() {
},
methods: {
search() {
},
loadNode: function (node, resolve) {
if (node.level === 0) {
resolve([{
treeId: "",
deviceId: "",
name: "根资源组",
isLeaf: false,
type: 0
}]);
} else {
if (node.data.leaf) {
resolve([]);
return
}
this.$axios({
method: 'get',
url: `/api/group/tree/list`,
params: {
query: this.searchSrt,
parent: node.data.id,
hasChannel: this.hasChannel
}
}).then((res) => {
if (res.data.code === 0) {
if (res.data.data.length > 0) {
this.showAlert = false
}
resolve(res.data.data);
}
}).catch(function (error) {
console.log(error);
});
}
},
reset: function () {
this.$forceUpdate();
},
contextmenuEventHandler: function (event, data, node, element) {
if (!this.edit) {
return;
}
if (node.data.type === 0) {
let menuItem = [
{
label: "刷新节点",
icon: "el-icon-refresh",
disabled: false,
onClick: () => {
this.refreshNode(node);
}
},
{
label: "新建节点",
icon: "el-icon-plus",
disabled: false,
onClick: () => {
this.addGroup(data.id, node);
}
},
{
label: "编辑节点",
icon: "el-icon-edit",
disabled: node.level === 1,
onClick: () => {
this.editGroup(data, node);
}
},
{
label: "删除节点",
icon: "el-icon-delete",
disabled: node.level === 1,
divided: true,
onClick: () => {
this.$confirm('确定删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.removeGroup(data.id, node)
}).catch(() => {
});
}
},
]
if (this.enableAddChannel) {
menuItem.push(
{
label: "添加设备",
icon: "el-icon-plus",
disabled: node.level <= 2,
onClick: () => {
this.addChannelFormDevice(data.id, node)
}
},
)
menuItem.push(
{
label: "移除设备",
icon: "el-icon-delete",
disabled: node.level <= 2,
divided: true,
onClick: () => {
this.removeChannelFormDevice(data.id, node)
}
},
)
menuItem.push(
{
label: "添加通道",
icon: "el-icon-plus",
disabled: node.level <= 2,
onClick: () => {
this.addChannel(data.id, node)
}
},
)
}
this.$contextmenu({
items: menuItem,
event, //
customClass: "custom-class", // class
zIndex: 3000, // z-index
});
}
return false;
},
removeGroup: function (id, node) {
this.$axios({
method: "delete",
url: `/api/group/delete`,
params: {
id: node.data.id,
}
}).then((res) => {
if (res.data.code === 0) {
console.log("移除成功")
node.parent.loaded = false
node.parent.expand();
if (this.onChannelChange) {
this.onChannelChange(node.data.deviceId)
}
}
}).catch(function (error) {
console.log(error);
});
},
addChannelFormDevice: function (id, node) {
this.$refs.gbDeviceSelect.openDialog((rows) => {
let deviceIds = []
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
this.$axios({
method: 'post',
url: `/api/common/channel/group/device/add`,
data: {
parentId: node.data.deviceId,
businessGroup: node.data.businessGroup,
deviceIds: deviceIds,
}
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
})
if (this.onChannelChange) {
this.onChannelChange()
}
console.log(node)
node.loaded = false
node.expand();
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
this.loading = false
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
this.loading = false
});
})
},
removeChannelFormDevice: function (id, node) {
this.$refs.gbDeviceSelect.openDialog((rows) => {
let deviceIds = []
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
this.$axios({
method: 'post',
url: `/api/common/channel/group/device/delete`,
data: {
deviceIds: deviceIds,
}
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
})
if (this.onChannelChange) {
this.onChannelChange()
}
node.loaded = false
node.expand();
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
this.loading = false
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
this.loading = false
});
})
},
addChannel: function (id, node) {
this.$refs.gbChannelSelect.openDialog((data) => {
console.log("选择的数据")
console.log(data)
this.addChannelToGroup(node.data.deviceId, node.data.businessGroup, data)
})
},
refreshNode: function (node) {
console.log(node)
node.loaded = false
node.expand();
},
refresh: function (id) {
console.log("刷新节点: " + id)
// node
let node = this.$refs.veTree.getNode(id)
if (node) {
node.loaded = false
node.expand();
}
},
addGroup: function (id, node) {
this.$refs.groupEdit.openDialog({
id: 0,
name: "",
deviceId: "",
civilCode: "",
parentDeviceId: node.level > 2 ? node.data.deviceId : "",
parentId: node.data.id,
businessGroup: node.level > 2 ? node.data.businessGroup : node.data.deviceId,
}, form => {
console.log(node)
node.loaded = false
node.expand();
}, id);
},
editGroup: function (id, node) {
console.log(node)
this.$refs.groupEdit.openDialog(node.data, form => {
console.log(node)
node.parent.loaded = false
node.parent.expand();
}, id);
},
nodeClickHandler: function (data, node, tree) {
this.chooseId = data.deviceId;
if (this.clickEvent) {
this.clickEvent(data)
}
}
},
destroyed() {
// if (this.jessibuca) {
// this.jessibuca.destroy();
// }
// this.playing = false;
// this.loaded = false;
// this.performance = "";
},
}
</script>
<style>
.device-tree-main-box {
text-align: left;
}
.device-online {
color: #252525;
}
.device-offline {
color: #727272;
}
.custom-tree-node .el-radio__label {
padding-left: 4px !important;
}
</style>

View File

@ -1,263 +0,0 @@
<template>
<div id="mapContainer" ref="mapContainer" style="width: 100%;height: 100%;"></div>
</template>
<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import OSM from 'ol/source/OSM';
import XYZ from 'ol/source/XYZ';
import VectorSource from 'ol/source/Vector';
import Tile from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import Style from 'ol/style/Style';
import Stroke from 'ol/style/Stroke';
import Icon from 'ol/style/Icon';
import View from 'ol/View';
import Feature from 'ol/Feature';
import Overlay from 'ol/Overlay';
import {Point, LineString} from 'ol/geom';
import {get as getProj, fromLonLat} from 'ol/proj';
import {ZoomSlider, Zoom} from 'ol/control';
import {containsCoordinate} from 'ol/extent';
import {v4} from 'uuid'
let olMap = null;
export default {
name: 'MapComponent',
data() {
return {
};
},
created(){
this.$nextTick(() => {
setTimeout(()=>{
this.init()
}, 100)
})
},
props: [],
mounted () {
},
methods: {
init(){
let center = fromLonLat([116.41020, 39.915119]);
if (mapParam.center) {
center = fromLonLat(mapParam.center);
}
const view = new View({
center: center,
zoom: mapParam.zoom || 10,
projection: this.projection,
maxZoom: mapParam.maxZoom || 19,
minZoom: mapParam.minZoom || 1,
});
let tileLayer = null;
if (mapParam.tilesUrl) {
tileLayer = new Tile({
source: new XYZ({
projection: getProj("EPSG:3857"),
wrapX: false,
tileSize: 256 || mapParam.tileSize,
url: mapParam.tilesUrl
})
})
}else {
tileLayer = new Tile({
preload: 4,
source: new OSM(),
})
}
olMap = new Map({
target: this.$refs.mapContainer, // ID
layers: [tileLayer], //
view: view, //
controls:[ //
// new ZoomSlider(),
new Zoom(),
] ,
})
console.log(3222)
},
setCenter(point){
},
zoomIn(zoom){
},
zoomOut(zoom){
},
centerAndZoom(point,zoom,callback){
var zoom_ = olMap.getView().getZoom();
zoom = zoom|| zoom_;
var duration = 600;
olMap.getView().setCenter(fromLonLat(point))
olMap.getView().animate({
zoom: zoom ,
duration: duration
});
},
panTo(point, zoom){
let duration = 800;
olMap.getView().cancelAnimations()
olMap.getView().animate({
center: fromLonLat(point),
duration: duration
});
if (!containsCoordinate(olMap.getView().calculateExtent(), fromLonLat(point))) {
olMap.getView().animate({
zoom: olMap.getView().getZoom() - 1,
duration: duration / 2
}, {
zoom: zoom || olMap.getView().getZoom(),
duration: duration / 2
});
}
},
fit(layer){
let extent = layer.getSource().getExtent();
if (extent) {
olMap.getView().fit(extent,{
duration : 600,
padding: [100, 100, 100, 100]
});
}
},
openInfoBox(position, content, offset){
let id = v4()
// let infoBox = document.createElement("div");
// infoBox.innerHTML = content ;
// infoBox.setAttribute("infoBoxId", id)
let overlay = new Overlay({
id:id,
autoPan:true,
autoPanAnimation:{
duration: 250
},
element: content,
positioning:"bottom-center",
offset:offset,
// className:overlayStyle.className
});
olMap.addOverlay(overlay);
overlay.setPosition(fromLonLat(position));
return id;
},
closeInfoBox(id){
olMap.getOverlayById(id).setPosition(undefined)
// olMap.removeOverlay(olMap.getOverlayById(id))
},
/**
* 添加图层
* @param data
* [
* {
*
* position: [119.1212,45,122],
* image: {
* src:"/images/123.png",
* anchor: [0.5, 0.5]
*
* }
* }
*
* ]
*/
addLayer(data, clickEvent){
let style = new Style();
if (data.length > 0) {
let features = [];
for (let i = 0; i < data.length; i++) {
let feature = new Feature(new Point(fromLonLat(data[i].position)));
feature.customData = data[i].data;
let cloneStyle = style.clone()
cloneStyle.setImage(new Icon({
anchor: data[i].image.anchor,
crossOrigin: 'Anonymous',
src: data[i].image.src,
}))
feature.setStyle(cloneStyle)
features.push(feature);
}
let source = new VectorSource();
source.addFeatures(features);
let vectorLayer = new VectorLayer({
source:source,
style:style,
renderMode:"image",
declutter: false
})
olMap.addLayer(vectorLayer)
if (typeof clickEvent == "function") {
olMap.on("click", (event)=>{
vectorLayer.getFeatures(event.pixel).then((features)=>{
if (features.length > 0) {
let items = []
for (let i = 0; i < features.length; i++) {
items.push(features[i].customData)
}
clickEvent(items)
}
})
})
}
return vectorLayer;
}
},
removeLayer(layer){
olMap.removeLayer(layer)
},
addLineLayer(positions) {
if (positions.length > 0) {
let points = [];
for (let i = 0; i < positions.length; i++) {
points.push(fromLonLat(positions[i]));
}
let line = new LineString(points)
let lineFeature = new Feature(line);
lineFeature.setStyle(new Style({
stroke: new Stroke({
width: 4 ,
color: "#0c6d6a",
})
}))
let source = new VectorSource();
source.addFeature(lineFeature);
let vectorLayer = new VectorLayer({
source: source,
})
olMap.addLayer(vectorLayer)
return vectorLayer;
}
}
},
destroyed() {
// if (this.jessibuca) {
// this.jessibuca.destroy();
// }
// this.playing = false;
// this.loaded = false;
// this.performance = "";
},
}
</script>
<style>
</style>

View File

@ -1,386 +0,0 @@
<template>
<div id="DeviceTree">
<div class="page-header" style="margin-bottom: 1rem;" v-if="showHeader">
<div class="page-title">行政区划</div>
<div class="page-header-btn">
<div style="display: inline;">
<el-input @input="search" style="visibility:hidden; margin-right: 1rem; width: 12rem;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
<el-checkbox v-model="showCode">显示编号</el-checkbox>
</div>
</div>
</div>
<div v-if="showHeader" style="height: 2rem; background-color: #FFFFFF" ></div>
<div>
<el-alert v-if="showAlert && edit" title="操作提示" description="你可以使用右键菜单管理节点" type="info" style="text-align: left"></el-alert>
<vue-easy-tree
class="flow-tree"
ref="veTree"
node-key="treeId"
:height="treeHeight?treeHeight:'78vh'"
lazy
style="padding: 0 0 2rem 0.5rem"
:load="loadNode"
:data="treeData"
:props="props"
:default-expanded-keys="['']"
@node-contextmenu="contextmenuEventHandler"
@node-click="nodeClickHandler"
>
<template class="custom-tree-node" v-slot:default="{ node, data }">
<span class="custom-tree-node" >
<span v-if="node.data.type === 0 && chooseId !== node.data.deviceId" style="color: #409EFF" class="iconfont icon-bianzubeifen3"></span>
<span v-if="node.data.type === 0 && chooseId === node.data.deviceId" style="color: #c60135;" class="iconfont icon-bianzubeifen3"></span>
<span v-if="node.data.type === 1 && node.data.status === 'ON'" style="color: #409EFF" class="iconfont icon-shexiangtou2"></span>
<span v-if="node.data.type === 1 && node.data.status !== 'ON'" style="color: #808181" class="iconfont icon-shexiangtou2"></span>
<span style=" padding-left: 1px" v-if="node.data.deviceId !=='' && showCode" :title="node.data.deviceId">{{ node.label }}编号{{ node.data.deviceId }}</span>
<span style=" padding-left: 1px" v-if="node.data.deviceId ==='' || !showCode" :title="node.data.deviceId">{{ node.label }}</span>
</span>
</template>
</vue-easy-tree>
</div>
<regionEdit ref="regionEdit"></regionEdit>
<gbDeviceSelect ref="gbDeviceSelect"></gbDeviceSelect>
<GbChannelSelect ref="gbChannelSelect" dataType="civilCode" ></GbChannelSelect>
</div>
</template>
<script>
import VueEasyTree from "@wchbrad/vue-easy-tree";
import regionEdit from './../dialog/regionEdit'
import gbDeviceSelect from './../dialog/GbDeviceSelect'
import GbChannelSelect from "../dialog/GbChannelSelect.vue";
export default {
name: 'DeviceTree',
components: {
GbChannelSelect,
VueEasyTree, regionEdit, gbDeviceSelect
},
data() {
return {
props: {
label: "name",
},
showCode: false,
showAlert: true,
searchSrt: "",
chooseId: "",
treeData: [],
}
},
props: ['edit', 'enableAddChannel', 'clickEvent', 'onChannelChange', 'showHeader', 'hasChannel', 'addChannelToCivilCode', 'treeHeight'],
created() {
},
methods: {
search() {
},
loadNode: function (node, resolve) {
if (node.level === 0) {
resolve([{
treeId: "",
deviceId: "",
name: "根资源组",
isLeaf: false,
type: 0
}]);
} else if (node.data.deviceId.length <= 8) {
if (node.data.leaf) {
resolve([]);
return
}
this.$axios({
method: 'get',
url: `/api/region/tree/list`,
params: {
query: this.searchSrt,
parent: node.data.id,
hasChannel: this.hasChannel
}
}).then((res) => {
if (res.data.code === 0) {
if (res.data.data.length > 0) {
this.showAlert = false
}
resolve(res.data.data);
}
}).catch(function (error) {
console.log(error);
});
} else {
resolve([]);
}
},
reset: function () {
this.$forceUpdate();
},
contextmenuEventHandler: function (event, data, node, element) {
if (!this.edit) {
return
}
console.log(node.level)
if (node.data.type === 0) {
let menuItem = [
{
label: "刷新节点",
icon: "el-icon-refresh",
disabled: false,
onClick: () => {
this.refreshNode(node);
}
},
{
label: "新建节点",
icon: "el-icon-plus",
disabled: false,
onClick: () => {
this.addRegion(data.id, node);
}
},
{
label: "编辑节点",
icon: "el-icon-edit",
disabled: node.level === 1,
onClick: () => {
this.editCatalog(data, node);
}
},
{
label: "删除节点",
icon: "el-icon-delete",
disabled: node.level === 1,
divided: true,
onClick: () => {
this.$confirm('确定删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.removeRegion(data.id, node)
}).catch(() => {
});
}
},
]
if (this.enableAddChannel) {
menuItem.push(
{
label: "添加设备",
icon: "el-icon-plus",
disabled: node.level === 1,
onClick: () => {
this.addChannelFormDevice(data.id, node)
}
}
)
menuItem.push(
{
label: "移除设备",
icon: "el-icon-delete",
disabled: node.level === 1,
divided: true,
onClick: () => {
this.removeChannelFormDevice(data.id, node)
}
}
)
menuItem.push(
{
label: "添加通道",
icon: "el-icon-plus",
disabled: node.level === 1,
onClick: () => {
this.addChannel(data.id, node)
}
}
)
}
this.$contextmenu({
items: menuItem,
event, //
customClass: "custom-class", // class
zIndex: 3000, // z-index
});
}
return false;
},
removeRegion: function (id, node) {
this.$axios({
method: "delete",
url: `/api/region/delete`,
params: {
id: node.data.id,
}
}).then((res) => {
if (res.data.code === 0) {
console.log("移除成功")
node.parent.loaded = false
node.parent.expand();
}
}).catch(function (error) {
console.log(error);
});
},
addChannelFormDevice: function (id, node) {
this.$refs.gbDeviceSelect.openDialog((rows)=>{
let deviceIds = []
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
this.$axios({
method: 'post',
url: `/api/common/channel/region/device/add`,
data: {
civilCode: node.data.deviceId,
deviceIds: deviceIds,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
})
if (this.onChannelChange) {
this.onChannelChange()
}
node.loaded = false
node.expand();
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
this.loading = false
}).catch((error)=> {
this.$message.error({
showClose: true,
message: error
})
this.loading = false
});
})
},
removeChannelFormDevice: function (id, node) {
this.$refs.gbDeviceSelect.openDialog((rows)=>{
let deviceIds = []
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
this.$axios({
method: 'post',
url: `/api/common/channel/region/device/delete`,
data: {
deviceIds: deviceIds,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
})
if (this.onChannelChange) {
this.onChannelChange(node.data.deviceId)
}
node.loaded = false
node.expand();
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
this.loading = false
}).catch((error)=> {
this.$message.error({
showClose: true,
message: error
})
this.loading = false
});
})
},
addChannel: function (id, node) {
this.$refs.gbChannelSelect.openDialog((data) => {
console.log("选择的数据")
console.log(data)
this.addChannelToCivilCode(node.data.deviceId, data)
})
},
refreshNode: function (node) {
node.loaded = false
node.expand();
},
refresh: function (id) {
// node
let node = this.$refs.veTree.getNode(id)
if (node) {
node.loaded = false
node.expand();
}
},
addRegion: function (id, node) {
console.log(node)
this.$refs.regionEdit.openDialog(form => {
node.loaded = false
node.expand();
}, {
deviceId: "",
name: "",
parentId: node.data.id,
parentDeviceId: node.data.deviceId,
});
},
editCatalog: function (data, node){
//
this.$refs.regionEdit.openDialog(form => {
node.loaded = false
node.expand();
}, node.data);
},
nodeClickHandler: function (data, node, tree) {
this.chooseId = data.deviceId;
if (this.clickEvent) {
this.clickEvent(data)
}
}
},
destroyed() {
// if (this.jessibuca) {
// this.jessibuca.destroy();
// }
// this.playing = false;
// this.loaded = false;
// this.performance = "";
},
}
</script>
<style>
.device-tree-main-box {
text-align: left;
}
.device-online {
color: #252525;
}
.device-offline {
color: #727272;
}
.custom-tree-node .el-radio__label {
padding-left: 4px !important;
}
</style>

View File

@ -1,67 +0,0 @@
<template>
<div id="easyplayer" ></div>
</template>
<script>
export default {
name: 'player',
data() {
return {
easyPlayer: null
};
},
props: ['videoUrl', 'error', 'hasaudio', 'height'],
mounted () {
let paramUrl = decodeURIComponent(this.$route.params.url)
this.$nextTick(() =>{
if (typeof (this.videoUrl) == "undefined") {
this.videoUrl = paramUrl;
}
console.log("初始化时的地址为: " + this.videoUrl)
this.play(this.videoUrl)
})
},
watch:{
videoUrl(newData, oldData){
this.play(newData)
},
immediate:true
},
methods: {
play: function (url) {
console.log(this.height)
if (this.easyPlayer != null) {
this.easyPlayer.destroy();
}
if (typeof (this.height) == "undefined") {
this.height = false
}
this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK, {Height: this.height})
this.easyPlayer.play(url, 1)
},
pause: function () {
this.easyPlayer.destroy();
this.easyPlayer = null
},
eventcallbacK: function(type, message) {
// console.log("player ")
// console.log(type)
// console.log(message)
}
},
destroyed() {
this.easyPlayer.destroy();
},
}
</script>
<style>
.LodingTitle {
min-width: 70px;
}
/* 隐藏logo */
.iconqingxiLOGO {
display: none !important;
}
</style>

View File

@ -1,246 +0,0 @@
<template>
<div ref="container" @dblclick="fullscreenSwich" style="width:100%;height:100%;background-color: #000000;margin:0 auto;">
<div id="glplayer" style="width: 100%; height: 100%; display: flex"></div>
<div class="buttons-box" id="buttonsBox">
<div class="buttons-box-left">
<i v-if="!playing" class="iconfont icon-play h265web-btn" @click="unPause"></i>
<i v-if="playing" class="iconfont icon-pause h265web-btn" @click="pause"></i>
<i class="iconfont icon-stop h265web-btn" @click="destroy"></i>
<i v-if="isNotMute" class="iconfont icon-audio-high h265web-btn" @click="mute()"></i>
<i v-if="!isNotMute" class="iconfont icon-audio-mute h265web-btn" @click="cancelMute()"></i>
</div>
<div class="buttons-box-right">
<!-- <i class="iconfont icon-file-record1 h265web-btn"></i>-->
<!-- <i class="iconfont icon-xiangqing2 h265web-btn" ></i>-->
<i class="iconfont icon-camera1196054easyiconnet h265web-btn" @click="screenshot"
style="font-size: 1rem !important"></i>
<i class="iconfont icon-shuaxin11 h265web-btn" @click="playBtnClick"></i>
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 h265web-btn" @click="fullscreenSwich"></i>
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 h265web-btn" @click="fullscreenSwich"></i>
</div>
</div>
</div>
</template>
<script>
let h265webPlayer = {};
/**
* 从github上复制的
* @see https://github.com/numberwolf/h265web.js/blob/master/example_normal/index.js
*/
const token = "base64:QXV0aG9yOmNoYW5neWFubG9uZ3xudW1iZXJ3b2xmLEdpdGh1YjpodHRwczovL2dpdGh1Yi5jb20vbnVtYmVyd29sZixFbWFpbDpwb3JzY2hlZ3QyM0Bmb3htYWlsLmNvbSxRUTo1MzEzNjU4NzIsSG9tZVBhZ2U6aHR0cDovL3h2aWRlby52aWRlbyxEaXNjb3JkOm51bWJlcndvbGYjODY5NCx3ZWNoYXI6bnVtYmVyd29sZjExLEJlaWppbmcsV29ya0luOkJhaWR1";
export default {
name: 'h265web',
data() {
return {
playing: false,
isNotMute: false,
quieting: false,
fullscreen: false,
loaded: false, // mute
speed: 0,
kBps: 0,
btnDom: null,
videoInfo: null,
volume: 1,
rotate: 0,
vod: true, //
forceNoOffscreen: false,
};
},
props: ['videoUrl', 'error', 'hasAudio', 'height'],
mounted() {
window.onerror = (msg) => {
// console.error(msg)
};
console.log(this._uid)
let paramUrl = decodeURIComponent(this.$route.params.url)
this.$nextTick(() => {
this.updatePlayerDomSize()
window.onresize = () => {
this.updatePlayerDomSize()
}
if (typeof (this.videoUrl) == "undefined") {
this.videoUrl = paramUrl;
}
this.btnDom = document.getElementById("buttonsBox");
console.log("初始化时的地址为: " + this.videoUrl)
this.play(this.videoUrl)
})
},
watch: {
videoUrl(newData, oldData) {
this.play(newData)
},
immediate: true
},
methods: {
updatePlayerDomSize() {
let dom = this.$refs.container;
let width = dom.parentNode.clientWidth
let height = (9 / 16) * width
const clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight)
if (height > clientHeight) {
height = clientHeight
width = (16 / 9) * height
}
dom.style.width = width + 'px';
dom.style.height = height + "px";
},
create() {
let options = {};
console.log("hasAudio " + this.hasAudio)
h265webPlayer[this._uid] = new window.new265webjs(this.videoUrl, Object.assign(
{
player: "glplayer", // id
width: 960,
height: 540,
token : token,
extInfo : {
coreProbePart : 0.4,
probeSize : 8192,
ignoreAudio : 0
}
},
options
));
let h265web = h265webPlayer[this._uid];
h265web.onOpenFullScreen = () => {
this.fullscreen = true
}
h265web.onCloseFullScreen = () => {
this.fullscreen = false
}
h265web.onReadyShowDone = () => {
//
const result = h265web.play()
this.playing = result;
}
h265web.onLoadFinish = () => {
this.loaded = true;
// mediaInfo
// @see https://github.com/numberwolf/h265web.js/blob/8b26a31ffa419bd0a0f99fbd5111590e144e36a8/example_normal/index.js#L252C9-L263C11
// mediaInfo = playerObj.mediaInfo();
}
h265web.onPlayTime = (...args) => {
console.log(args)
}
h265web.do()
},
screenshot: function () {
if (h265webPlayer[this._uid]) {
h265webPlayer[this._uid].screenshot();
}
},
playBtnClick: function (event) {
this.play(this.videoUrl)
},
play: function (url) {
console.log(url)
if (h265webPlayer[this._uid]) {
this.destroy();
}
this.create();
},
unPause: function () {
if (h265webPlayer[this._uid]) {
h265webPlayer[this._uid].play();
}
this.playing = h265webPlayer[this._uid].isPlaying();
this.err = "";
},
pause: function () {
if (h265webPlayer[this._uid]) {
h265webPlayer[this._uid].pause();
}
this.playing = h265webPlayer[this._uid].isPlaying();
this.err = "";
},
mute: function () {
if (h265webPlayer[this._uid]) {
h265webPlayer[this._uid].setVoice(0.0);
this.isNotMute = false;
}
},
cancelMute: function () {
if (h265webPlayer[this._uid]) {
h265webPlayer[this._uid].setVoice(1.0);
this.isNotMute = true;
}
},
destroy: function () {
if (h265webPlayer[this._uid]) {
h265webPlayer[this._uid].release();
}
if (document.getElementById("buttonsBox") == null) {
this.$refs.container.appendChild(this.btnDom)
}
h265webPlayer[this._uid] = null;
this.playing = false;
this.err = "";
},
eventcallbacK: function (type, message) {
// console.log("player ")
// console.log(type)
// console.log(message)
},
fullscreenSwich: function () {
let isFull = this.isFullscreen()
if (isFull) {
h265webPlayer[this._uid].closeFullScreen()
} else {
h265webPlayer[this._uid].fullScreen()
}
this.fullscreen = !isFull;
},
isFullscreen: function () {
return document.fullscreenElement ||
document.msFullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement || false;
}
},
destroyed() {
if (h265webPlayer[this._uid]) {
h265webPlayer[this._uid].destroy();
}
this.playing = false;
this.loaded = false;
},
}
</script>
<style>
.buttons-box {
width: 100%;
height: 28px;
background-color: rgba(43, 51, 63, 0.7);
position: absolute;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
left: 0;
bottom: 0;
user-select: none;
z-index: 10;
}
.h265web-btn {
width: 20px;
color: rgb(255, 255, 255);
line-height: 27px;
margin: 0px 10px;
padding: 0px 2px;
cursor: pointer;
text-align: center;
font-size: 0.8rem !important;
}
.buttons-box-right {
position: absolute;
right: 0;
}
</style>

View File

@ -1,309 +0,0 @@
<template>
<div ref="container" @dblclick="fullscreenSwich"
style="width:100%; height: 100%; background-color: #000000;margin:0 auto;position: relative;">
<div style="width:100%; padding-top: 56.25%; position: relative;"></div>
<div class="buttons-box" id="buttonsBox">
<div class="buttons-box-left">
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i>
<i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause"></i>
<i class="iconfont icon-stop jessibuca-btn" @click="destroy"></i>
<i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="mute()"></i>
<i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="cancelMute()"></i>
</div>
<div class="buttons-box-right">
<span class="jessibuca-btn">{{ kBps }} kb/s</span>
<!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>-->
<!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>-->
<i class="iconfont icon-camera1196054easyiconnet jessibuca-btn" @click="screenshot"
style="font-size: 1rem !important"></i>
<i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick"></i>
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich"></i>
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich"></i>
</div>
</div>
</div>
</template>
<script>
let jessibucaPlayer = {};
export default {
name: 'jessibuca',
data() {
return {
playing: false,
isNotMute: false,
quieting: false,
fullscreen: false,
loaded: false, // mute
speed: 0,
performance: "", //
kBps: 0,
btnDom: null,
videoInfo: null,
volume: 1,
rotate: 0,
vod: true, //
forceNoOffscreen: false,
};
},
props: ['videoUrl', 'error', 'hasAudio', 'height'],
created() {
let paramUrl = decodeURIComponent(this.$route.params.url)
this.$nextTick(() => {
console.log(2222)
this.updatePlayerDomSize()
window.onresize = this.updatePlayerDomSize
if (typeof (this.videoUrl) == "undefined") {
this.videoUrl = paramUrl;
}
this.btnDom = document.getElementById("buttonsBox");
})
},
// mounted() {
// const ro = new ResizeObserver(entries => {
// entries.forEach(entry => {
// this.updatePlayerDomSize()
// });
// });
// ro.observe(this.$refs.container);
// },
mounted(){
this.updatePlayerDomSize();
},
watch: {
videoUrl: {
handler(val, _) {
this.$nextTick(() => {
this.play(val);
})
},
immediate: true
}
},
methods: {
updatePlayerDomSize() {
let dom = this.$refs.container;
let width = dom.parentNode.clientWidth
let height = (9 / 16) * width
console.log(height)
console.log(dom.clientHeight)
if (height > dom.clientHeight) {
height = dom.clientHeight
width = (16 / 9) * height
}
if (width > 0 && height > 0) {
dom.style.width = width + 'px';
dom.style.height = height + "px";
dom.style.paddingTop = 0;
console.log(width)
console.log(height)
}
},
create() {
let options = {
container: this.$refs.container,
autoWasm: true,
background: "",
controlAutoHide: false,
debug: false,
decoder: "static/js/jessibuca/decoder.js",
forceNoOffscreen: false,
hasAudio: typeof (this.hasAudio) == "undefined" ? true : this.hasAudio,
heartTimeout: 5,
heartTimeoutReplay: true,
heartTimeoutReplayTimes: 3,
hiddenAutoPause: false,
hotKey: true,
isFlv: false,
isFullResize: false,
isNotMute: this.isNotMute,
isResize: false,
keepScreenOn: true,
loadingText: "请稍等, 视频加载中......",
loadingTimeout: 10,
loadingTimeoutReplay: true,
loadingTimeoutReplayTimes: 3,
openWebglAlignment: false,
operateBtns: {
fullscreen: false,
screenshot: false,
play: false,
audio: false,
record: false
},
recordType: "mp4",
rotate: 0,
showBandwidth: false,
supportDblclickFullscreen: false,
timeout: 10,
useMSE: true,
useWCS: false,
useWebFullScreen: true,
videoBuffer: 0.1,
wasmDecodeErrorReplay: true,
wcsUseVideoRender: true
};
console.log("Jessibuca -> options: ", options);
jessibucaPlayer[this._uid] = new window.Jessibuca({...options});
let jessibuca = jessibucaPlayer[this._uid];
let _this = this;
jessibuca.on("pause", function () {
_this.playing = false;
});
jessibuca.on("play", function () {
_this.playing = true;
});
jessibuca.on("fullscreen", function (msg) {
_this.fullscreen = msg
});
jessibuca.on("mute", function (msg) {
_this.isNotMute = !msg;
});
jessibuca.on("performance", function (performance) {
let show = "卡顿";
if (performance === 2) {
show = "非常流畅";
} else if (performance === 1) {
show = "流畅";
}
_this.performance = show;
});
jessibuca.on('kBps', function (kBps) {
_this.kBps = Math.round(kBps);
});
jessibuca.on("videoInfo", function (msg) {
console.log("Jessibuca -> videoInfo: ", msg);
});
jessibuca.on("audioInfo", function (msg) {
console.log("Jessibuca -> audioInfo: ", msg);
});
jessibuca.on("error", function (msg) {
console.log("Jessibuca -> error: ", msg);
});
jessibuca.on("timeout", function (msg) {
console.log("Jessibuca -> timeout: ", msg);
});
jessibuca.on("loadingTimeout", function (msg) {
console.log("Jessibuca -> timeout: ", msg);
});
jessibuca.on("delayTimeout", function (msg) {
console.log("Jessibuca -> timeout: ", msg);
});
jessibuca.on("playToRenderTimes", function (msg) {
console.log("Jessibuca -> playToRenderTimes: ", msg);
});
},
playBtnClick: function (event) {
this.play(this.videoUrl)
},
play: function (url) {
console.log("Jessibuca -> url: ", url);
if (jessibucaPlayer[this._uid]) {
this.destroy();
}
this.create();
jessibucaPlayer[this._uid].on("play", () => {
this.playing = true;
this.loaded = true;
this.quieting = jessibuca.quieting;
});
if (jessibucaPlayer[this._uid].hasLoaded()) {
jessibucaPlayer[this._uid].play(url);
} else {
jessibucaPlayer[this._uid].on("load", () => {
jessibucaPlayer[this._uid].play(url);
});
}
},
pause: function () {
if (jessibucaPlayer[this._uid]) {
jessibucaPlayer[this._uid].pause();
}
this.playing = false;
this.err = "";
this.performance = "";
},
screenshot: function () {
if (jessibucaPlayer[this._uid]) {
jessibucaPlayer[this._uid].screenshot();
}
},
mute: function () {
if (jessibucaPlayer[this._uid]) {
jessibucaPlayer[this._uid].mute();
}
},
cancelMute: function () {
if (jessibucaPlayer[this._uid]) {
jessibucaPlayer[this._uid].cancelMute();
}
},
destroy: function () {
if (jessibucaPlayer[this._uid]) {
jessibucaPlayer[this._uid].destroy();
}
if (document.getElementById("buttonsBox") == null) {
this.$refs.container.appendChild(this.btnDom)
}
jessibucaPlayer[this._uid] = null;
this.playing = false;
this.err = "";
this.performance = "";
},
fullscreenSwich: function () {
let isFull = this.isFullscreen()
jessibucaPlayer[this._uid].setFullscreen(!isFull)
this.fullscreen = !isFull;
},
isFullscreen: function () {
return document.fullscreenElement ||
document.msFullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement || false;
}
},
destroyed() {
if (jessibucaPlayer[this._uid]) {
jessibucaPlayer[this._uid].destroy();
}
this.playing = false;
this.loaded = false;
this.performance = "";
},
}
</script>
<style>
.buttons-box {
width: 100%;
height: 28px;
background-color: rgba(43, 51, 63, 0.7);
position: absolute;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
left: 0;
bottom: 0;
user-select: none;
z-index: 10;
}
.jessibuca-btn {
width: 20px;
color: rgb(255, 255, 255);
line-height: 27px;
margin: 0px 10px;
padding: 0px 2px;
cursor: pointer;
text-align: center;
font-size: 0.8rem !important;
}
.buttons-box-right {
position: absolute;
right: 0;
}
</style>

View File

@ -1,109 +0,0 @@
<template>
<div id="mediaInfo" >
<el-button style="position: absolute; right: 1rem;" icon="el-icon-refresh-right" circle size="mini" @click="getMediaInfo"></el-button>
<el-descriptions size="mini" :column="3" title="概况">
<el-descriptions-item label="观看人数">{{ info.readerCount }}</el-descriptions-item>
<el-descriptions-item label="网络">{{ formatByteSpeed() }}</el-descriptions-item>
<el-descriptions-item label="持续时间">{{info.aliveSecond}}</el-descriptions-item>
</el-descriptions>
<div style="display: grid; grid-template-columns: 1fr 1fr">
<el-descriptions size="mini" v-if="info.videoCodec" :column="2" title="视频信息">
<el-descriptions-item label="编码">{{ info.videoCodec }}</el-descriptions-item>
<el-descriptions-item label="分辨率"
>{{ info.width }}x{{ info.height }}
</el-descriptions-item>
<el-descriptions-item label="FPS">{{ info.fps }}</el-descriptions-item>
<el-descriptions-item label="丢包率">{{ info.loss }}</el-descriptions-item>
</el-descriptions>
<el-descriptions size="mini" v-if="info.audioCodec" :column="2" title="音频信息">
<el-descriptions-item label="编码">
{{ info.audioCodec }}
</el-descriptions-item>
<el-descriptions-item label="采样率">{{ info.audioSampleRate }}</el-descriptions-item>
</el-descriptions>
</div>
</div>
</template>
<script>
export default {
name: "mediaInfo",
props: [ 'app', 'stream', 'mediaServerId'],
components: {},
created() {
this.getMediaInfo();
},
data() {
return {
info: {},
task: null,
};
},
methods: {
getMediaInfo: function () {
this.$axios({
method: 'get',
url: `/api/server/media_server/media_info`,
params: {
app: this.app,
stream: this.stream,
mediaServerId: this.mediaServerId,
}
}).then((res)=> {
console.log(res.data.data);
if (res.data.code === 0) {
this.info = res.data.data
}
}).catch((error)=> {
console.log(error);
});
},
startTask: function () {
this.task = setInterval(this.getMediaInfo, 1000)
},
stopTask: function () {
if (this.task) {
window.clearInterval(this.task);
this.task = null;
}
},
formatByteSpeed: function (){
let bytesSpeed = this.info.bytesSpeed
let num = 1024.0 //byte
if (bytesSpeed < num) return bytesSpeed + ' B/S'
if (bytesSpeed < Math.pow(num, 2)) return (bytesSpeed / num).toFixed(2) + ' KB/S' //kb
if (bytesSpeed < Math.pow(num, 3))
return (bytesSpeed / Math.pow(num, 2)).toFixed(2) + ' MB/S' //M
if (bytesSpeed < Math.pow(num, 4))
return (bytesSpeed / Math.pow(num, 3)).toFixed(2) + ' G/S' //G
return (bytesSpeed / Math.pow(num, 4)).toFixed(2) + ' T/S' //T
},
formatAliveSecond: function (){
let aliveSecond = this.info.aliveSecond
const h = parseInt(aliveSecond.value / 3600)
const minute = parseInt((aliveSecond.value / 60) % 60)
const second = Math.ceil(aliveSecond.value % 60)
const hours = h < 10 ? '0' + h : h
const formatSecond = second > 59 ? 59 : second
return `${hours > 0 ? `${hours}小时` : ''}${minute < 10 ? '0' + minute : minute}${
formatSecond < 10 ? '0' + formatSecond : formatSecond
}`
}
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,426 +0,0 @@
<template>
<div id="ptzCruising">
<div style="display: grid; grid-template-columns: 80px auto; line-height: 28px">
<span>巡航组号: </span>
<el-input
min="1"
max="255"
placeholder="巡航组号"
addonBefore="巡航组号"
addonAfter="(1-255)"
v-model="cruiseId"
size="mini"
>
</el-input>
</div>
<p>
<el-tag v-for="(item, index) in presetList"
key="item.presetId"
closable
@close="delPreset(item, index)"
style="margin-right: 1rem; cursor: pointer"
>
{{item.presetName?item.presetName:item.presetId}}
</el-tag>
</p>
<el-form size="mini" :inline="true" v-if="selectPresetVisible">
<el-form-item >
<el-select v-model="selectPreset" placeholder="请选择预置点">
<el-option
v-for="item in allPresetList"
:key="item.presetId"
:label="item.presetName"
:value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addCruisePoint">保存</el-button>
<el-button type="primary" @click="cancelAddCruisePoint">取消</el-button>
</el-form-item>
</el-form>
<el-button size="mini" v-else @click="selectPresetVisible=true">添加巡航点</el-button>
<el-form size="mini" :inline="true" v-if="setSpeedVisible">
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="巡航速度"
addonBefore="巡航速度"
addonAfter="(1-4095)"
v-if="setSpeedVisible"
v-model="cruiseSpeed"
size="mini"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="setCruiseSpeed">保存</el-button>
<el-button @click="cancelSetCruiseSpeed">取消</el-button>
</el-form-item>
</el-form>
<el-button v-else size="mini" @click="setSpeedVisible = true">设置巡航速度</el-button>
<el-form size="mini" :inline="true" v-if="setTimeVisible">
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="巡航停留时间(秒)"
addonBefore="巡航停留时间(秒)"
addonAfter="(1-4095)"
style="width: 100%;"
v-model="cruiseTime"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="setCruiseTime">保存</el-button>
<el-button @click="cancelSetCruiseTime">取消</el-button>
</el-form-item>
</el-form>
<el-button v-else size="mini" @click="setTimeVisible = true">设置巡航时间</el-button>
<el-button size="mini" @click="startCruise">开始巡航</el-button>
<el-button size="mini" @click="stopCruise">停止巡航</el-button>
<el-button size="mini" type="danger" @click="deleteCruise">删除巡航</el-button>
</div>
</template>
<script>
export default {
name: "ptzCruising",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
this.getPresetList()
},
data() {
return {
cruiseId: 1,
presetList: [],
allPresetList: [],
selectPreset: "",
inputVisible: false,
selectPresetVisible: false,
setSpeedVisible: false,
setTimeVisible: false,
cruiseSpeed: '',
cruiseTime: '',
};
},
methods: {
getPresetList: function () {
this.$axios({
method: 'get',
url: `/api/front-end/preset/query/${this.deviceId}/${this.channelDeviceId}`,
}).then((res)=> {
if (res.data.code === 0) {
this.allPresetList = res.data.data;
}
}).catch((error)=> {
console.log(error);
});
},
addCruisePoint: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/point/add/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
presetId: this.selectPreset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
this.presetList.push(this.selectPreset)
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.selectPreset = ""
this.selectPresetVisible = false;
loading.close()
})
},
cancelAddCruisePoint: function () {
this.selectPreset = ""
this.selectPresetVisible = false;
},
delPreset: function (preset, index){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/point/delete/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
presetId: preset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
this.presetList.splice(index, 1)
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
deleteCruise: function (preset, index){
this.$confirm("确定删除此巡航组", '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/point/delete/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
presetId: 0
}
}).then((res)=> {
if (res.data.code === 0) {
this.presetList = []
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
})
},
setCruiseSpeed: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/speed/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
speed: this.cruiseSpeed
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.cruiseSpeed = ""
this.setSpeedVisible = false
loading.close()
})
},
cancelSetCruiseSpeed: function (){
this.cruiseSpeed = ""
this.setSpeedVisible = false
},
setCruiseTime: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/time/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
time: this.cruiseTime
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.setTimeVisible = false;
this.cruiseTime = "";
loading.close()
})
},
cancelSetCruiseTime: function (){
this.setTimeVisible = false;
this.cruiseTime = "";
},
startCruise: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/start/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
stopCruise: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/stop/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,212 +0,0 @@
<template>
<div id="ptzPreset" style="width: 100%">
<el-tag v-for="item in presetList"
key="item.presetId"
closable
@close="delPreset(item)"
@click="gotoPreset(item)"
size="mini"
style="margin-right: 1rem; cursor: pointer; margin-bottom: 0.6rem"
>
{{item.presetName?item.presetName:item.presetId}}
</el-tag>
<el-input
min="1"
max="255"
placeholder="预置位编号"
addonBefore="预置位编号"
addonAfter="(1-255)"
style="width: 300px; vertical-align: bottom;"
v-if="inputVisible"
v-model="ptzPresetId"
ref="saveTagInput"
size="small"
>
<template v-slot:append>
<el-button @click="addPreset()">保存</el-button>
<el-button @click="cancel()">取消</el-button>
</template>
</el-input>
<el-button v-else size="small" @click="showInput">+ 添加</el-button>
</div>
</template>
<script>
export default {
name: "ptzPreset",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
this.getPresetList()
},
data() {
return {
presetList: [],
inputVisible: false,
ptzPresetId: '',
};
},
methods: {
getPresetList: function () {
this.$axios({
method: 'get',
url: `/api/front-end/preset/query/${this.deviceId}/${this.channelDeviceId}`,
}).then((res)=> {
if (res.data.code === 0) {
this.presetList = res.data.data;
//
this.$nextTick(() => {
this.$refs.channelListTable.doLayout();
})
}
}).catch((error)=> {
console.log(error);
});
},
showInput() {
this.inputVisible = true;
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus();
});
},
addPreset: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/preset/add/${this.deviceId}/${this.channelDeviceId}`,
params: {
presetId: this.ptzPresetId
}
}).then((res)=> {
if (res.data.code === 0) {
setTimeout(()=>{
loading.close()
this.inputVisible = false;
this.ptzPresetId = ""
this.getPresetList()
}, 1000)
}else {
loading.close()
this.inputVisible = false;
this.ptzPresetId = ""
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
loading.close()
this.inputVisible = false;
this.ptzPresetId = ""
this.$message({
showClose: true,
message: error,
type: 'error'
});
});
},
cancel: function () {
this.inputVisible = false;
this.ptzPresetId = ""
},
gotoPreset: function (preset){
console.log(preset)
this.$axios({
method: 'get',
url: `/api/front-end/preset/call/${this.deviceId}/${this.channelDeviceId}`,
params: {
presetId: preset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '调用成功',
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
});
},
delPreset: function (preset){
this.$confirm("确定删除此预置位", '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/preset/delete/${this.deviceId}/${this.channelDeviceId}`,
params: {
presetId: preset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
setTimeout(()=>{
loading.close()
this.getPresetList()
}, 1000)
}else {
loading.close()
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
loading.close()
this.$message({
showClose: true,
message: error,
type: 'error'
});
});
}).catch(() => {
});
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,273 +0,0 @@
<template>
<div id="ptzScan">
<div style="display: grid; grid-template-columns: 80px auto; line-height: 28px">
<span>扫描组号: </span>
<el-input
min="1"
max="255"
placeholder="扫描组号"
addonBefore="扫描组号"
addonAfter="(1-255)"
v-model="scanId"
size="mini"
>
</el-input>
</div>
<el-button size="mini" @click="setScanLeft">设置左边界</el-button>
<el-button size="mini" @click="setScanRight">设置右边界</el-button>
<el-form size="mini" :inline="true" v-if="setSpeedVisible">
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="巡航速度"
addonBefore="巡航速度"
addonAfter="(1-4095)"
v-if="setSpeedVisible"
v-model="speed"
size="mini"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="setSpeed">保存</el-button>
<el-button @click="cancelSetSpeed">取消</el-button>
</el-form-item>
</el-form>
<el-button v-else size="mini" @click="setSpeedVisible = true">设置扫描速度</el-button>
<el-button size="mini" @click="startScan">开始自动扫描</el-button>
<el-button size="mini" @click="stopScan">停止自动扫描</el-button>
</div>
</template>
<script>
export default {
name: "ptzScan",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
},
data() {
return {
scanId: 1,
setSpeedVisible: false,
speed: '',
};
},
methods: {
setSpeed: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/set/speed/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId,
speed: this.speed
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.speed = ""
this.setSpeedVisible = false
loading.close()
})
},
cancelSetSpeed: function (){
this.speed = ""
this.setSpeedVisible = false
},
setScanLeft: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/set/left/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
setScanRight: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/set/right/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.setSpeedVisible = false;
this.speed = "";
loading.close()
})
},
startScan: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/start/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
stopScan: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/stop/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,90 +0,0 @@
<template>
<div id="ptzScan">
<el-form size="mini" :inline="true" >
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="开关编号"
addonBefore="开关编号"
addonAfter="(2-255)"
v-model="switchId"
size="mini"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button size="mini" @click="open('on')">开启</el-button>
<el-button size="mini" @click="open('off')">关闭</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "ptzScan",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
},
data() {
return {
switchId: 1,
};
},
methods: {
open: function (command){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/auxiliary/${this.deviceId}/${this.channelDeviceId}`,
params: {
command: command,
switchId: this.switchId,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,70 +0,0 @@
<template>
<div id="ptzWiper">
<el-button size="mini" @click="open('on')">开启</el-button>
<el-button size="mini" @click="open('off')">关闭</el-button>
</div>
</template>
<script>
export default {
name: "ptzWiper",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
},
data() {
return {};
},
methods: {
open: function (command){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/wiper/${this.deviceId}/${this.channelDeviceId}`,
params: {
command: command,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -1,158 +0,0 @@
<template>
<div id="app" style="width: 100%">
<div class="page-header">
<div class="page-title">控制台</div>
</div>
<el-row style="width: 100%">
<el-col :xl="{ span: 8 }" :lg="{ span: 8 }" :md="{ span: 12 }" :sm="{ span: 12 }" :xs="{ span: 24 }" >
<div class="control-cell" id="ThreadsLoad" >
<div style="width:100%; height:100%; ">
<consoleCPU ref="consoleCPU"></consoleCPU>
</div>
</div>
</el-col>
<el-col :xl="{ span: 8 }" :lg="{ span: 8 }" :md="{ span: 12 }" :sm="{ span: 12 }" :xs="{ span: 24 }" >
<div class="control-cell" id="WorkThreadsLoad" >
<div style="width:100%; height:100%; ">
<consoleResource ref="consoleResource"></consoleResource>
</div>
</div>
</el-col>
<el-col :xl="{ span: 8 }" :lg="{ span: 8 }" :md="{ span: 12 }" :sm="{ span: 12 }" :xs="{ span: 24 }" >
<div class="control-cell" id="WorkThreadsLoad" >
<div style="width:100%; height:100%; ">
<consoleNet ref="consoleNet"></consoleNet>
</div>
</div>
</el-col>
<el-col :xl="{ span: 8 }" :lg="{ span: 8 }" :md="{ span: 12 }" :sm="{ span: 12 }" :xs="{ span: 24 }" >
<div class="control-cell" id="WorkThreadsLoad" >
<div style="width:100%; height:100%; ">
<consoleMem ref="consoleMem"></consoleMem>
</div>
</div>
</el-col>
<el-col :xl="{ span: 8 }" :lg="{ span: 8 }" :md="{ span: 12 }" :sm="{ span: 12 }" :xs="{ span: 24 }" >
<div class="control-cell" id="WorkThreadsLoad" >
<div style="width:100%; height:100%; ">
<consoleNodeLoad ref="consoleNodeLoad"></consoleNodeLoad>
</div>
</div>
</el-col>
<el-col :xl="{ span: 8 }" :lg="{ span: 8 }" :md="{ span: 12 }" :sm="{ span: 12 }" :xs="{ span: 24 }" >
<div class="control-cell" id="WorkThreadsLoad" >
<div style="width:100%; height:100%; ">
<consoleDisk ref="consoleDisk"></consoleDisk>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import consoleCPU from './console/ConsoleCPU.vue'
import consoleMem from './console/ConsoleMEM.vue'
import consoleNet from './console/ConsoleNet.vue'
import consoleNodeLoad from './console/ConsoleNodeLoad.vue'
import consoleDisk from './console/ConsoleDisk.vue'
import consoleResource from './console/ConsoleResource.vue'
import echarts from 'echarts';
export default {
name: 'app',
components: {
echarts,
uiHeader,
consoleCPU,
consoleMem,
consoleNet,
consoleNodeLoad,
consoleDisk,
consoleResource,
},
data() {
return {
timer: null,
};
},
created() {
this.getSystemInfo();
this.getLoad();
this.getResourceInfo();
this.loopForSystemInfo();
},
destroyed() {
},
methods: {
loopForSystemInfo: function (){
if (this.timer != null) {
window.clearTimeout(this.timer);
}
this.timer = setTimeout(()=>{
if (this.$route.path === "/console") {
this.getSystemInfo();
this.getLoad();
this.timer = null;
this.loopForSystemInfo()
this.getResourceInfo()
}
}, 2000)
},
getSystemInfo: function (){
this.$axios({
method: 'get',
url: `/api/server/system/info`,
}).then( (res)=> {
if (res.data.code === 0) {
this.$refs.consoleCPU.setData(res.data.data.cpu)
this.$refs.consoleMem.setData(res.data.data.mem)
this.$refs.consoleNet.setData(res.data.data.net, res.data.data.netTotal)
this.$refs.consoleDisk.setData(res.data.data.disk)
}
}).catch( (error)=> {
});
},
getLoad: function (){
this.$axios({
method: 'get',
url: `/api/server/media_server/load`,
}).then( (res)=> {
if (res.data.code === 0) {
this.$refs.consoleNodeLoad.setData(res.data.data)
}
}).catch( (error)=> {
});
},
getResourceInfo: function (){
this.$axios({
method: 'get',
url: `/api/server/resource/info`,
}).then( (res)=> {
if (res.data.code === 0) {
this.$refs.consoleResource.setData(res.data.data)
}
}).catch( (error)=> {
});
},
}
};
</script>
<style>
#app {
height: 100%;
}
.control-cell {
padding-top: 10px;
padding-left: 5px;
padding-right: 10px;
height: 360px;
}
</style>

View File

@ -1,109 +0,0 @@
<template>
<div id="consoleCPU" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
<ve-line ref="consoleCPU" :data="chartData" :extend="extend" width="100%" height="100%" :legend-visible="false"></ve-line>
</div>
</template>
<script>
import moment from "moment/moment";
export default {
name: 'consoleCPU',
data() {
return {
chartData: {
columns: ['time', 'data'],
rows: []
},
extend: {
title: {
show: true,
text: "CPU",
left: "center",
top: 20,
},
grid: {
show: true,
right: "30px",
containLabel: true,
},
xAxis: {
time: "time",
max: 'dataMax',
boundaryGap: ['20%', '20%'],
axisLabel: {
formatter:(v)=>{
return moment(v).format("HH:mm:ss");
},
showMaxLabel: true,
}
},
yAxis: {
type: 'value',
min: 0,
max: 1,
splitNumber: 6,
position: "left",
silent: true,
axisLabel: {
formatter: (v)=>{
return v*100 + "%";
},
}
},
tooltip: {
trigger: 'axis',
formatter: (data)=>{
console.log(data)
return moment(data[0].data[0]).format("HH:mm:ss") + "</br> "
+ data[0].marker + "使用:" + (data[0].data[1]*100).toFixed(2) + "%";
}
},
series: {
itemStyle: {
color: "#409EFF"
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: '#50a3f8' // 0%
}, {
offset: 1, color: '#69b0fa' // 100%
}],
global: false // false
}
}
}
}
};
},
created() {
},
mounted() {
this.$nextTick(_ => {
setTimeout(()=>{
this.$refs.consoleCPU.echarts.resize()
}, 100)
})
},
destroyed() {
},
methods: {
setData: function(data) {
this.chartData .rows = data;
}
}
};
</script>

View File

@ -1,81 +0,0 @@
<template>
<div id="ConsoleNet" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
<ve-bar ref="ConsoleNet" :data="chartData" :extend="extend" :settings="chartSettings" width="100%" height="100%" ></ve-bar>
</div>
</template>
<script>
import moment from "moment/moment";
export default {
name: 'ConsoleNet',
data() {
return {
chartData: {
columns: ['path','free','use'],
rows: []
},
chartSettings: {
stack: {
'xxx': ['free', 'use']
},
labelMap: {
'free': '剩余',
'use': '已使用'
},
},
extend: {
title: {
show: true,
text: "磁盘",
left: "center",
top: 20,
},
grid: {
show: true,
right: "30px",
containLabel: true,
},
series: {
barWidth: 30
},
legend: {
left: "center",
bottom: "15px",
},
tooltip: {
trigger: 'axis',
formatter: (data)=>{
console.log(data)
let relVal = "";
for (let i = 0; i < data.length; i++) {
relVal += data[i].marker + data[i].seriesName + ":" + data[i].value.toFixed(2) + "GB"
if (i < data.length - 1) {
relVal += "</br>";
}
}
return relVal;
}
},
}
};
},
mounted() {
this.$nextTick(_ => {
setTimeout(()=>{
this.$refs.ConsoleNet.echarts.resize()
}, 100)
})
},
destroyed() {
},
methods: {
setData: function(data) {
this.chartData.rows = data;
}
}
};
</script>

View File

@ -1,103 +0,0 @@
<template>
<div id="ConsoleMEM" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
<ve-line ref="ConsoleMEM" :data="chartData" :extend="extend" width="100%" height="100%" :legend-visible="false"></ve-line>
</div>
</template>
<script>
import moment from "moment/moment";
export default {
name: 'ConsoleMEM',
data() {
return {
chartData: {
columns: ['time', 'data'],
rows: []
},
extend: {
title: {
show: true,
text: "内存",
left: "center",
top: 20,
},
grid: {
show: true,
right: "30px",
containLabel: true,
},
xAxis: {
time: "time",
max: 'dataMax',
boundaryGap: ['20%', '20%'],
axisLabel: {
formatter:(v)=>{
return moment(v).format("HH:mm:ss");
},
showMaxLabel: true,
}
},
yAxis: {
type: 'value',
min: 0,
max: 1,
splitNumber: 6,
position: "left",
silent: true,
axisLabel: {
formatter: (v)=>{
return v*100 + "%";
},
}
},
tooltip: {
trigger: 'axis',
formatter: (data)=>{
console.log(data)
return moment(data[0].data[0]).format("HH:mm:ss") + "</br>"+ data[0].marker +" 使用:" + (data[0].data[1]*100).toFixed(2) + "%";
}
},
series: {
itemStyle: {
color: "#409EFF"
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: '#50a3f8' // 0%
}, {
offset: 1, color: '#69b0fa' // 100%
}],
global: false // false
}
}
}
}
};
},
mounted() {
this.$nextTick(_ => {
setTimeout(()=>{
this.$refs.ConsoleMEM.echarts.resize()
}, 100)
})
},
destroyed() {
},
methods: {
setData: function(data) {
this.chartData .rows = data;
}
}
};
</script>

View File

@ -1,85 +0,0 @@
<template>
<div id="ConsoleMediaServer" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
<ve-histogram ref="ConsoleMEM" :data="chartData" :extend="extend" :settings="chartSettings" width="100%" height="100%" ></ve-histogram>
</div>
</template>
<script>
import moment from "moment/moment";
export default {
name: 'ConsoleMediaServer',
data() {
return {
chartData: {
columns: ['time', 'in', 'out'],
rows: [
]
},
chartSettings: {
area: true,
labelMap: {
'in': '下载',
'out': '上传'
},
},
extend: {
title: {
show: true,
text: "网络",
left: "center",
top: 20,
},
grid: {
show: true,
right: "30px",
containLabel: true,
},
xAxis: {
time: "time",
max: 'dataMax',
boundaryGap: ['20%', '20%'],
axisLabel: {
formatter:(v)=>{
return moment(v).format("HH:mm:ss");
},
showMaxLabel: true,
},
},
tooltip: {
trigger: 'axis',
formatter: (data)=>{
console.log(parseFloat(data[0].data[1]).toFixed(2))
console.log(parseFloat(data[1].data[1]).toFixed(2))
console.log("############")
return "下载:" + parseFloat(data[0].data[1]).toFixed(2) + "Mbps" + "</br> 上传:" + parseFloat(data[1].data[1]).toFixed(2) + "Mbps";
}
},
legend: {
left: "center",
bottom: "15px",
}
}
};
},
mounted() {
this.$nextTick(_ => {
setTimeout(()=>{
this.$refs.ConsoleMEM.echarts.resize()
}, 100)
})
},
destroyed() {
},
methods: {
setData: function(data) {
console.log(data)
this.chartData .rows = data;
}
}
};
</script>

View File

@ -1,131 +0,0 @@
<template>
<div id="ConsoleNet" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
<ve-line ref="ConsoleNet" :data="chartData" :extend="extend" :settings="chartSettings" :events="chartEvents" width="100%" height="100%" ></ve-line>
</div>
</template>
<script>
import moment from "moment/moment";
export default {
name: 'ConsoleNet',
data() {
return {
chartData: {
columns: ['time','out','in'],
rows: []
},
chartSettings: {
area: true,
labelMap: {
'in': '下载',
'out': '上传'
},
},
extend: {
title: {
show: true,
text: "网络",
left: "center",
top: 20,
},
grid: {
show: true,
right: "30px",
containLabel: true,
},
xAxis: {
time: "time",
max: 'dataMax',
boundaryGap: ['20%', '20%'],
axisLabel: {
formatter:(v)=>{
return moment(v).format("HH:mm:ss");
},
showMaxLabel: true,
},
},
yAxis: {
type: 'value',
min: 0,
max: 1000,
splitNumber: 6,
position: "left",
silent: true,
},
tooltip: {
trigger: 'axis',
formatter: (data)=>{
let in_sel = true;
let out_sel = true;
for (let key in this.extend.legend.selected) {
if (key == "上传") {
out_sel = this.extend.legend.selected[key];
}
if (key == "下载") {
in_sel = this.extend.legend.selected[key];
}
}
if (out_sel && in_sel) {
return (
data[1].marker +
"下载:" +
parseFloat(data[1].data[1]).toFixed(2) +
"Mbps" +
"</br> " +
data[0].marker +
"上传:" +
parseFloat(data[0].data[1]).toFixed(2) +
"Mbps"
);
} else if (out_sel)
return (
data[0].marker +
"上传:" +
parseFloat(data[0].data[1]).toFixed(2) +
"Mbps"
);
else if (in_sel)
return (
data[0].marker +
"下载:" +
parseFloat(data[0].data[1]).toFixed(2) +
"Mbps"
);
return "";
}
},
legend: {
left: "center",
bottom: "15px",
selected: {},
}
},
chartEvents: {
legendselectchanged: (item) => {
this.extend.legend.selected = item.selected;
}
}
};
},
mounted() {
this.$nextTick(_ => {
setTimeout(()=>{
this.$refs.ConsoleNet.echarts.resize()
}, 100)
})
},
destroyed() {
},
methods: {
setData: function(data, total) {
this.chartData .rows = data;
this.extend.yAxis.max= total;
}
}
};
</script>

View File

@ -1,63 +0,0 @@
<template>
<div id="ConsoleNodeLoad" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
<ve-histogram ref="consoleNodeLoad" :data="chartData" :extend="extend" :settings="chartSettings" width="100%" height="100%" :legend-visible="true"></ve-histogram>
</div>
</template>
<script>
import moment from "moment/moment";
export default {
name: 'ConsoleNodeLoad',
data() {
return {
chartData: {
columns: ['id', 'push', 'proxy', 'gbReceive', 'gbSend'],
rows: []
},
chartSettings: {
labelMap: {
'push': '直播推流',
'proxy': '拉流代理',
'gbReceive': '国标收流',
'gbSend': '国标推流',
},
},
extend: {
title: {
show: true,
text: "节点负载",
left: "center",
top: 20,
},
legend: {
left: "center",
bottom: "15px",
},
label: {
show: true,
position: "top"
}
}
};
},
mounted() {
this.$nextTick(_ => {
setTimeout(()=>{
this.$refs.consoleNodeLoad.echarts.resize()
}, 100)
})
},
destroyed() {
},
methods: {
setData: function(data) {
this.chartData .rows = data;
}
}
};
</script>

View File

@ -1,90 +0,0 @@
<template >
<div id="consoleResource" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
<div style="width: 50%;height: 50%; float:left; ">
<el-progress v-if="deviceInfo.total > 0" :width="100" :stroke-width="8" type="circle" :percentage="Math.floor(deviceInfo.online/deviceInfo.total*100)" style="margin-top: 20px; font-size: 18px"></el-progress>
<el-progress v-if="deviceInfo.total === 0" :width="100" :stroke-width="8" type="circle" :percentage="0" style="margin-top: 20px; font-size: 18px"></el-progress>
<div class="resourceInfo">
设备总数:{{deviceInfo.total}}<br/>
在线数:{{deviceInfo.online}}
</div>
</div>
<div style="width: 50%;height: 50%; float:left; ">
<el-progress v-if="channelInfo.total > 0" :width="100" :stroke-width="10" type="circle" :percentage="Math.floor(channelInfo.online/channelInfo.total*100)" style="margin-top: 20px"></el-progress>
<el-progress v-if="channelInfo.total === 0" :width="100" :stroke-width="10" type="circle" :percentage="0" style="margin-top: 20px"></el-progress>
<div class="resourceInfo">
通道总数:{{channelInfo.total}}<br/>
在线数:{{channelInfo.online}}
</div>
</div>
<div style="width: 50%;height: 50%; float:left; ">
<el-progress v-if="pushInfo.total > 0" :width="100" :stroke-width="10" type="circle" :percentage="Math.floor(pushInfo.online/pushInfo.total*100)" style="margin-top: 20px"></el-progress>
<el-progress v-if="pushInfo.total === 0" :width="100" :stroke-width="10" type="circle" :percentage="0" style="margin-top: 20px"></el-progress>
<div class="resourceInfo">
推流总数:{{pushInfo.total}}<br/>
在线数:{{pushInfo.online}}
</div>
</div>
<div style="width: 50%;height: 50%; float:left; ">
<el-progress v-if="proxyInfo.total > 0" :width="100" :stroke-width="10" type="circle" :percentage="Math.floor(proxyInfo.online/proxyInfo.total*100)" style="margin-top: 20px"></el-progress>
<el-progress v-if="proxyInfo.total === 0" :width="100" :stroke-width="10" type="circle" :percentage="0" style="margin-top: 20px"></el-progress>
<div class="resourceInfo">
拉流代理总数:{{proxyInfo.total}}<br/>
在线数:{{proxyInfo.online}}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'consoleResource',
data() {
return {
deviceInfo: {
total: 0,
online: 0
},
channelInfo: {
total: 0,
online: 0
},
pushInfo: {
total: 0,
online: 0
},
proxyInfo: {
total: 0,
online: 0
},
};
},
created() {
},
mounted() {
},
destroyed() {
},
methods: {
setData: function(data) {
this.deviceInfo = data.device;
this.channelInfo = data.channel;
this.pushInfo = data.push;
this.proxyInfo = data.proxy;
}
}
};
</script>
<style>
.resourceInfo{
width: 100%;
text-align: center;
font-size: 12px
}
.el-progress__text {
font-size: 18px !important;
}
</style>

View File

@ -1,188 +0,0 @@
<template>
<div id="gbChannelSelect" v-loading="getChannelListLoading">
<el-dialog
title="添加国标通道"
width="60%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
append-to-body
@close="close()"
>
<div class="page-header" style="width: 100%">
<div class="page-header-btn" style="width: 100%; text-align: left">
搜索:
<el-input @input="getChannelList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
在线状态:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="getChannelList" v-model="online" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
类型:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="getChannelList" v-model="channelType" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option v-for="item in Object.values($channelTypeList)" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-button size="mini" :loading="getChannelListLoading"
@click="getChannelList()">刷新</el-button>
<el-button type="primary" size="mini" style="float: right" @click="onSubmit"> </el-button>
</div>
</div>
<!--通道列表-->
<el-table size="small" ref="channelListTable" :data="channelList" :height="winHeight" style="width: 100%;"
header-row-class-name="table-header" @selection-change="handleSelectionChange" >
<el-table-column type="selection" width="55" >
</el-table-column>
<el-table-column prop="gbName" label="名称" min-width="180">
</el-table-column>
<el-table-column prop="gbDeviceId" label="编号" min-width="180">
</el-table-column>
<el-table-column prop="gbManufacturer" label="厂家" min-width="100">
</el-table-column>
<el-table-column label="类型" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" effect="plain" type="success" :style="$channelTypeList[scope.row.dataType].style" >{{$channelTypeList[scope.row.dataType].name}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.gbStatus === 'ON'">在线</el-tag>
<el-tag size="medium" type="info" v-if="scope.row.gbStatus !== 'ON'">离线</el-tag>
</div>
</template>
</el-table-column>
</el-table>
<div style="display: grid; grid-template-columns: 1fr 1fr">
<div style="text-align: left; line-height: 32px">
<i class="el-icon-info"></i> 未找到通道可在国标设备/通道中选择编辑按钮 选择{{dataType === 'civilCode'?'行政区划':'父节点编码'}}
</div>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[10, 25, 35, 50, 200, 1000, 50000]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "gbChannelSelect",
props: ['dataType', "selected"],
computed: {},
data() {
return {
showDialog: false,
channelList: [], //
currentDevice: {}, //
searchSrt: "",
online: null,
channelType: "",
videoComponentList: [],
updateLooper: 0, //
currentDeviceChannelsLenth: 0,
winHeight: 580,
currentPage: 1,
count: 10,
total: 0,
getChannelListLoading: false,
multipleSelection: [],
};
},
methods: {
initData: function () {
this.getChannelList();
},
currentChange: function (val) {
this.currentPage = val;
this.getChannelList();
},
handleSizeChange: function (val) {
this.count = val;
this.getChannelList();
},
handleSelectionChange: function (val){
this.multipleSelection = val;
},
getChannelList: function () {
this.getChannelListLoading = true;
if (this.dataType === "civilCode") {
this.$axios({
method: 'get',
url: `/api/common/channel/civilcode/list`,
params: {
page: this.currentPage,
count: this.count,
channelType: this.channelType,
query: this.searchSrt,
online: this.online,
}
}).then( (res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.channelList = res.data.data.list;
}
this.getChannelListLoading = false;
}).catch( (error)=> {
console.error(error);
this.getChannelListLoading = false;
});
}else {
this.$axios({
method: 'get',
url: `/api/common/channel/parent/list`,
params: {
page: this.currentPage,
count: this.count,
query: this.searchSrt,
channelType: this.channelType,
online: this.online,
}
}).then( (res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.channelList = res.data.data.list;
}
this.getChannelListLoading = false;
}).catch( (error)=> {
console.error(error);
this.getChannelListLoading = false;
});
}
},
openDialog: function (callback) {
this.listChangeCallback = callback;
this.showDialog = true;
this.initData();
},
onSubmit: function () {
if (this.listChangeCallback ) {
this.listChangeCallback(this.multipleSelection)
}
this.showDialog = false;
},
close: function () {
this.showDialog = false;
},
}
};
</script>

View File

@ -1,153 +0,0 @@
<template>
<div id="addUser" v-loading="getDeviceListLoading">
<el-dialog
title="添加国标设备通道"
width="60%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
append-to-body
@close="close()"
>
<div class="page-header" style="width: 100%">
<div class="page-header-btn" style="width: 100%; text-align: left">
搜索:
<el-input @input="getDeviceList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
在线状态:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="getDeviceList" v-model="online" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
<el-button size="mini" :loading="getDeviceListLoading"
@click="getDeviceList()">刷新</el-button>
<el-button type="primary" size="mini" style="float: right" @click="onSubmit"> </el-button>
</div>
</div>
<!--设备列表-->
<el-table size="medium" :data="deviceList" style="width: 100%;font-size: 12px;" :height="winHeight" header-row-class-name="table-header" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" >
</el-table-column>
<el-table-column prop="name" label="名称" min-width="160">
</el-table-column>
<el-table-column prop="deviceId" label="设备编号" min-width="200" >
</el-table-column>
<el-table-column prop="channelCount" label="通道数" min-width="120" >
</el-table-column>
<el-table-column prop="manufacturer" label="厂家" min-width="120" >
</el-table-column>
<el-table-column label="地址" min-width="160" >
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag v-if="scope.row.hostAddress" size="medium">{{ scope.row.hostAddress }}</el-tag>
<el-tag v-if="!scope.row.hostAddress" size="medium">未知</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="状态" min-width="120">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.onLine">在线</el-tag>
<el-tag size="medium" type="info" v-if="!scope.row.onLine">离线</el-tag>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[10, 25, 35, 50, 200, 1000, 50000]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</el-dialog>
</div>
</template>
<script>
export default {
name: "gbDeviceSelect",
props: {},
computed: {},
data() {
return {
showDialog: false,
deviceList: [], //
currentDevice: {}, //
searchSrt: "",
online: null,
videoComponentList: [],
updateLooper: 0, //
currentDeviceChannelsLenth: 0,
winHeight: 580,
currentPage: 1,
count: 10,
total: 0,
getDeviceListLoading: false,
multipleSelection: [],
};
},
mounted() {
this.initData();
},
methods: {
initData: function () {
this.getDeviceList();
},
currentChange: function (val) {
this.currentPage = val;
this.getDeviceList();
},
handleSizeChange: function (val) {
this.count = val;
this.getDeviceList();
},
handleSelectionChange: function (val){
this.multipleSelection = val;
},
getDeviceList: function () {
this.getDeviceListLoading = true;
this.$axios({
method: 'get',
url: `/api/device/query/devices`,
params: {
page: this.currentPage,
count: this.count,
query: this.searchSrt,
status: this.online,
}
}).then( (res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.deviceList = res.data.data.list;
}
this.getDeviceListLoading = false;
}).catch( (error)=> {
console.error(error);
this.getDeviceListLoading = false;
});
},
openDialog: function (callback) {
this.listChangeCallback = callback;
this.showDialog = true;
},
onSubmit: function () {
if (this.listChangeCallback ) {
this.listChangeCallback(this.multipleSelection)
}
this.showDialog = false;
},
close: function () {
this.showDialog = false;
},
}
};
</script>

View File

@ -1,386 +0,0 @@
<template>
<div id="mediaServerEdit" v-loading="isLoging">
<el-dialog
title="媒体节点"
:width="dialogWidth"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="formStep" style="margin-top: 1rem; margin-right: 20px;">
<el-form v-if="currentStep == 1" ref="mediaServerForm" :rules="rules" :model="mediaServerForm" label-width="140px" >
<el-form-item label="IP" prop="ip">
<el-input v-model="mediaServerForm.ip" placeholder="媒体服务IP" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="HTTP端口" prop="httpPort">
<el-input v-model="mediaServerForm.httpPort" placeholder="媒体服务HTTP端口" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="SECRET" prop="secret">
<el-input v-model="mediaServerForm.secret" placeholder="媒体服务SECRET" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="mediaServerForm.type" style="float: left; width: 100%" >
<el-option key="zlm" label="ZLMediaKit" value="zlm"></el-option>
<el-option key="abl" label="ABLMediaServer" value="abl"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" v-if="currentStep === 1 && serverCheck === 1" @click="next" >下一步</el-button>
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="checkServer" >测试</el-button>
<i v-if="serverCheck === 1" class="el-icon-success" style="color: #3caf36"></i>
<i v-if="serverCheck === -1" class="el-icon-error" style="color: #c80000"></i>
</div>
</el-form-item>
</el-form>
<el-row :gutter="24">
<el-col :span="12">
<el-form v-if="currentStep === 2 || currentStep === 3" ref="mediaServerForm1" :rules="rules" :model="mediaServerForm" label-width="140px" >
<el-form-item label="IP" prop="ip">
<el-input v-if="currentStep === 2" v-model="mediaServerForm.ip" disabled :disabled="mediaServerForm.defaultServer"></el-input>
<el-input v-if="currentStep === 3" v-model="mediaServerForm.ip" :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="HTTP端口" prop="httpPort">
<el-input v-if="currentStep === 2" v-model="mediaServerForm.httpPort" disabled :disabled="mediaServerForm.defaultServer"></el-input>
<el-input v-if="currentStep === 3" v-model="mediaServerForm.httpPort" :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="HOOK IP" prop="ip">
<el-input v-model="mediaServerForm.hookIp" placeholder="媒体服务HOOK_IP" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="SDP IP" prop="ip">
<el-input v-model="mediaServerForm.sdpIp" placeholder="媒体服务SDP_IP" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="流IP" prop="ip">
<el-input v-model="mediaServerForm.streamIp" placeholder="媒体服务流IP" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="HTTPS PORT" prop="httpSSlPort">
<el-input v-model="mediaServerForm.httpSSlPort" placeholder="媒体服务HTTPS_PORT" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="RTSP PORT" prop="rtspPort">
<el-input v-model="mediaServerForm.rtspPort" placeholder="媒体服务RTSP_PORT" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="RTSPS PORT" prop="rtspSSLPort">
<el-input v-model="mediaServerForm.rtspSSLPort" placeholder="媒体服务RTSPS_PORT" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12">
<el-form v-if="currentStep === 2 || currentStep === 3" ref="mediaServerForm2" :rules="rules" :model="mediaServerForm" label-width="180px" >
<el-form-item label="RTMP PORT" prop="rtmpPort">
<el-input v-model="mediaServerForm.rtmpPort" placeholder="媒体服务RTMP_PORT" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="RTMPS PORT" prop="rtmpSSlPort">
<el-input v-model="mediaServerForm.rtmpSSlPort" placeholder="媒体服务RTMPS_PORT" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="SECRET" prop="secret">
<el-input v-if="currentStep === 2" v-model="mediaServerForm.secret" disabled :disabled="mediaServerForm.defaultServer"></el-input>
<el-input v-if="currentStep === 3" v-model="mediaServerForm.secret" :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="自动配置媒体服务" >
<el-switch v-model="mediaServerForm.autoConfig" :disabled="mediaServerForm.defaultServer"></el-switch>
</el-form-item>
<el-form-item label="收流端口模式" >
<el-switch active-text="多端口" inactive-text="单端口" @change="portRangeChange" v-model="mediaServerForm.rtpEnable" :disabled="mediaServerForm.defaultServer"></el-switch>
</el-form-item>
<el-form-item v-if="!mediaServerForm.rtpEnable" label="收流端口" prop="rtpProxyPort">
<el-input v-model.number="mediaServerForm.rtpProxyPort" clearable :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item v-if="mediaServerForm.rtpEnable" label="收流端口" >
<el-input v-model="rtpPortRange1" placeholder="起始" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange1" :disabled="mediaServerForm.defaultServer"></el-input>
-
<el-input v-model="rtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item v-if="mediaServerForm.sendRtpEnable" label="发流端口" >
<el-input v-model="sendRtpPortRange1" placeholder="起始" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange1" :disabled="mediaServerForm.defaultServer"></el-input>
-
<el-input v-model="sendRtpPortRange2" placeholder="终止" @change="portRangeChange" clearable style="width: 100px" prop="rtpPortRange2" :disabled="mediaServerForm.defaultServer"></el-input>
</el-form-item>
<el-form-item label="录像管理服务端口" prop="recordAssistPort">
<el-input v-model.number="mediaServerForm.recordAssistPort" :disabled="mediaServerForm.defaultServer">
<!-- <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">测试</el-button>-->
<el-button v-if="mediaServerForm.recordAssistPort > 0" class="el-icon-check" slot="append" type="primary" @click="checkRecordServer"></el-button>
</el-input>
<i v-if="recordServerCheck == 1" class="el-icon-success" style="color: #3caf36; position: absolute;top: 14px;"></i>
<i v-if="recordServerCheck == 2" class="el-icon-loading" style="color: #3caf36; position: absolute;top: 14px;"></i>
<i v-if="recordServerCheck === -1" class="el-icon-error" style="color: #c80000; position: absolute;top: 14px;"></i>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button v-if="!mediaServerForm.defaultServer" type="primary" @click="onSubmit" >提交</el-button>
<el-button v-if="!mediaServerForm.defaultServer" @click="close">取消</el-button>
<el-button v-if="mediaServerForm.defaultServer" @click="close">关闭</el-button>
</div>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</el-dialog>
</div>
</template>
<script>
import MediaServer from './../service/MediaServer'
export default {
name: "streamProxyEdit",
props: {},
computed: {},
created() {
this.setDialogWidth()
},
data() {
const isValidIp = (rule, value, callback) => { // IP
var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
console.log(this.mediaServerForm.ip)
if (!reg.test(this.mediaServerForm.ip)) {
return callback(new Error('请输入有效的IP地址'))
} else {
callback()
}
return true
}
const isValidPort = (rule, value, callback) => { // IP
var reg = /^(([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5]))$/
if (!reg.test(this.mediaServerForm.httpPort)) {
return callback(new Error('请输入有效的端口号'))
} else {
callback()
}
return true
}
return {
dialogWidth: 0,
defaultWidth: 1000,
listChangeCallback: null,
showDialog: false,
isLoging: false,
dialogLoading: false,
currentStep: 1,
platformList: [],
mediaServer: new MediaServer(),
serverCheck: 0,
recordServerCheck: 0,
mediaServerForm: {
id: "",
ip: "",
autoConfig: true,
hookIp: "",
sdpIp: "",
streamIp: "",
secret: "",
httpPort: "",
httpSSlPort: "",
recordAssistPort: "",
rtmpPort: "",
rtmpSSlPort: "",
rtpEnable: false,
rtpPortRange: "",
sendRtpPortRange: "",
rtpProxyPort: "",
rtspPort: "",
rtspSSLPort: "",
type: "zlm",
},
rtpPortRange1:30000,
rtpPortRange2:30500,
sendRtpPortRange1:50000,
sendRtpPortRange2:60000,
rules: {
ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }],
httpPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
httpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
recordAssistPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtmpPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtmpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtpProxyPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtspPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
rtspSSLPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }],
secret: [{ required: true, message: "请输入secret", trigger: "blur" }],
timeout_ms: [{ required: true, message: "请输入FFmpeg推流成功超时时间", trigger: "blur" }],
ffmpeg_cmd_key: [{ required: false, message: "请输入FFmpeg命令参数模板可选", trigger: "blur" }],
},
};
},
methods: {
setDialogWidth() {
let val = document.body.clientWidth
if (val < this.defaultWidth) {
this.dialogWidth = '100%'
} else {
this.dialogWidth = this.defaultWidth + 'px'
}
},
openDialog: function (param, callback) {
this.showDialog = true;
this.listChangeCallback = callback;
if (param != null) {
this.mediaServerForm = param;
this.currentStep = 3;
if (param.rtpPortRange) {
let rtpPortRange = this.mediaServerForm.rtpPortRange.split(",");
let sendRtpPortRange = this.mediaServerForm.sendRtpPortRange.split(",");
if (rtpPortRange.length > 0) {
this.rtpPortRange1 = rtpPortRange[0]
this.rtpPortRange2 = rtpPortRange[1]
}
if (sendRtpPortRange.length > 0) {
this.sendRtpPortRange1 = sendRtpPortRange[0]
this.sendRtpPortRange2 = sendRtpPortRange[1]
}
}
}
},
checkServer: function() {
let that = this;
that.serverCheck = 0;
that.mediaServer.checkServer(that.mediaServerForm, data =>{
if (data.code === 0) {
if (parseInt(that.mediaServerForm.httpPort) !== parseInt(data.data.httpPort)) {
that.$message({
showClose: true,
message: '如果你正在使用docker部署你的媒体服务请注意的端口映射。',
type: 'warning',
duration: 0
});
}
let httpPort = that.mediaServerForm.httpPort;
that.mediaServerForm = data.data;
that.mediaServerForm.httpPort = httpPort;
that.mediaServerForm.autoConfig = true;
that.rtpPortRange1 = 30000
that.rtpPortRange2 = 30500
that.sendRtpPortRange1 = 50000
that.sendRtpPortRange2 = 60000
that.serverCheck = 1;
}else {
that.serverCheck = -1;
that.$message({
showClose: true,
message: data.msg,
type: "error",
});
}
})
},
next: function (){
this.currentStep = 2;
this.defaultWidth = 900;
this.setDialogWidth();
},
checkRecordServer: function (){
let that = this;
that.recordServerCheck = 2;
if (that.mediaServerForm.recordAssistPort <= 0 || that.mediaServerForm.recordAssistPort > 65535 ) {
that.recordServerCheck = -1;
that.$message({
showClose: true,
message: "端口号应该在-65535之间",
type: "error",
});
return;
}
that.mediaServer.checkRecordServer(that.mediaServerForm, data =>{
if (data.code === 0) {
that.recordServerCheck = 1;
}else {
that.recordServerCheck = -1;
that.$message({
showClose: true,
message: data.msg,
type: "error",
});
}
})
},
onSubmit: function () {
this.dialogLoading = true;
let that = this;
that.mediaServer.addServer(this.mediaServerForm, data => {
if (data.code === 0) {
that.$message({
showClose: true,
message: "保存成功",
type: "success",
});
if (this.listChangeCallback) this.listChangeCallback();
that.close()
}else {
that.$message({
showClose: true,
message: data.msg,
type: "error",
});
}
})
},
close: function () {
this.showDialog = false;
this.dialogLoading = false;
this.mediaServerForm = {
id: "",
ip: "",
autoConfig: true,
hookIp: "",
sdpIp: "",
streamIp: "",
secret: "",
httpPort: "",
httpSSlPort: "",
recordAssistPort: "",
rtmpPort: "",
rtmpSSlPort: "",
rtpEnable: false,
rtpPortRange: "",
sendRtpPortRange: "",
rtpProxyPort: "",
rtspPort: "",
rtspSSLPort: "",
};
this.rtpPortRange1 = 30500;
this.rtpPortRange2 = 30500;
this.sendRtpPortRange1 = 50000;
this.sendRtpPortRange2 = 60000;
this.listChangeCallback = null
this.currentStep = 1;
},
deviceGBIdExit: async function (deviceGbId) {
var result = false;
var that = this;
await that.$axios({
method: 'get',
url:`/api/platform/exit/${deviceGbId}`
}).then(function (res) {
result = res.data;
}).catch(function (error) {
console.log(error);
});
return result;
},
checkExpires: function() {
if (this.platform.enable && this.platform.expires == "0") {
this.platform.expires = "300";
}
},
portRangeChange: function() {
if (this.mediaServerForm.rtpEnable) {
this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2
this.mediaServerForm.sendRtpPortRange = this.sendRtpPortRange1 + "," + this.sendRtpPortRange2
}
}
},
};
</script>

View File

@ -1,294 +0,0 @@
<template>
<div id="addStreamProxy" v-loading="isLoging">
<el-dialog
title="添加代理"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-top: 1rem;margin-right: 100px;">
<el-form ref="streamProxy" :rules="rules" :model="proxyParam" label-width="140px" >
<el-form-item label="类型" prop="type">
<el-select
v-model="proxyParam.type"
style="width: 100%"
placeholder="请选择代理类型"
>
<el-option label="默认" value="default"></el-option>
<el-option label="FFmpeg" value="ffmpeg"></el-option>
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="proxyParam.name" clearable></el-input>
</el-form-item>
<el-form-item label="流应用名" prop="app">
<el-input v-model="proxyParam.app" clearable></el-input>
</el-form-item>
<el-form-item label="流ID" prop="stream">
<el-input v-model="proxyParam.stream" clearable></el-input>
</el-form-item>
<el-form-item label="拉流地址" prop="url" v-if="proxyParam.type=='default'">
<el-input v-model="proxyParam.url" clearable></el-input>
</el-form-item>
<el-form-item label="拉流地址" prop="srcUrl" v-if="proxyParam.type=='ffmpeg'">
<el-input v-model="proxyParam.srcUrl" clearable></el-input>
</el-form-item>
<el-form-item label="超时时间:毫秒" prop="timeoutMs" v-if="proxyParam.type=='ffmpeg'">
<el-input v-model="proxyParam.timeoutMs" clearable></el-input>
</el-form-item>
<el-form-item label="节点选择" prop="rtpType">
<el-select
v-model="proxyParam.mediaServerId"
@change="mediaServerIdChange"
style="width: 100%"
placeholder="请选择拉流节点"
>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="FFmpeg命令模板" prop="ffmpegCmdKey" v-if="proxyParam.type=='ffmpeg'">
<el-select
v-model="proxyParam.ffmpegCmdKey"
style="width: 100%"
placeholder="请选择FFmpeg命令模板"
>
<el-option
v-for="item in Object.keys(ffmpegCmdList)"
:key="item"
:label="ffmpegCmdList[item]"
:value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="国标编码" prop="gbId">
<el-input v-model="proxyParam.gbId" placeholder="设置国标编码可推送到国标" clearable></el-input>
</el-form-item>
<el-form-item label="拉流方式" prop="rtpType" v-if="proxyParam.type=='default'">
<el-select
v-model="proxyParam.rtpType"
style="width: 100%"
placeholder="请选择拉流方式"
>
<el-option label="TCP" value="0"></el-option>
<el-option label="UDP" value="1"></el-option>
<el-option label="组播" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="无人观看" prop="rtpType" >
<el-radio v-model="proxyParam.noneReader" label="0">不做处理</el-radio>
<el-radio v-model="proxyParam.noneReader" label="1">停用</el-radio>
<el-radio v-model="proxyParam.noneReader" label="2">移除</el-radio>
<!-- <el-select-->
<!-- @change="noneReaderHandler"-->
<!-- v-model="proxyParam.noneReader"-->
<!-- style="width: 100%"-->
<!-- placeholder="请选择无人观看的处理方式"-->
<!-- >-->
<!-- <el-option label="不做处理" value="0"></el-option>-->
<!-- <el-option label="停用" value="1"></el-option>-->
<!-- <el-option label="移除" value="2"></el-option>-->
<!-- </el-select>-->
</el-form-item>
<el-form-item label="其他选项">
<div style="float: left;">
<el-checkbox label="启用" v-model="proxyParam.enable" ></el-checkbox>
<el-checkbox label="开启音频" v-model="proxyParam.enableAudio" ></el-checkbox>
<el-checkbox label="录制" v-model="proxyParam.enableMp4" ></el-checkbox>
</div>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit" :loading="dialogLoading" >{{onSubmit_text}}</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
import MediaServer from './../service/MediaServer'
export default {
name: "streamProxyEdit",
props: {},
computed: {},
created() {},
data() {
// var deviceGBIdRules = async (rule, value, callback) => {
// console.log(value);
// if (value === "") {
// callback(new Error(""));
// } else {
// var exit = await this.deviceGBIdExit(value);
// console.log(exit);
// console.log(exit == "true");
// console.log(exit === "true");
// if (exit) {
// callback(new Error(""));
// } else {
// callback();
// }
// }
// };
return {
listChangeCallback: null,
showDialog: false,
isLoging: false,
dialogLoading: false,
onSubmit_text: "保存",
platformList: [],
mediaServer: new MediaServer(),
proxyParam: {
name: null,
type: "default",
app: null,
stream: null,
url: "",
srcUrl: null,
timeoutMs: null,
ffmpegCmdKey: null,
gbId: null,
rtpType: null,
enable: true,
enableAudio: true,
enableMp4: false,
noneReader: null,
enableRemoveNoneReader: false,
enableDisableNoneReader: false,
platformGbId: null,
mediaServerId: null,
},
mediaServerList:{},
ffmpegCmdList:{},
rules: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
app: [{ required: true, message: "请输入应用名", trigger: "blur" }],
stream: [{ required: true, message: "请输入流ID", trigger: "blur" }],
url: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],
srcUrl: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],
timeoutMs: [{ required: true, message: "请输入FFmpeg推流成功超时时间", trigger: "blur" }],
ffmpegCmdKey: [{ required: false, message: "请输入FFmpeg命令参数模板可选", trigger: "blur" }],
},
};
},
methods: {
openDialog: function (proxyParam, callback) {
this.showDialog = true;
this.listChangeCallback = callback;
if (proxyParam != null) {
this.proxyParam = proxyParam;
this.proxyParam.noneReader = null;
}
let that = this;
this.$axios({
method: 'get',
url:`/api/platform/query/10000/1`
}).then(function (res) {
that.platformList = res.data.data.list;
}).catch(function (error) {
console.log(error);
});
this.mediaServer.getOnlineMediaServerList((data)=>{
this.mediaServerList = data.data;
this.proxyParam.mediaServerId = this.mediaServerList[0].id
this.mediaServerIdChange()
})
},
mediaServerIdChange:function (){
let that = this;
if (that.proxyParam.mediaServerId !== "auto"){
that.$axios({
method: 'get',
url:`/api/proxy/ffmpeg_cmd/list`,
params: {
mediaServerId: that.proxyParam.mediaServerId
}
}).then(function (res) {
that.ffmpegCmdList = res.data.data;
that.proxyParam.ffmpegCmdKey = Object.keys(res.data.data)[0];
}).catch(function (error) {
console.log(error);
});
}
},
onSubmit: function () {
this.dialogLoading = true;
this.noneReaderHandler();
this.$axios({
method: 'post',
url:`/api/proxy/save`,
data: this.proxyParam
}).then((res)=> {
this.dialogLoading = false;
if (typeof (res.data.code) != "undefined" && res.data.code === 0) {
this.$message({
showClose: true,
message: res.data.msg,
type: "success",
});
this.showDialog = false;
if (this.listChangeCallback != null) {
this.listChangeCallback();
this.dialogLoading = false;
}
}
}).catch((error) =>{
console.log(error);
this.dialogLoading = false;
});
},
close: function () {
this.showDialog = false;
this.dialogLoading = false;
this.$refs.streamProxy.resetFields();
},
deviceGBIdExit: async function (deviceGbId) {
var result = false;
var that = this;
await that.$axios({
method: 'get',
url:`/api/platform/exit/${deviceGbId}`
}).then(function (res) {
result = res.data;
}).catch(function (error) {
console.log(error);
});
return result;
},
checkExpires: function() {
if (this.platform.enable && this.platform.expires == "0") {
this.platform.expires = "300";
}
},
noneReaderHandler: function() {
if (this.proxyParam.noneReader === null || this.proxyParam.noneReader === "0") {
this.proxyParam.enableDisableNoneReader = false;
this.proxyParam.enableRemoveNoneReader = false;
}else if (this.proxyParam.noneReader === "1"){
this.proxyParam.enableDisableNoneReader = true;
this.proxyParam.enableRemoveNoneReader = false;
}else if (this.proxyParam.noneReader ==="2"){
this.proxyParam.enableDisableNoneReader = false;
this.proxyParam.enableRemoveNoneReader = true;
}
},
},
};
</script>

View File

@ -1,118 +0,0 @@
<template>
<div id="SyncChannelProgress" v-loading="isLoging">
<el-dialog
width="240px"
top="13%"
:append-to-body="true"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
:show-close="true"
@close="close()"
style="text-align: center">
<el-progress type="circle" :percentage="percentage" :status="syncStatus"></el-progress>
<div style="text-align: center">
{{msg}}
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "SyncChannelProgress",
computed: {},
props: ['platformId'],
created() {},
data() {
return {
endCallBack: null,
syncStatus: null,
percentage: 0,
total: 0,
current: 0,
showDialog: false,
isLoging: false,
syncFlag: false,
deviceId: null,
timmer: null,
msg: "正在同步",
};
},
methods: {
openDialog: function (deviceId, endCallBack) {
console.log("deviceId: " + deviceId)
this.deviceId = deviceId;
this.showDialog = true;
this.msg = "";
this.percentage= 0;
this.total= 0;
this.current= 0;
this.syncFlag= false;
this.syncStatus = null;
this.endCallBack = endCallBack;
this.getProgress()
},
getProgress(){
this.$axios({
method: 'get',
url:`/api/device/query/${this.deviceId}/sync_status/`,
}).then((res) => {
if (res.data.code === 0) {
if (res.data.data != null) {
if (res.data.data.syncIng) {
if (res.data.data.total === 0) {
this.msg = `等待同步中`;
this.timmer = setTimeout(this.getProgress, 300)
}else {
this.syncFlag = true;
this.total = res.data.data.total;
this.current = res.data.data.current;
this.percentage = Math.floor(Number(res.data.data.current)/Number(res.data.data.total)* 10000)/100;
this.msg = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
this.timmer = setTimeout(this.getProgress, 300)
}
}else {
if (res.data.data.errorMsg){
this.msg = res.data.data.errorMsg;
this.syncStatus = "exception"
}else {
this.syncStatus = "success"
this.percentage = 100;
this.msg = '同步成功';
setTimeout(()=>{
this.showDialog = false;
}, 3000)
}
}
}else {
this.msg = res.data.msg;
this.timmer = setTimeout(this.getProgress, 300)
}
}else {
if (this.syncFlag) {
this.syncStatus = "success"
this.percentage = 100;
this.msg = '同步成功';
}else {
this.syncStatus = "error"
this.msg = res.data.msg;
}
}
}).catch((error) =>{
console.log(error);
this.syncStatus = "error"
this.msg = error.response.data.msg;
});
},
close: function (){
if (this.endCallBack) {
this.endCallBack()
}
window.clearTimeout(this.timmer)
}
},
};
</script>

View File

@ -1,257 +0,0 @@
<template>
<div id="gbChannelSelect" v-loading="getChannelListLoading">
<el-dialog
title="异常挂载通道"
width="60%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
append-to-body
@close="close()"
>
<div class="page-header" style="width: 100%">
<div class="page-header-btn" style="width: 100%; text-align: left">
搜索:
<el-input @input="getChannelList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
在线状态:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="getChannelList" v-model="online" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
类型:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="getChannelList" v-model="channelType" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option v-for="item in Object.values($channelTypeList)" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-button size="mini" type="primary" :loading="getChannelListLoading" :disabled="multipleSelection.length ===0"
@click="clearUnusualRegion()">清除</el-button>
<el-button size="mini" :loading="getChannelListLoading"
@click="clearUnusualRegion(true)">全部清除</el-button>
<el-button size="mini" :loading="getChannelListLoading"
@click="getChannelList()">刷新</el-button>
</div>
</div>
<!--通道列表-->
<el-table size="small" ref="channelListTable" :data="channelList" :height="winHeight" style="width: 100%;"
header-row-class-name="table-header" @selection-change="handleSelectionChange" >
<el-table-column type="selection" width="55" >
</el-table-column>
<el-table-column prop="gbName" label="名称" min-width="180">
</el-table-column>
<el-table-column prop="gbDeviceId" label="编号" min-width="180">
</el-table-column>
<el-table-column prop="gbManufacturer" label="厂家" min-width="100">
</el-table-column>
<el-table-column prop="gbCivilCode" label="行政区划" min-width="100">
</el-table-column>
<el-table-column label="类型" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" effect="plain" type="success" :style="$channelTypeList[scope.row.dataType].style">{{$channelTypeList[scope.row.dataType].name}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.gbStatus === 'ON'">在线</el-tag>
<el-tag size="medium" type="info" v-if="scope.row.gbStatus !== 'ON'">离线</el-tag>
</div>
</template>
</el-table-column>
</el-table>
<div style="display: grid; grid-template-columns: 1fr 1fr">
<div style="text-align: left; line-height: 32px">
<i class="el-icon-info"></i> 清除后通道可正常添加到分组节点
</div>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[10, 25, 35, 50, 200, 1000, 50000]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "UnusualGroupChannelSelect",
props: [],
computed: {},
data() {
return {
showDialog: false,
channelList: [], //
searchSrt: "",
online: null,
channelType: "",
winHeight: 580,
currentPage: 1,
count: 10,
total: 0,
getChannelListLoading: false,
multipleSelection: [],
};
},
methods: {
initData: function () {
this.getChannelList();
},
currentChange: function (val) {
this.currentPage = val;
this.getChannelList();
},
handleSizeChange: function (val) {
this.count = val;
this.getChannelList();
},
handleSelectionChange: function (val){
this.multipleSelection = val;
},
getChannelList: function () {
this.getChannelListLoading = true;
this.$axios({
method: 'get',
url: `/api/common/channel/parent/unusual/list`,
params: {
page: this.currentPage,
count: this.count,
channelType: this.channelType,
query: this.searchSrt,
online: this.online,
}
}).then( (res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
for (let i = 0; i < res.data.data.list.length; i++) {
res.data.data.list[i]["addRegionLoading"] = false
}
this.channelList = res.data.data.list;
}
}).catch( (error)=> {
console.error(error);
}).finally(()=>{
this.getChannelListLoading = false;
})
},
openDialog: function () {
this.showDialog = true;
this.initData();
},
close: function () {
this.showDialog = false;
},
clearUnusualRegion: function (all) {
let channels = null
if (all || this.multipleSelection.length > 0 ) {
channels = []
for (let i = 0; i < this.multipleSelection.length; i++) {
channels.push(this.multipleSelection[i].gbId)
}
}
this.$axios({
method: 'post',
url: `/api/common/channel/parent/unusual/clear`,
data: {
all: all,
channelIds: channels
}
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "清除成功"
})
this.getChannelList()
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
}).finally(()=>{
this.loading = false
})
},
addRegion: function (row) {
row.addRegionLoading = true;
this.$axios({
method: 'get',
url: `/api/region/description`,
params: {
civilCode: row.gbCivilCode,
}
}).then((res) => {
if (res.data.code === 0) {
this.$confirm(`确定添加: ${res.data.data}`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(() => {
this.$axios({
method: 'get',
url: `/api/region/addByCivilCode`,
params: {
civilCode: row.gbCivilCode,
}
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "添加成功"
})
this.initData()
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
console.error(error);
});
}).catch(() => {
});
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
}).finally(()=>{
row.addRegionLoading = false;
})
},
}
};
</script>

View File

@ -1,270 +0,0 @@
<template>
<div id="gbChannelSelect" v-loading="getChannelListLoading">
<el-dialog
title="异常挂载通道"
width="60%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
append-to-body
@close="close()"
>
<div class="page-header" style="width: 100%">
<div class="page-header-btn" style="width: 100%; text-align: left">
搜索:
<el-input @input="getChannelList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
在线状态:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="getChannelList" v-model="online" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
类型:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="getChannelList" v-model="channelType" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option v-for="item in Object.values($channelTypeList)" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-button size="mini" type="primary" :loading="getChannelListLoading" :disabled="multipleSelection.length ===0"
@click="clearUnusualRegion()">清除</el-button>
<el-button size="mini" :loading="getChannelListLoading"
@click="clearUnusualRegion(true)">全部清除</el-button>
<el-button size="mini" :loading="getChannelListLoading"
@click="getChannelList()">刷新</el-button>
</div>
</div>
<!--通道列表-->
<el-table size="small" ref="channelListTable" :data="channelList" :height="winHeight" style="width: 100%;"
header-row-class-name="table-header" @selection-change="handleSelectionChange" >
<el-table-column type="selection" width="55" >
</el-table-column>
<el-table-column prop="gbName" label="名称" min-width="180">
</el-table-column>
<el-table-column prop="gbDeviceId" label="编号" min-width="180">
</el-table-column>
<el-table-column prop="gbManufacturer" label="厂家" min-width="100">
</el-table-column>
<el-table-column prop="gbCivilCode" label="行政区划" min-width="100">
</el-table-column>
<el-table-column label="类型" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" effect="plain" type="success" :style="$channelTypeList[scope.row.dataType].style">{{$channelTypeList[scope.row.dataType].name}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.gbStatus === 'ON'">在线</el-tag>
<el-tag size="medium" type="info" v-if="scope.row.gbStatus !== 'ON'">离线</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="操作" min-width="140" fixed="right">
<template v-slot:default="scope">
<el-button
size="medium"
type="text"
icon="el-icon-plus"
:loading="scope.row.addRegionLoading"
@click="addRegion(scope.row)"
>
添加
</el-button>
</template>
</el-table-column>
</el-table>
<div style="display: grid; grid-template-columns: 1fr 1fr">
<div style="text-align: left; line-height: 32px">
<i class="el-icon-info"></i> 清除后通道可正常添加到行政区划添加可以自动添加对应的行政区划节点
</div>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[10, 25, 35, 50, 200, 1000, 50000]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "UnusualRegionChannelSelect",
props: [],
computed: {},
data() {
return {
showDialog: false,
channelList: [], //
searchSrt: "",
online: null,
channelType: "",
winHeight: 580,
currentPage: 1,
count: 10,
total: 0,
getChannelListLoading: false,
multipleSelection: [],
};
},
methods: {
initData: function () {
this.getChannelList();
},
currentChange: function (val) {
this.currentPage = val;
this.getChannelList();
},
handleSizeChange: function (val) {
this.count = val;
this.getChannelList();
},
handleSelectionChange: function (val){
this.multipleSelection = val;
},
getChannelList: function () {
this.getChannelListLoading = true;
this.$axios({
method: 'get',
url: `/api/common/channel/civilCode/unusual/list`,
params: {
page: this.currentPage,
count: this.count,
channelType: this.channelType,
query: this.searchSrt,
online: this.online,
}
}).then( (res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
for (let i = 0; i < res.data.data.list.length; i++) {
res.data.data.list[i]["addRegionLoading"] = false
}
this.channelList = res.data.data.list;
}
}).catch( (error)=> {
console.error(error);
}).finally(()=>{
this.getChannelListLoading = false;
})
},
openDialog: function () {
this.showDialog = true;
this.initData();
},
close: function () {
this.showDialog = false;
},
clearUnusualRegion: function (all) {
let channels = null
if (all || this.multipleSelection.length > 0 ) {
channels = []
for (let i = 0; i < this.multipleSelection.length; i++) {
channels.push(this.multipleSelection[i].gbId)
}
}
this.$axios({
method: 'post',
url: `/api/common/channel/civilCode/unusual/clear`,
data: {
all: all,
channelIds: channels
}
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "清除成功"
})
this.getChannelList()
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
}).finally(()=>{
this.loading = false
})
},
addRegion: function (row) {
row.addRegionLoading = true;
this.$axios({
method: 'get',
url: `/api/region/description`,
params: {
civilCode: row.gbCivilCode,
}
}).then((res) => {
if (res.data.code === 0) {
this.$confirm(`确定添加: ${res.data.data}`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(() => {
this.$axios({
method: 'get',
url: `/api/region/addByCivilCode`,
params: {
civilCode: row.gbCivilCode,
}
}).then((res) => {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "添加成功"
})
this.initData()
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
console.error(error);
});
}).catch(() => {
});
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
})
}).finally(()=>{
row.addRegionLoading = false;
})
},
}
};
</script>

View File

@ -1,154 +0,0 @@
<template>
<div id="addUser" v-loading="isLoging">
<el-dialog
title="添加用户"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-right: 20px;">
<el-form ref="passwordForm" :rules="rules" status-icon label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="用户类型" prop="roleId" >
<el-select v-model="roleId" placeholder="请选择" style="width: 100%">
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="confirmPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "addUser",
props: {},
computed: {},
created() {
this.getAllRole();
},
data() {
let validatePass1 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入新密码'));
} else {
if (this.confirmPassword !== '') {
this.$refs.passwordForm.validateField('confirmPassword');
}
callback();
}
};
let validatePass2 = (rule, value, callback) => {
if (this.confirmPassword === '') {
callback(new Error('请再次输入密码'));
} else if (this.confirmPassword !== this.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
value:"",
options: [],
loading: false,
username: null,
password: null,
roleId: null,
confirmPassword: null,
listChangeCallback: null,
showDialog: false,
isLoging: false,
rules: {
newPassword: [{required: true, validator: validatePass1, trigger: "blur"}, {
pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,20}$/,
message: "密码长度在8-20位之间,由字母+数字+特殊字符组成",
},],
confirmPassword: [{required: true, validator: validatePass2, trigger: "blur"}],
},
};
},
methods: {
openDialog: function (callback) {
this.listChangeCallback = callback;
this.showDialog = true;
},
onSubmit: function () {
this.$axios({
method: 'post',
url: "/api/user/add",
params: {
username: this.username,
password: this.password,
roleId: this.roleId
}
}).then((res) => {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '添加成功',
type: 'success',
});
this.showDialog = false;
this.listChangeCallback()
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error) => {
console.error(error)
});
},
close: function () {
this.showDialog = false;
this.password = null;
this.confirmPassword = null;
this.username = null;
this.roleId = null;
},
getAllRole:function () {
this.$axios({
method: 'get',
url: "/api/role/all"
}).then((res) => {
this.loading = true;
if (res.data.code === 0) {
this.options=res.data.data
}
}).catch((error) => {
console.error(error)
});
}
},
};
</script>

View File

@ -1,139 +0,0 @@
<template>
<div id="addUserApiKey" v-loading="isLoading">
<el-dialog
title="添加ApiKey"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-right: 20px;">
<el-form ref="formRef" :model="form" :rules="rules" status-icon label-width="80px">
<el-form-item label="应用名" prop="app">
<el-input
v-model="form.app"
property="app"
autocomplete="off"/>
</el-form-item>
<el-form-item label="启用状态" prop="enable" style="text-align: left">
<el-switch
v-model="form.enable"
property="enable"
active-text="启用"
inactive-text="停用"/>
</el-form-item>
<el-form-item label="过期时间" prop="expiresAt" style="text-align: left">
<el-date-picker v-model="form.expiresAt"
style="width: 100%"
property="expiresAt"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd HH:mm:ss"
placeholder="选择过期时间"/>
</el-form-item>
<el-form-item label="备注信息" prop="remark">
<el-input v-model="form.remark"
type="textarea"
property="remark"
autocomplete="off"
:autosize="{ minRows: 5}"
maxlength="255"
show-word-limit/>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'addUserApiKey',
props: {},
computed: {},
created() {
},
data() {
return {
userId: null,
form: {
app: null,
enable: true,
expiresAt: null,
remark: null
},
rules: {
app: [{required: true, trigger: 'blur', message: '应用名不能为空'}]
},
listChangeCallback: null,
showDialog: false,
isLoading: false
};
},
methods: {
resetForm() {
this.form = {
app: null,
enable: true,
expiresAt: null,
remark: null
}
},
openDialog(userId, callback) {
this.resetForm()
this.userId = userId
this.listChangeCallback = callback
this.showDialog = true
},
onSubmit() {
this.$refs.formRef.validate((valid) => {
if (valid) {
this.$axios({
method: 'post',
url: '/api/userApiKey/add',
params: {
userId: this.userId,
app: this.form.app,
enable: this.form.enable,
expiresAt: this.form.expiresAt,
remark: this.form.remark,
}
}).then((res) => {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '添加成功',
type: 'success'
});
this.showDialog = false
if (this.listChangeCallback) {
this.listChangeCallback()
}
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error) => {
console.error(error)
});
}
});
},
close() {
this.showDialog = false
}
},
};
</script>

View File

@ -1,158 +0,0 @@
<template>
<div id="catalogEdit" v-loading="isLoging">
<el-dialog
title="通道共享"
width="40%"
top="2rem"
:append-to-body="true"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-top: 1rem;margin-right: 100px;">
<el-form ref="form" :rules="rules" :model="form" label-width="140px" >
<el-form-item label="节点编号" prop="id" >
<el-input v-model="form.id" :disabled="isEdit" clearable></el-input>
</el-form-item>
<el-form-item label="节点名称" prop="name">
<el-input v-model="form.name" clearable></el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit" >确认</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "catalogEdit",
computed: {},
props: ['platformId', 'platformDeviceId'],
created() {},
data() {
let checkId = (rule, value, callback) => {
console.log("checkId")
console.log(rule)
console.log(value)
console.log(value.length)
console.log(this.level)
if (!value) {
return callback(new Error('编号不能为空'));
}
if (value.trim().length <= 8) {
if (value.trim().length%2 !== 0) {
return callback(new Error('行政区划编号必须为2/4/6/8位'));
}
if (this.form.parentId !== this.platformDeviceId && this.form.parentId.length >= value.trim().length) {
if (this.form.parentId.length === 20) {
return callback(new Error('业务分组/虚拟组织下不可创建行政区划'));
}else {
return callback(new Error('行政区划编号长度应该每次两位递增'));
}
}
}else {
if (value.trim().length !== 20) {
return callback(new Error('编号必须为2/4/6/8位的行政区划或20位的虚拟组织/业务分组'));
}
let catalogType = value.substring(10, 13);
console.log(catalogType)
if (catalogType !== "215" && catalogType !== "216") {
return callback(new Error('编号错误业务分组11-13位为215虚拟组织11-13位为216'));
}
if (catalogType === "216") {
if (this.form.parentId !== this.platformDeviceId){
if (this.form.parentId.length <= 8) {
return callback(new Error('编号错误建立虚拟组织前必须先建立业务分组11-13位为215'));
}
}
}
if (catalogType === "215") {
if (this.form.parentId.length === "215") {
return callback(new Error('编号错误业务分组下只能建立虚拟组织11-13位为216'));
}
}
}
callback();
}
return {
submitCallback: null,
showDialog: false,
isLoging: false,
isEdit: false,
level: 0,
form: {
id: null,
name: null,
platformId: null,
parentId: null,
},
rules: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
id: [{ required: true, trigger: "blur",validator: checkId }]
},
};
},
methods: {
openDialog: function (isEdit, id, name, parentId, level, callback) {
console.log("parentId: " + parentId)
console.log(this.form)
this.isEdit = isEdit;
this.form.id = id;
this.form.name = name;
this.form.platformId = this.platformId;
this.form.parentId = parentId;
this.showDialog = true;
this.submitCallback = callback;
this.level = level;
},
onSubmit: function () {
this.$refs["form"].validate((valid) => {
if (valid) {
this.$axios({
method:"post",
url:`/api/platform/catalog/${!this.isEdit? "add":"edit"}`,
data: this.form
}).then((res)=> {
if (res.data.code === 0) {
if (this.submitCallback)this.submitCallback(this.form)
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
this.close();
})
.catch((error)=> {
console.log(error);
});
} else {
return false;
}
});
},
close: function () {
this.isEdit = false;
this.form.id = null;
this.form.name = null;
this.form.platformId = null;
this.form.parentId = null;
this.callback = null;
this.showDialog = false;
console.log(this.form)
},
},
};
</script>

View File

@ -1,132 +0,0 @@
<template>
<div id="changePassword" v-loading="isLoging">
<el-dialog
title="修改密码"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-right: 20px;">
<el-form ref="passwordForm" :rules="rules" status-icon label-width="80px">
<el-form-item label="旧密码" prop="oldPassword" >
<el-input v-model="oldPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newPassword" >
<el-input v-model="newPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="confirmPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
import crypto from 'crypto'
import userService from "../service/UserService";
export default {
name: "changePassword",
props: {},
computed: {},
created() {},
data() {
let validatePass0 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入旧密码'));
} else {
callback();
}
};
let validatePass1 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入新密码'));
} else {
if (this.confirmPassword !== '') {
this.$refs.passwordForm.validateField('confirmPassword');
}
callback();
}
};
let validatePass2 = (rule, value, callback) => {
if (this.confirmPassword === '') {
callback(new Error('请再次输入密码'));
} else if (this.confirmPassword !== this.newPassword) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
oldPassword: null,
newPassword: null,
confirmPassword: null,
showDialog: false,
isLoging: false,
rules: {
oldPassword: [{ required: true, validator: validatePass0, trigger: "blur" }],
newPassword: [{ required: true, validator: validatePass1, trigger: "blur" }, {
pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,20}$/,
message: "密码长度在8-20位之间,由字母+数字+特殊字符组成",
},],
confirmPassword: [{ required: true, validator: validatePass2, trigger: "blur" }],
},
};
},
methods: {
openDialog: function () {
this.showDialog = true;
},
onSubmit: function () {
this.$axios({
method: 'post',
url:"/api/user/changePassword",
params: {
oldPassword: crypto.createHash('md5').update(this.oldPassword, "utf8").digest('hex'),
password: this.newPassword
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '修改成功,请重新登录',
type: 'success'
});
this.showDialog = false;
setTimeout(()=>{
// cookie
userService.clearUserInfo();
this.$router.push('/login');
this.sseSource.close();
},800)
}else {
this.$message({
showClose: true,
message: '修改密码失败,是否已登录(接口鉴权关闭无法修改密码)',
type: 'error'
});
}
}).catch((error)=> {
console.error(error)
});
},
close: function () {
this.showDialog = false;
this.oldPassword = null;
this.newPassword = null;
this.confirmPassword = null;
},
},
};
</script>

View File

@ -1,121 +0,0 @@
<template>
<div id="changePassword" v-loading="isLoging">
<el-dialog
title="修改密码"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-right: 20px;">
<el-form ref="passwordForm" :rules="rules" status-icon label-width="80px">
<el-form-item label="新密码" prop="newPassword" >
<el-input v-model="newPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="confirmPassword" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "changePasswordForAdmin",
props: {},
computed: {},
created() {},
data() {
let validatePass1 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入新密码'));
} else {
if (this.confirmPassword !== '') {
this.$refs.passwordForm.validateField('confirmPassword');
}
callback();
}
};
let validatePass2 = (rule, value, callback) => {
if (this.confirmPassword === '') {
callback(new Error('请再次输入密码'));
} else if (this.confirmPassword !== this.newPassword) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
newPassword: null,
confirmPassword: null,
userId: null,
showDialog: false,
isLoging: false,
listChangeCallback: null,
form: {},
rules: {
newPassword: [{ required: true, validator: validatePass1, trigger: "blur" }, {
pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,20}$/,
message: "密码长度在8-20位之间,由字母+数字+特殊字符组成",
},],
confirmPassword: [{ required: true, validator: validatePass2, trigger: "blur" }],
},
};
},
methods: {
openDialog: function (row, callback) {
console.log(row)
this.showDialog = true;
this.listChangeCallback = callback;
if (row != null) {
this.form = row;
}
},
onSubmit: function () {
this.$axios({
method: 'post',
url:"/api/user/changePasswordForAdmin",
params: {
password: this.newPassword,
userId: this.form.id,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '修改成功',
type: 'success'
});
this.showDialog = false;
}else {
this.$message({
showClose: true,
message: '修改密码失败,是否已登录(接口鉴权关闭无法修改密码)',
type: 'error'
});
}
}).catch((error)=> {
console.error(error)
});
},
close: function () {
this.showDialog = false;
this.newPassword = null;
this.confirmPassword = null;
this.userId=null;
this.adminId=null;
},
},
};
</script>

View File

@ -1,101 +0,0 @@
<template>
<div id="changepushKey" v-loading="isLoging">
<el-dialog
title="修改pushKey"
width="42%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-right: 18px;">
<el-form ref="pushKeyForm" :rules="rules" status-icon label-width="86px">
<el-form-item label="新pushKey" prop="newPushKey" >
<el-input v-model="newPushKey" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "changePushKey",
props: {},
computed: {},
created() {},
data() {
let validatePass1 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入新pushKey'));
} else {
callback();
}
};
return {
newPushKey: null,
confirmpushKey: null,
userId: null,
showDialog: false,
isLoging: false,
listChangeCallback: null,
form: {},
rules: {
newpushKey: [{ required: true, validator: validatePass1, trigger: "blur" }],
},
};
},
methods: {
openDialog: function (row, callback) {
console.log(row)
this.showDialog = true;
this.listChangeCallback = callback;
if (row != null) {
this.form = row;
}
},
onSubmit: function () {
this.$axios({
method: 'post',
url:"/api/user/changePushKey",
params: {
pushKey: this.newPushKey,
userId: this.form.id,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '修改成功',
type: 'success'
});
this.showDialog = false;
this.listChangeCallback();
}else {
this.$message({
showClose: true,
message: '修改pushKey失败是否已登录接口鉴权关闭无法修改pushKey',
type: 'error'
});
}
}).catch((error)=> {
console.error(error)
});
},
close: function () {
this.showDialog = false;
this.newpushKey = null;
this.userId=null;
this.adminId=null;
},
},
};
</script>

View File

@ -1,364 +0,0 @@
<template>
<el-dialog
title="生成国标编码"
width="65rem"
top="2rem"
center
:append-to-body="true"
:close-on-click-modal="false"
:visible.sync="showVideoDialog"
:destroy-on-close="false"
>
<el-tabs v-model="activeKey" style="padding: 0 1rem; margin: auto 0" @tab-click="getRegionList">
<el-tab-pane name="0" >
<div slot="label" >
<div class="show-code-item">{{ allVal[0].val }}</div>
<div style="text-align: center">{{ allVal[0].meaning }}</div>
</div>
<el-radio-group v-model="allVal[0].val" >
<el-radio v-for="item in regionList" :key="item.deviceId" :label="item.deviceId" style="line-height: 2rem">
{{ item.name }} - {{ item.deviceId }}
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="1">
<div slot="label">
<div class="show-code-item">{{ allVal[1].val }}</div>
<div style="text-align: center">{{ allVal[1].meaning }}</div>
</div>
<el-radio-group v-model="allVal[1].val" :disabled="allVal[1].lock">
<el-radio v-for="item in regionList" :key="item.deviceId" :label="item.deviceId.substring(2)" style="line-height: 2rem">
{{ item.name }} - {{ item.deviceId.substring(2) }}
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="2">
<div slot="label">
<div class="show-code-item">{{ allVal[2].val }}</div>
<div style="text-align: center">{{ allVal[2].meaning }}</div>
</div>
<el-radio-group v-model="allVal[2].val" :disabled="allVal[2].lock">
<el-radio v-for="item in regionList" :key="item.deviceId" :label="item.deviceId.substring(4)" style="line-height: 2rem">
{{ item.name }} - {{ item.deviceId.substring(4) }}
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="3">
请手动输入基层接入单位编码,两位数字
<div slot="label">
<div class="show-code-item">{{ allVal[3].val }}</div>
<div style="text-align: center">{{ allVal[3].meaning }}</div>
</div>
<el-input
type="text"
placeholder="请输入内容"
v-model="allVal[3].val"
maxlength="2"
:disabled="allVal[3].lock"
show-word-limit
>
</el-input>
</el-tab-pane>
<el-tab-pane name="4">
<div slot="label">
<div class="show-code-item">{{ allVal[4].val }}</div>
<div style="text-align: center; ">{{ allVal[4].meaning }}</div>
</div>
<el-radio-group v-model="allVal[4].val" :disabled="allVal[4].lock">
<el-radio v-for="item in industryCodeTypeList" :key="item.code" :label="item.code" style="line-height: 2rem">
{{ item.name }} - {{ item.code }}
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="5">
<div slot="label">
<div class="show-code-item">{{ allVal[5].val }}</div>
<div style="text-align: center">{{ allVal[5].meaning }}</div>
</div>
<el-radio-group v-model="allVal[5].val" :disabled="allVal[5].lock" >
<el-radio v-for="item in deviceTypeList" :label="item.code" :key="item.code" style="line-height: 2rem">
{{ item.name }} - {{ item.code }}
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="6">
<div slot="label">
<div class="show-code-item">{{ allVal[6].val }}</div>
<div style="text-align: center">{{ allVal[6].meaning }}</div>
</div>
<el-radio-group v-model="allVal[6].val" :disabled="allVal[6].lock">
<el-radio v-for="item in networkIdentificationTypeList" :label="item.code" :key="item.code" style="line-height: 2rem">
{{ item.name }} - {{ item.code }}
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="7">
请手动输入设备/用户序号, 六位数字
<div slot="label">
<div class="show-code-item">{{ allVal[7].val }}</div>
<div style="text-align: center">{{ allVal[7].meaning }}</div>
</div>
<el-input
type="text"
placeholder="请输入内容"
v-model="allVal[7].val"
maxlength="6"
:disabled="allVal[7].lock"
show-word-limit
>
</el-input>
</el-tab-pane>
</el-tabs>
<el-form style="">
<el-form-item style="margin-top: 22px; margin-bottom: 0;">
<div style="float:right;">
<el-button type="primary" @click="handleOk">保存</el-button>
<el-button @click="closeModel">取消</el-button>
</div>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script>
export default {
props: {},
computed: {},
data() {
return {
showVideoDialog: false,
activeKey: '0',
allVal: [
{
id: [1, 2],
meaning: '省级编码',
val: '11',
type: '中心编码',
lock: false,
},
{
id: [3, 4],
meaning: '市级编码',
val: '01',
type: '中心编码',
lock: false,
},
{
id: [5, 6],
meaning: '区级编码',
val: '01',
type: '中心编码',
lock: false,
},
{
id: [7, 8],
meaning: '基层接入单位编码',
val: '01',
type: '中心编码',
lock: false,
},
{
id: [9, 10],
meaning: '行业编码',
val: '00',
type: '行业编码',
lock: false,
},
{
id: [11, 13],
meaning: '类型编码',
val: '132',
type: '类型编码',
lock: false,
},
{
id: [14],
meaning: '网络标识编码',
val: '7',
type: '网络标识',
lock: false,
},
{
id: [15, 20],
meaning: '设备/用户序号',
val: '000001',
type: '序号',
lock: false,
}
],
regionList: [],
deviceTypeList: [],
industryCodeTypeList: [],
networkIdentificationTypeList: [],
endCallBck: null,
};
},
methods: {
openDialog: function (endCallBck, code, lockIndex, lockContent) {
console.log(code)
this.showVideoDialog = true
this.activeKey= '0';
this.regionList = []
this.getRegionList()
if (typeof code != 'undefined' && code.length === 20) {
this.allVal[0].val = code.substring(0, 2)
this.allVal[1].val = code.substring(2, 4)
this.allVal[2].val = code.substring(4, 6)
this.allVal[3].val = code.substring(6, 8)
this.allVal[4].val = code.substring(8, 10)
this.allVal[5].val = code.substring(10, 13)
this.allVal[6].val = code.substring(13, 14)
this.allVal[7].val = code.substring(14)
}
console.log(this.allVal)
if (typeof lockIndex != 'undefined') {
this.allVal[lockIndex].lock = true
this.allVal[lockIndex].val = lockContent
}
this.endCallBck = endCallBck;
},
getRegionList: function() {
if (this.activeKey === '0' || this.activeKey === '1' || this.activeKey === '2') {
let parent = ''
if (this.activeKey === '1') {
parent = this.allVal[0].val
}
if (this.activeKey === '2') {
parent = this.allVal[0].val + this.allVal[1].val
}
if (this.activeKey !== '0' && parent === '') {
this.$message.error({
showClose: true,
message: '请先选择上级行政区划'
})
}
this.queryChildList(parent);
} else if (this.activeKey === '4') {
console.log(222)
this.queryIndustryCodeList();
} else if (this.activeKey === '5') {
this.queryDeviceTypeList();
} else if (this.activeKey === '6') {
this.queryNetworkIdentificationTypeList();
}
},
queryChildList: function(parent){
this.regionList = []
this.$axios({
method: 'get',
url: "/api/region/base/child/list",
params: {
parent: parent,
}
}).then((res) => {
if (res.data.code === 0) {
this.regionList = res.data.data
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
});
});
},
queryIndustryCodeList: function(){
this.industryCodeTypeList = []
this.$axios({
method: 'get',
url: "/api/common/channel/industry/list",
}).then((res) => {
if (res.data.code === 0) {
this.industryCodeTypeList = res.data.data
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
});
});
},
queryDeviceTypeList: function(){
this.deviceTypeList = []
this.$axios({
method: 'get',
url: "/api/common/channel/type/list",
}).then((res) => {
if (res.data.code === 0) {
this.deviceTypeList = res.data.data
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
});
});
},
queryNetworkIdentificationTypeList: function(){
this.networkIdentificationTypeList = []
this.$axios({
method: 'get',
url: "/api/common/channel/network/identification/list",
}).then((res) => {
if (res.data.code === 0) {
this.networkIdentificationTypeList = res.data.data
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
});
});
},
closeModel: function (){
this.showVideoDialog = false
},
handleOk: function() {
const code =
this.allVal[0].val +
this.allVal[1].val +
this.allVal[2].val +
this.allVal[3].val +
this.allVal[4].val +
this.allVal[5].val +
this.allVal[6].val +
this.allVal[7].val
console.log(code)
if (this.endCallBck) {
this.endCallBck(code)
}
this.showVideoDialog = false
}
},
};
</script>
<style>
.show-code-item {
text-align: center;
font-size: 3rem;
}
</style>

View File

@ -1,65 +0,0 @@
<template>
<div id="channelMapInfobox" style="display: none">
<div >
<el-descriptions class="margin-top" title="channel.name" :column="4" direction="vertical">
<el-descriptions-item label="生产厂商">{{channel.manufacture}}</el-descriptions-item>
<el-descriptions-item label="型号">{{channel.model}}</el-descriptions-item>
<el-descriptions-item label="设备归属" >{{channel.owner}}</el-descriptions-item>
<el-descriptions-item label="行政区域" >{{channel.civilCode}}</el-descriptions-item>
<el-descriptions-item label="安装地址" >{{channel.address}}</el-descriptions-item>
<el-descriptions-item label="云台类型" >{{channel.ptzTypeText}}</el-descriptions-item>
<el-descriptions-item label="经纬度" >{{channel.longitude}},{{channel.latitude}}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag size="small" v-if="channel.status === 1">在线</el-tag>
<el-tag size="small" v-if="channel.status === 0">离线</el-tag>
</el-descriptions-item>
</el-descriptions>
</div>
<devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer>
</div>
</template>
<script>
import devicePlayer from '../dialog/devicePlayer.vue'
export default {
name: "channelMapInfobox",
props: ['channel'],
computed: {devicePlayer},
created() {},
data() {
return {
showDialog: false,
isLoging: false
};
},
methods: {
play: function (){
let deviceId = this.channel.deviceId;
this.isLoging = true;
let channelId = this.channel.channelId;
console.log("通知设备推流1" + deviceId + " : " + channelId);
let that = this;
this.$axios({
method: 'get',
url: '/api/play/start/' + deviceId + '/' + channelId
}).then(function (res) {
that.isLoging = false;
if (res.data.code === 0) {
that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
streamInfo: res.data.data,
hasAudio: this.channel.hasAudio
});
} else {
that.$message.error(res.data.msg);
}
}).catch(function (e) {
});
},
close: function () {
},
},
};
</script>

View File

@ -1,65 +0,0 @@
<template>
<div id="chooseCivilCode" >
<el-dialog
title="选择行政区划"
width="30%"
top="5rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<RegionTree ref="regionTree" :showHeader=true :edit="true" :enableAddChannel="false" :clickEvent="treeNodeClickEvent"
:onChannelChange="onChannelChange" :treeHeight="'45vh'"></RegionTree>
<el-form>
<el-form-item>
<div style="text-align: right">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import RegionTree from "../common/RegionTree.vue";
export default {
name: "chooseCivilCode",
components: {RegionTree},
props: {},
computed: {},
created() {},
data() {
return {
showDialog: false,
endCallback: false,
regionDeviceId: "",
};
},
methods: {
openDialog: function (callback) {
this.showDialog = true;
this.endCallback = callback;
},
onSubmit: function () {
if (this.endCallback) {
this.endCallback(this.regionDeviceId)
}
this.close();
},
close: function () {
this.showDialog = false;
},
treeNodeClickEvent: function (region) {
this.regionDeviceId = region.deviceId;
},
onChannelChange: function (deviceId) {
//
},
},
};
</script>

View File

@ -1,71 +0,0 @@
<template>
<div id="chooseGroup" >
<el-dialog
title="选择虚拟组织"
width="30%"
top="5rem"
:append-to-body="true"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<GroupTree ref="regionTree" :showHeader=true :edit="true" :enableAddChannel="false" :clickEvent="treeNodeClickEvent"
:onChannelChange="onChannelChange" :treeHeight="'45vh'"></GroupTree>
<el-form>
<el-form-item>
<div style="text-align: right">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import GroupTree from "../common/GroupTree.vue";
export default {
name: "chooseCivilCode",
components: {GroupTree},
props: {},
computed: {},
created() {},
data() {
return {
showDialog: false,
endCallback: false,
groupDeviceId: "",
businessGroup: "",
};
},
methods: {
openDialog: function (callback) {
this.showDialog = true;
this.endCallback = callback;
},
onSubmit: function () {
if (this.endCallback) {
this.endCallback(this.groupDeviceId, this.businessGroup)
}
this.close();
},
close: function () {
this.showDialog = false;
},
treeNodeClickEvent: function (group) {
if (group.deviceId === "" || group.deviceId === group.businessGroup) {
return
}
this.groupDeviceId = group.deviceId;
this.businessGroup = group.businessGroup;
},
onChannelChange: function (deviceId) {
//
},
},
};
</script>

View File

@ -1,59 +0,0 @@
<template>
<div id="configInfo">
<el-dialog
title="系统信息"
width="=80%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-top: 1rem;margin-right: 100px;">
<el-descriptions title="国标服务信息" v-if="configInfoData.sip" :span="2">
<el-descriptions-item label="编号" >{{configInfoData.sip.id}}</el-descriptions-item>
<el-descriptions-item label="域">{{configInfoData.sip.domain}}</el-descriptions-item>
<el-descriptions-item label="IP">{{configInfoData.sip.showIp}}</el-descriptions-item>
<el-descriptions-item label="端口">{{configInfoData.sip.port}}</el-descriptions-item>
<el-descriptions-item label="密码">
<el-tag size="small">{{configInfoData.sip.password}}</el-tag>
</el-descriptions-item>
</el-descriptions>
<el-descriptions title="版本信息"v-if="configInfoData.version">
<el-descriptions-item label="版本">{{configInfoData.version.version}}</el-descriptions-item>
<el-descriptions-item label="编译时间">{{configInfoData.version.build_DATE}}</el-descriptions-item>
<el-descriptions-item label="GIT版本">{{configInfoData.version.git_Revision_SHORT}}</el-descriptions-item>
<el-descriptions-item label="GIT最后提交时间">{{configInfoData.version.git_DATE}}</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "configInfo",
props: {},
computed: {},
created() {},
data() {
return {
showDialog: false,
configInfoData: {
sip:{},
}
};
},
methods: {
openDialog: function (data) {
console.log(data)
this.showDialog = true;
this.configInfoData = data;
},
close: function () {
this.showDialog = false;
},
},
};
</script>

View File

@ -1,135 +0,0 @@
<template>
<div id="deviceEdit" v-loading="isLoging">
<el-dialog
title="设备编辑"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-top: 1rem;margin-right: 100px;">
<el-form ref="form" :rules="rules" :model="form" label-width="200px" >
<el-form-item label="设备编号" prop="deviceId">
<el-input v-if="isEdit" v-model="form.deviceId" disabled></el-input>
<el-input v-if="!isEdit" v-model="form.deviceId" clearable></el-input>
</el-form-item>
<el-form-item label="设备名称" prop="name">
<el-input v-model="form.name" clearable></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" clearable></el-input>
</el-form-item>
<el-form-item label="收流IP" prop="sdpIp">
<el-input type="sdpIp" v-model="form.sdpIp" clearable></el-input>
</el-form-item>
<el-form-item label="流媒体ID" prop="mediaServerId">
<el-select v-model="form.mediaServerId" style="float: left; width: 100%" >
<el-option key="auto" label="自动负载最小" value="auto"></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字符集" prop="charset" >
<el-select v-model="form.charset" style="float: left; width: 100%" >
<el-option key="GB2312" label="GB2312" value="gb2312"></el-option>
<el-option key="UTF-8" label="UTF-8" value="utf-8"></el-option>
</el-select>
</el-form-item>
<el-form-item label="其他选项">
<el-checkbox label="SSRC校验" v-model="form.ssrcCheck" style="float: left"></el-checkbox>
<el-checkbox label="作为消息通道" v-model="form.asMessageChannel" style="float: left"></el-checkbox>
<el-checkbox label="收到ACK后发流" v-model="form.broadcastPushAfterAck" style="float: left"></el-checkbox>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit" >确认</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
import MediaServer from '../service/MediaServer'
export default {
name: "deviceEdit",
props: {},
computed: {},
created() {},
data() {
return {
listChangeCallback: null,
showDialog: false,
isLoging: false,
hostNames:[],
mediaServerList: [], //
mediaServerObj : new MediaServer(),
form: {},
isEdit: false,
rules: {
deviceId: [{ required: true, message: "请输入设备编号", trigger: "blur" }]
},
};
},
methods: {
openDialog: function (row, callback) {
console.log(row)
this.showDialog = true;
this.isEdit = false;
if (row) {
this.isEdit = true;
}
this.form = {};
this.listChangeCallback = callback;
if (row != null) {
this.form = row;
}
this.getMediaServerList();
},
getMediaServerList: function (){
let that = this;
that.mediaServerObj.getOnlineMediaServerList((data)=>{
that.mediaServerList = data.data;
})
},
onSubmit: function () {
console.log("onSubmit");
console.log(this.form);
this.$axios({
method: 'post',
url:`/api/device/query/device/${this.isEdit?'update':'add'}`,
data: this.form
}).then((res) => {
console.log(res.data)
if (res.data.code === 0) {
this.listChangeCallback()
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
}).catch(function (error) {
console.log(error);
});
},
close: function () {
this.showDialog = false;
this.$refs.form.resetFields();
},
},
};
</script>

View File

@ -1,866 +0,0 @@
<template>
<div id="devicePlayer" v-loading="isLoging">
<el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()" v-if="showVideoDialog">
<div style="width: 100%; height: 100%">
<el-tabs type="card" :stretch="true" v-model="activePlayer" @tab-click="changePlayer"
v-if="Object.keys(this.player).length > 1">
<el-tab-pane label="Jessibuca" name="jessibuca">
<jessibucaPlayer v-if="activePlayer === 'jessibuca'" ref="jessibuca" :visible.sync="showVideoDialog"
:videoUrl="videoUrl" :error="videoError" :message="videoError"
:hasAudio="hasAudio" fluent autoplay live></jessibucaPlayer>
</el-tab-pane>
<el-tab-pane label="WebRTC" name="webRTC">
<rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog"
:videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px"
:hasAudio="hasAudio" fluent autoplay live></rtc-player>
</el-tab-pane>
<el-tab-pane label="h265web" name="h265web">
<h265web v-if="activePlayer === 'h265web'" ref="h265web"
:videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px"
:hasAudio="hasAudio" fluent autoplay live></h265web>
</el-tab-pane>
</el-tabs>
<jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca"
:visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError"
:hasAudio="hasAudio" fluent autoplay live></jessibucaPlayer>
<rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca"
:visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError"
height="100px" :hasAudio="hasAudio" fluent autoplay live></rtc-player>
<h265web v-if="Object.keys(this.player).length == 1 && this.player.h265web" ref="jessibuca"
:visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError"
height="100px" :hasAudio="hasAudio" fluent autoplay live></h265web>
</div>
<div id="shared" style="text-align: right; margin-top: 1rem;">
<el-tabs v-model="tabActiveName" @tab-click="tabHandleClick">
<el-tab-pane label="实时视频" name="media">
<div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
<span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址</span>
<el-input v-model="getPlayerShared.sharedUrl" :disabled="true">
<template slot="append">
<i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedUrl"
@success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
</template>
</el-input>
</div>
<div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
<span style="width: 5rem; line-height: 2.5rem; text-align: right;">iframe</span>
<el-input v-model="getPlayerShared.sharedIframe" :disabled="true">
<template slot="append">
<i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedIframe"
@success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
</template>
</el-input>
</div>
<div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
<span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址</span>
<el-input v-model="getPlayerShared.sharedRtmp" :disabled="true">
<el-button slot="append" icon="el-icon-document-copy" title="点击拷贝"
v-clipboard="getPlayerShared.sharedRtmp"
@success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>
<el-dropdown slot="prepend" v-if="streamInfo" trigger="click" @command="copyUrl">
<el-button>
更多地址<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu>
<el-dropdown-item v-if="streamInfo.flv" :command="streamInfo.flv">
<el-tag>FLV:</el-tag>
<span>{{ streamInfo.flv }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.https_flv" :command="streamInfo.https_flv">
<el-tag>FLV(https):</el-tag>
<span>{{ streamInfo.https_flv }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.ws_flv" :command="streamInfo.ws_flv">
<el-tag>FLV(ws):</el-tag>
<span>{{ streamInfo.ws_flv }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.wss_flv" :command="streamInfo.wss_flv">
<el-tag>FLV(wss):</el-tag>
<span>{{ streamInfo.wss_flv }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.fmp4" :command="streamInfo.fmp4">
<el-tag>FMP4:</el-tag>
<span>{{ streamInfo.fmp4 }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.https_fmp4" :command="streamInfo.https_fmp4">
<el-tag>FMP4(https):</el-tag>
<span>{{ streamInfo.https_fmp4 }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.ws_fmp4" :command="streamInfo.ws_fmp4">
<el-tag>FMP4(ws):</el-tag>
<span>{{ streamInfo.ws_fmp4 }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.wss_fmp4" :command="streamInfo.wss_fmp4">
<el-tag>FMP4(wss):</el-tag>
<span>{{ streamInfo.wss_fmp4 }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.hls" :command="streamInfo.hls">
<el-tag>HLS:</el-tag>
<span>{{ streamInfo.hls }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.https_hls" :command="streamInfo.https_hls">
<el-tag>HLS(https):</el-tag>
<span>{{ streamInfo.https_hls }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.ws_hls" :command="streamInfo.ws_hls">
<el-tag>HLS(ws):</el-tag>
<span>{{ streamInfo.ws_hls }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.wss_hls" :command="streamInfo.wss_hls">
<el-tag>HLS(wss):</el-tag>
<span>{{ streamInfo.wss_hls }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.ts" :command="streamInfo.ts">
<el-tag>TS:</el-tag>
<span>{{ streamInfo.ts }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.https_ts" :command="streamInfo.https_ts">
<el-tag>TS(https):</el-tag>
<span>{{ streamInfo.https_ts }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.ws_ts" :command="streamInfo.ws_ts">
<el-tag>TS(ws):</el-tag>
<span>{{ streamInfo.ws_ts }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.wss_ts" :command="streamInfo.wss_ts">
<el-tag>TS(wss):</el-tag>
<span>{{ streamInfo.wss_ts }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.rtc" :command="streamInfo.rtc">
<el-tag>RTC:</el-tag>
<span>{{ streamInfo.rtc }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.rtcs" :command="streamInfo.rtcs">
<el-tag>RTCS:</el-tag>
<span>{{ streamInfo.rtcs }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.rtmp" :command="streamInfo.rtmp">
<el-tag>RTMP:</el-tag>
<span>{{ streamInfo.rtmp }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.rtmps" :command="streamInfo.rtmps">
<el-tag>RTMPS:</el-tag>
<span>{{ streamInfo.rtmps }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.rtsp" :command="streamInfo.rtsp">
<el-tag>RTSP:</el-tag>
<span>{{ streamInfo.rtsp }}</span>
</el-dropdown-item>
<el-dropdown-item v-if="streamInfo.rtsps" :command="streamInfo.rtsps">
<el-tag>RTSPS:</el-tag>
<span>{{ streamInfo.rtsps }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-input>
</div>
</el-tab-pane>
<!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
<!--遥控界面-->
<el-tab-pane label="云台控制" name="control" v-if="showPtz">
<div style="display: grid; grid-template-columns: 240px auto; height: 180px; overflow: auto">
<div style="display: grid; grid-template-columns: 6.25rem auto;">
<div class="control-wrapper">
<div class="control-btn control-top" @mousedown="ptzCamera('up')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-top"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-left" @mousedown="ptzCamera('left')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-left"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-bottom" @mousedown="ptzCamera('down')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-bottom"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-right" @mousedown="ptzCamera('right')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-right"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-round">
<div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
</div>
<div class="contro-speed" style="position: absolute; left: 4px; top: 7rem; width: 6.25rem;">
<el-slider v-model="controSpeed" :max="100"></el-slider>
</div>
</div>
<div>
<div class="ptz-btn-box">
<div style="" @mousedown="ptzCamera('zoomin')" @mouseup="ptzCamera('stop')" title="变倍+">
<i class="el-icon-zoom-in control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
<div style="" @mousedown="ptzCamera('zoomout')" @mouseup="ptzCamera('stop')" title="变倍-">
<i class="el-icon-zoom-out control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
</div>
<div class="ptz-btn-box">
<div @mousedown="focusCamera('near')" @mouseup="focusCamera('stop')" title="聚焦+">
<i class="iconfont icon-bianjiao-fangda control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
<div @mousedown="focusCamera('far')" @mouseup="focusCamera('stop')" title="聚焦-">
<i class="iconfont icon-bianjiao-suoxiao control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
</div>
<div class="ptz-btn-box">
<div @mousedown="irisCamera('in')" @mouseup="irisCamera('stop')" title="光圈+">
<i class="iconfont icon-guangquan control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
<div @mousedown="pirisCamera('out')" @mouseup="irisCamera('stop')" title="光圈-">
<i class="iconfont icon-guangquan- control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
</div>
<div style="text-align: left" >
<el-select
v-model="ptzMethod"
style="width: 100%"
size="mini"
placeholder="请选择云台功能"
>
<el-option label="预置点" value="preset"></el-option>
<el-option label="巡航组" value="cruise"></el-option>
<el-option label="自动扫描" value="scan"></el-option>
<el-option label="雨刷" value="wiper"></el-option>
<el-option label="辅助开关" value="switch"></el-option>
</el-select>
<ptzPreset :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'preset'" style="margin-top: 1rem"></ptzPreset>
<ptzCruising :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'cruise'" style="margin-top: 1rem"></ptzCruising>
<ptzScan :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'scan'" style="margin-top: 1rem"></ptzScan>
<ptzWiper :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'wiper'" style="margin-top: 1rem"></ptzWiper>
<ptzSwitch :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'switch'" style="margin-top: 1rem"></ptzSwitch>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="编码信息" name="codec" >
<mediaInfo ref="mediaInfo" :app="app" :stream="streamId" :mediaServerId="mediaServerId"></mediaInfo>
</el-tab-pane>
<el-tab-pane label="语音对讲" name="broadcast">
<div style="padding: 0 10px">
<!-- <el-switch v-model="broadcastMode" :disabled="broadcastStatus !== -1" active-color="#409EFF"-->
<!-- active-text="喊话(Broadcast)"-->
<!-- inactive-text="对讲(Talk)"></el-switch>-->
<el-radio-group v-model="broadcastMode" :disabled="broadcastStatus !== -1">
<el-radio :label="true" >喊话(Broadcast)</el-radio>
<el-radio :label="false" >对讲(Talk)</el-radio>
</el-radio-group>
</div>
<div class="trank" style="text-align: center;">
<el-button @click="broadcastStatusClick()" :type="getBroadcastStatus()" :disabled="broadcastStatus === -2"
circle icon="el-icon-microphone" style="font-size: 32px; padding: 24px;margin-top: 24px;"/>
<p>
<span v-if="broadcastStatus === -2">正在释放资源</span>
<span v-if="broadcastStatus === -1">点击开始对讲</span>
<span v-if="broadcastStatus === 0">等待接通中...</span>
<span v-if="broadcastStatus === 1">请说话</span>
</p>
</div>
</el-tab-pane>
</el-tabs>
</div>
</el-dialog>
</div>
</template>
<script>
import rtcPlayer from '../dialog/rtcPlayer.vue'
import LivePlayer from '@liveqing/liveplayer'
import crypto from 'crypto'
import jessibucaPlayer from '../common/jessibuca.vue'
import PtzPreset from "../common/ptzPreset.vue";
import PtzCruising from "../common/ptzCruising.vue";
import ptzScan from "../common/ptzScan.vue";
import ptzWiper from "../common/ptzWiper.vue";
import ptzSwitch from "../common/ptzSwitch.vue";
import mediaInfo from "../common/mediaInfo.vue";
import H265web from "../common/h265web.vue";
export default {
name: 'devicePlayer',
props: {},
components: {
H265web,
PtzPreset,PtzCruising,ptzScan,ptzWiper,ptzSwitch,mediaInfo,
LivePlayer, jessibucaPlayer, rtcPlayer,
},
computed: {
getPlayerShared: function () {
return {
sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl),
sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>',
sharedRtmp: this.videoUrl
};
}
},
created() {
console.log("created")
console.log(this.player)
this.broadcastStatus = -1;
if (Object.keys(this.player).length === 1) {
this.activePlayer = Object.keys(this.player)[0]
}
},
data() {
return {
video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4',
videoUrl: '',
activePlayer: "jessibuca",
//
player: {
jessibuca: ["ws_flv", "wss_flv"],
webRTC: ["rtc", "rtcs"],
h265web: ["ws_flv", "wss_flv"],
},
showVideoDialog: false,
streamId: '',
ptzMethod: 'preset',
ptzPresetId: '',
app: '',
mediaServerId: '',
deviceId: '',
channelId: '',
tabActiveName: 'media',
hasAudio: false,
loadingRecords: false,
recordsLoading: false,
isLoging: false,
controSpeed: 30,
timeVal: 0,
timeMin: 0,
timeMax: 1440,
presetPos: 1,
cruisingSpeed: 100,
cruisingTime: 5,
cruisingGroup: 0,
scanSpeed: 100,
scanGroup: 0,
tracks: [],
showPtz: true,
showRrecord: true,
sliderTime: 0,
seekTime: 0,
recordStartTime: 0,
showTimeText: "00:00:00",
streamInfo: null,
broadcastMode: true,
broadcastRtc: null,
broadcastStatus: -1, // -2 -1 0 1
};
},
methods: {
tabHandleClick: function (tab, event) {
console.log(tab)
this.tracks = [];
if (tab.name === "codec") {
this.$refs.mediaInfo.startTask()
}else {
this.$refs.mediaInfo.stopTask()
}
},
changePlayer: function (tab) {
console.log(this.player[tab.name][0])
this.activePlayer = tab.name;
this.videoUrl = this.getUrlByStreamInfo()
console.log(this.videoUrl)
},
openDialog: function (tab, deviceId, channelId, param) {
if (this.showVideoDialog) {
return;
}
this.tabActiveName = tab;
this.channelId = channelId;
this.deviceId = deviceId;
this.streamId = "";
this.mediaServerId = "";
this.app = "";
this.videoUrl = ""
if (!!this.$refs[this.activePlayer]) {
this.$refs[this.activePlayer].pause();
}
switch (tab) {
case "media":
this.play(param.streamInfo, param.hasAudio)
break;
case "streamPlay":
this.tabActiveName = "media";
this.showRrecord = false;
this.showPtz = false;
this.play(param.streamInfo, param.hasAudio)
break;
case "control":
break;
}
},
play: function (streamInfo, hasAudio) {
this.streamInfo = streamInfo;
this.hasAudio = hasAudio;
this.isLoging = false;
// this.videoUrl = streamInfo.rtc;
this.videoUrl = this.getUrlByStreamInfo();
this.streamId = streamInfo.stream;
this.app = streamInfo.app;
this.mediaServerId = streamInfo.mediaServerId;
this.playFromStreamInfo(false, streamInfo)
},
getUrlByStreamInfo() {
console.log(this.streamInfo)
let streamInfo = this.streamInfo
if (this.streamInfo.transcodeStream) {
streamInfo = this.streamInfo.transcodeStream;
}
if (location.protocol === "https:") {
this.videoUrl = streamInfo[this.player[this.activePlayer][1]]
} else {
this.videoUrl = streamInfo[this.player[this.activePlayer][0]]
}
return this.videoUrl;
},
playFromStreamInfo: function (realHasAudio, streamInfo) {
this.showVideoDialog = true;
this.hasaudio = realHasAudio && this.hasaudio;
if (this.$refs[this.activePlayer]) {
this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
}else {
this.$nextTick(() => {
this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
});
}
},
close: function () {
console.log('关闭视频');
if (!!this.$refs[this.activePlayer]){
this.$refs[this.activePlayer].pause();
}
this.videoUrl = '';
this.coverPlaying = false;
this.showVideoDialog = false;
this.stopBroadcast()
},
copySharedInfo: function (data) {
console.log('复制内容:' + data);
this.coverPlaying = false;
this.tracks = []
let _this = this;
this.$copyText(data).then(
function (e) {
_this.$message({
showClose: true,
message: '复制成功',
type: 'success'
});
},
function (e) {
_this.$message({
showClose: true,
message: '复制失败,请手动复制',
type: 'error'
});
}
);
},
ptzCamera: function (command) {
console.log('云台控制:' + command);
let that = this;
this.$axios({
method: 'get',
url: '/api/front-end/ptz/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + parseInt(this.controSpeed * 255/100) + '&verticalSpeed=' + parseInt(this.controSpeed * 255/100) + '&zoomSpeed=' + parseInt(this.controSpeed * 16/100)
}).then(function (res) {
});
},
irisCamera: function (command) {
this.$axios({
method: 'get',
url: '/api/front-end/fi/iris/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&speed=' + parseInt(this.controSpeed * 255/100)
}).then(function (res) {
});
},
focusCamera: function (command) {
this.$axios({
method: 'get',
url: '/api/front-end/fi/focus/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&speed=' + parseInt(this.controSpeed * 255/100)
}).then(function (res) {
});
},
////////////////////////////////////////////////
videoError: function (e) {
console.log("播放器错误:" + JSON.stringify(e));
},
copyUrl: function (dropdownItem) {
console.log(dropdownItem)
this.$copyText(dropdownItem).then((e) => {
this.$message.success({
showClose: true,
message: "成功拷贝到粘贴板"
})
}, (e) => {
})
},
getBroadcastStatus() {
if (this.broadcastStatus == -2) {
return "primary"
}
if (this.broadcastStatus == -1) {
return "primary"
}
if (this.broadcastStatus == 0) {
return "warning"
}
if (this.broadcastStatus == 1) {
return "danger"
}
},
broadcastStatusClick() {
if (this.broadcastStatus == -1) {
//
this.broadcastStatus = 0
//
this.$axios({
method: 'get',
url: '/api/play/broadcast/' + this.deviceId + '/' + this.channelId + "?timeout=30&broadcastMode=" + this.broadcastMode
}).then((res) => {
if (res.data.code === 0) {
let streamInfo = res.data.data.streamInfo;
if (document.location.protocol.includes("https")) {
this.startBroadcast(streamInfo.rtcs)
} else {
this.startBroadcast(streamInfo.rtc)
}
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
});
} else if (this.broadcastStatus === 1) {
this.broadcastStatus = -1;
this.broadcastRtc.close()
}
},
startBroadcast(url) {
// Key
this.$axios({
method: 'post',
url: '/api/user/userInfo',
}).then((res) => {
if (res.data.code !== 0) {
this.$message({
showClose: true,
message: "获取推流鉴权Key失败",
type: "error",
});
this.broadcastStatus = -1;
} else {
let pushKey = res.data.data.pushKey;
// KEY
url += "&sign=" + crypto.createHash('md5').update(pushKey, "utf8").digest('hex')
console.log("开始语音喊话: " + url)
this.broadcastRtc = new ZLMRTCClient.Endpoint({
debug: true, //
zlmsdpUrl: url, //
simulecast: false,
useCamera: false,
audioEnable: true,
videoEnable: false,
recvOnly: false,
})
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_NOT_SUPPORT, (e) => {//
console.error('不支持webrtc', e)
this.$message({
showClose: true,
message: '不支持webrtc, 无法进行语音喊话',
type: 'error'
});
this.broadcastStatus = -1;
});
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e) => {// ICE
console.error('ICE 协商出错')
this.$message({
showClose: true,
message: 'ICE 协商出错',
type: 'error'
});
this.broadcastStatus = -1;
});
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e) => {// offer anwser
console.error('offer anwser 交换失败', e)
this.$message({
showClose: true,
message: 'offer anwser 交换失败' + e,
type: 'error'
});
this.broadcastStatus = -1;
});
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, (e) => {// offer anwser
console.log('状态改变', e)
if (e === "connecting") {
this.broadcastStatus = 0;
} else if (e === "connected") {
this.broadcastStatus = 1;
} else if (e === "disconnected") {
this.broadcastStatus = -1;
}
});
this.broadcastRtc.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, (e) => {// offer anwser
console.log('捕获流失败', e)
this.$message({
showClose: true,
message: '捕获流失败' + e,
type: 'error'
});
this.broadcastStatus = -1;
});
}
}).catch((e) => {
this.$message({
showClose: true,
message: e,
type: 'error'
});
this.broadcastStatus = -1;
});
},
stopBroadcast() {
this.broadcastRtc.close();
this.broadcastStatus = -1;
this.$axios({
method: 'get',
url: '/api/play/broadcast/stop/' + this.deviceId + '/' + this.channelId
}).then((res) => {
if (res.data.code == 0) {
// this.broadcastStatus = -1;
// this.broadcastRtc.close()
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
});
}
}
};
</script>
<style>
.control-wrapper {
position: relative;
width: 6.25rem;
height: 6.25rem;
max-width: 6.25rem;
max-height: 6.25rem;
border-radius: 100%;
margin-top: 1.5rem;
margin-left: 0.5rem;
float: left;
}
.control-panel {
position: relative;
top: 0;
left: 5rem;
height: 11rem;
max-height: 11rem;
}
.control-btn {
display: flex;
justify-content: center;
position: absolute;
width: 44%;
height: 44%;
border-radius: 5px;
border: 1px solid #78aee4;
box-sizing: border-box;
transition: all 0.3s linear;
}
.control-btn:hover {
cursor: pointer
}
.control-btn i {
font-size: 20px;
color: #78aee4;
display: flex;
justify-content: center;
align-items: center;
}
.control-btn i:hover {
cursor: pointer
}
.control-zoom-btn:hover {
cursor: pointer
}
.control-round {
position: absolute;
top: 21%;
left: 21%;
width: 58%;
height: 58%;
background: #fff;
border-radius: 100%;
}
.control-round-inner {
position: absolute;
left: 13%;
top: 13%;
display: flex;
justify-content: center;
align-items: center;
width: 70%;
height: 70%;
font-size: 40px;
color: #78aee4;
border: 1px solid #78aee4;
border-radius: 100%;
transition: all 0.3s linear;
}
.control-inner-btn {
position: absolute;
width: 60%;
height: 60%;
background: #fafafa;
}
.control-top {
top: -8%;
left: 27%;
transform: rotate(-45deg);
border-radius: 5px 100% 5px 0;
}
.control-top i {
transform: rotate(45deg);
border-radius: 5px 100% 5px 0;
}
.control-top .control-inner {
left: -1px;
bottom: 0;
border-top: 1px solid #78aee4;
border-right: 1px solid #78aee4;
border-radius: 0 100% 0 0;
}
.control-top .fa {
transform: rotate(45deg) translateY(-7px);
}
.control-left {
top: 27%;
left: -8%;
transform: rotate(45deg);
border-radius: 5px 0 5px 100%;
}
.control-left i {
transform: rotate(-45deg);
}
.control-left .control-inner {
right: -1px;
top: -1px;
border-bottom: 1px solid #78aee4;
border-left: 1px solid #78aee4;
border-radius: 0 0 0 100%;
}
.control-left .fa {
transform: rotate(-45deg) translateX(-7px);
}
.control-right {
top: 27%;
right: -8%;
transform: rotate(45deg);
border-radius: 5px 100% 5px 0;
}
.control-right i {
transform: rotate(-45deg);
}
.control-right .control-inner {
left: -1px;
bottom: -1px;
border-top: 1px solid #78aee4;
border-right: 1px solid #78aee4;
border-radius: 0 100% 0 0;
}
.control-right .fa {
transform: rotate(-45deg) translateX(7px);
}
.control-bottom {
left: 27%;
bottom: -8%;
transform: rotate(45deg);
border-radius: 0 5px 100% 5px;
}
.control-bottom i {
transform: rotate(-45deg);
}
.control-bottom .control-inner {
top: -1px;
left: -1px;
border-bottom: 1px solid #78aee4;
border-right: 1px solid #78aee4;
border-radius: 0 0 100% 0;
}
.control-bottom .fa {
transform: rotate(-45deg) translateY(7px);
}
.trank {
width: 80%;
height: 180px;
text-align: left;
padding: 0 10%;
overflow: auto;
}
.trankInfo {
width: 80%;
padding: 0 10%;
}
.el-dialog__body{
padding: 10px 20px;
}
.ptz-btn-box {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
height: 3rem;
line-height: 4rem;
}
</style>

View File

@ -1,228 +0,0 @@
<template>
<div id="editRecordPlan" v-loading="loading" style="text-align: left;">
<el-dialog
title="录制计划"
width="700px"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-right: 20px;">
<el-form >
<el-form-item label="名称">
<el-input type="text" v-model="planName"></el-input>
</el-form-item>
<el-form-item>
<ByteWeektimePicker v-model="byteTime" name="name"/>
</el-form-item>
<el-form-item>
<div style="float: right; margin-top: 20px">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
import { ByteWeektimePicker } from 'byte-weektime-picker'
export default {
name: "editRecordPlan",
props: {},
components: {ByteWeektimePicker},
created() {
},
data() {
return {
options: [],
loading: false,
edit: false,
planName: null,
id: null,
showDialog: false,
endCallback: "",
byteTime: "",
};
},
methods: {
openDialog: function (recordPlan, endCallback) {
this.endCallback = endCallback;
this.showDialog = true;
this.byteTime= "";
if (recordPlan) {
this.edit = true
this.planName = recordPlan.name
this.id = recordPlan.id
this.$axios({
method: 'get',
url: "/api/record/plan/get",
params: {
planId: recordPlan.id,
}
}).then((res) => {
if (res.data.code === 0 && res.data.data.planItemList) {
this.byteTime = this.plan2Byte(res.data.data.planItemList)
}
}).catch((error) => {
console.error(error)
});
}
},
onSubmit: function () {
let planList = this.byteTime2PlanList();
if (!this.edit) {
this.$axios({
method: 'post',
url: "/api/record/plan/add",
data: {
name: this.planName,
planItemList: planList
}
}).then((res) => {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '添加成功',
type: 'success',
});
this.showDialog = false;
this.endCallback()
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error) => {
console.error(error)
});
}else {
this.$axios({
method: 'post',
url: "/api/record/plan/update",
data: {
id: this.id,
name: this.planName,
planItemList: planList
}
}).then((res) => {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '更新成功',
type: 'success',
});
this.showDialog = false;
this.endCallback()
} else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error) => {
console.error(error)
});
}
},
close: function () {
this.showDialog = false;
this.id = null
this.planName = null
this.byteTime = ""
this.endCallback = ""
if(this.endCallback) {
this.endCallback();
}
},
byteTime2PlanList() {
if (this.byteTime.length === 0) {
return;
}
const DayTimes = 24 * 2;
let planList = []
let week = 1;
// 336 list 7 48
for (let i = 0; i < this.byteTime.length; i += DayTimes) {
let planArray = this.byteTime2Plan(this.byteTime.slice(i, i + DayTimes));
if(!planArray || planArray.length === 0) {
week ++;
continue
}
for (let j = 0; j < planArray.length; j++) {
planList.push({
planId: this.id,
start: planArray[j].start,
stop: planArray[j].stop,
weekDay: week
})
}
week ++;
}
return planList
},
byteTime2Plan(weekItem){
let start = null;
let stop = null;
let result = []
for (let i = 0; i < weekItem.length; i++) {
let item = weekItem[i]
if (item === '1') { //
stop = i
if (start === null ) {
start = i
}
if (i === weekItem.length - 1 && start != null && stop != null) {
result.push({
start: start,
stop: stop,
})
}
} else {
if (stop !== null){
result.push({
start: start,
stop: stop,
})
start = null
stop = null
}
}
}
return result;
},
plan2Byte(planList) {
let byte = ""
let indexArray = {}
for (let i = 0; i < planList.length; i++) {
let weekDay = planList[i].weekDay
let index = planList[i].start
let endIndex = planList[i].stop
for (let j = index; j <= endIndex; j++) {
indexArray["key_" + (j + (weekDay - 1 )*48)] = 1
}
}
for (let i = 0; i < 336; i++) {
if (indexArray["key_" + i]){
byte += "1"
}else {
byte += "0"
}
}
return byte
}
},
};
</script>

View File

@ -1,129 +0,0 @@
<template>
<div id="groupEdit" v-loading="loading">
<el-dialog
title="分组编辑"
width="40%"
top="2rem"
:append-to-body="true"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-top: 1rem;margin-right: 100px;">
<el-form ref="form" :model="group" label-width="140px" >
<el-form-item label="节点编号" prop="id" >
<el-input v-model="group.deviceId" placeholder="请输入编码">
<el-button slot="append" @click="buildDeviceIdCode(group.deviceId)">生成</el-button>
</el-input>
</el-form-item>
<el-form-item label="节点名称" prop="name">
<el-input v-model="group.name" clearable></el-input>
</el-form-item>
<el-form-item label="行政区划" prop="name">
<el-input v-model="group.civilCode" >
<el-button slot="append" @click="buildCivilCode(group.civilCode)">选择</el-button>
</el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit" >确认</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
<channelCode ref="channelCode"></channelCode>
<chooseCivilCode ref="chooseCivilCode"></chooseCivilCode>
</div>
</template>
<script>
import channelCode from "./channelCode.vue";
import ChooseCivilCode from "./chooseCivilCode.vue";
export default {
name: "groupEdit",
components: {ChooseCivilCode, channelCode},
computed: {},
props: [],
created() {},
data() {
return {
submitCallback: null,
showDialog: false,
loading: false,
level: 0,
group: {
id: 0,
deviceId: "",
name: "",
parentDeviceId: "",
businessGroup: "",
civilCode: "",
platformId: "",
},
};
},
methods: {
openDialog: function (group, callback) {
console.log(group)
if (group) {
this.group = group;
}
this.showDialog = true;
this.submitCallback = callback;
},
onSubmit: function () {
this.$axios({
method:"post",
url: this.group.id ? '/api/group/update':'/api/group/add',
data: this.group
}).then((res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
})
if (this.submitCallback)this.submitCallback(this.group)
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
this.close();
})
.catch((error)=> {
this.$message({
showClose: true,
message: error,
type: "error",
});
});
},
buildDeviceIdCode: function (deviceId){
console.log(this.group)
let lockContent = this.group.businessGroup ? "216":"215"
this.$refs.channelCode.openDialog(code=>{
this.group.deviceId = code;
}, deviceId, 5 , lockContent);
},
buildCivilCode: function (deviceId){
this.$refs.chooseCivilCode.openDialog(code=>{
this.group.civilCode = code;
});
},
close: function () {
this.showDialog = false;
console.log(this.group)
},
},
};
</script>

View File

@ -1,131 +0,0 @@
<template>
<div id="importChannel" v-loading="isLoging">
<el-dialog
title="导入通道数据"
width="30rem"
top="2rem"
:append-to-body="true"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div>
<el-upload
class="upload-box"
drag
:action="uploadUrl"
name="file"
:headers="headers"
:on-success="successHook"
:on-error="errorHook"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传 csv / xls / xlsx 文件</div>
</el-upload>
</div>
</el-dialog>
<ShowErrorData ref="showErrorData" :gbIds="errorGBIds" :streams="errorStreams" ></ShowErrorData>
</div>
</template>
<script>
import ShowErrorData from './importChannelShowErrorData.vue'
import userService from "../service/UserService";
export default {
name: "importChannel",
components: {
ShowErrorData,
},
created() {},
data() {
return {
submitCallback: null,
showDialog: false,
isLoging: false,
isEdit: false,
errorStreams: [],
errorGBIds: [],
headers: {
"access-token": userService.getToken()
},
uploadUrl: process.env.NODE_ENV === 'development'? `http://127.0.0.1:8080/debug/api/push/upload`: (window.baseUrl ? window.baseUrl : "") + `/api/push/upload`,
};
},
methods: {
openDialog: function (callback) {
this.showDialog = true;
this.submitCallback = callback;
},
onSubmit: function () {
console.log("onSubmit");
console.log(this.form);
this.$axios({
method:"post",
url:`/api/platform/catalog/${!this.isEdit? "add":"edit"}`,
data: this.form
})
.then((res)=> {
if (res.data.code === 0) {
console.log("添加/修改成功")
if (this.submitCallback)this.submitCallback()
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
this.close();
})
.catch((error)=> {
console.log(error);
});
},
close: function () {
this.showDialog = false;
},
successHook: function(response, file, fileList){
if (response.code === 0) {
this.$message({
showClose: true,
message: response.msg,
type: "success",
});
}else if (response.code === 1) {
this.errorGBIds = response.data.gbId
this.errorStreams = response.data.stream
console.log(this.$refs)
console.log(this.$refs.showErrorData)
this.$refs.showErrorData.openDialog()
}else {
this.$message({
showClose: true,
message: response.msg,
type: "error",
});
}
},
errorHook: function (err, file, fileList) {
this.$message({
showClose: true,
message: err,
type: "error",
});
}
},
};
</script>
<style>
.upload-box{
text-align: center;
}
.errDataBox{
max-height: 15rem;
overflow: auto;
}
</style>

View File

@ -1,64 +0,0 @@
<template>
<div id="importChannelShowErrorData" v-loading="isLoging">
<el-dialog
title="导入通道数据成功,但数据存在重复"
width="30rem"
top="2rem"
:append-to-body="true"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div >
重复国标ID:
<el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="gbIds.join(',')" @success="$message({type:'success', message:'成功拷贝到粘贴板'})">复制</el-button>
<ul class="errDataBox">
<li v-for="id in gbIds" >
{{ id }}
</li>
</ul>
</div>
<div >
重复App/stream:
<el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="streams.join(',')" @success="$message({type:'success', message:'成功拷贝到粘贴板'})">复制</el-button>
<ul class="errDataBox">
<li v-for="id in streams" >
{{ id }}
</li>
</ul>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "importChannelShowErrorData",
computed: {},
created() {},
props: ['gbIds', 'streams'],
data() {
return {
isLoging: false,
showDialog: false,
};
},
methods: {
openDialog: function () {
this.showDialog = true;
},
close: function () {
this.showDialog = false;
},
},
};
</script>
<style>
.errDataBox{
max-height: 15rem;
overflow: auto;
}
</style>

View File

@ -1,351 +0,0 @@
<template>
<div id="linkChannelRecord" style="width: 100%; background-color: #FFFFFF; display: grid; grid-template-columns: 200px auto;">
<el-dialog title="通道关联" v-loading="dialogLoading" v-if="showDialog" top="2rem" width="80%" :close-on-click-modal="false" :visible.sync="showDialog" :destroy-on-close="true" @close="close()">
<div style="display: grid; grid-template-columns: 100px auto;">
<el-tabs tab-position="left" style="" v-model="hasLink" @tab-click="search">
<el-tab-pane label="未关联" name="false"></el-tab-pane>
<el-tab-pane label="已关联" name="true"></el-tab-pane>
</el-tabs>
<div>
<div class="page-header">
<div class="page-header-btn" >
<div style="display: inline;">
搜索:
<el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
在线状态:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="search" v-model="online" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
类型:
<el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="search" v-model="channelType" placeholder="请选择"
default-first-option>
<el-option label="全部" value=""></el-option>
<el-option v-for="item in Object.values($channelTypeList)" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-button v-if="hasLink !=='true'" size="mini" type="primary" @click="add()">
添加
</el-button>
<el-button v-if="hasLink ==='true'" size="mini" type="danger" @click="remove()">
移除
</el-button>
<el-button size="mini" v-if="hasLink !=='true'" @click="addByDevice()">按设备添加</el-button>
<el-button size="mini" v-if="hasLink ==='true'" @click="removeByDevice()">按设备移除</el-button>
<el-button size="mini" v-if="hasLink !=='true'" @click="addAll()">添加所有通道</el-button>
<el-button size="mini" v-if="hasLink ==='true'" @click="removeAll()">移除所有通道</el-button>
<el-button size="mini" @click="getChannelList()">刷新</el-button>
</div>
</div>
</div>
<el-table size="small" ref="channelListTable" :data="channelList" :height="winHeight"
header-row-class-name="table-header" @selection-change="handleSelectionChange" >
<el-table-column type="selection" width="55" >
</el-table-column>
<el-table-column prop="gbName" label="名称" min-width="180">
</el-table-column>
<el-table-column prop="gbDeviceId" label="编号" min-width="180">
</el-table-column>
<el-table-column prop="gbManufacturer" label="厂家" min-width="100">
</el-table-column>
<el-table-column label="类型" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" effect="plain" type="success" :style="$channelTypeList[scope.row.dataType].style">{{$channelTypeList[scope.row.dataType].name}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100">
<template v-slot:default="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium" v-if="scope.row.gbStatus === 'ON'">在线</el-tag>
<el-tag size="medium" type="info" v-if="scope.row.gbStatus !== 'ON'">离线</el-tag>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
<gbDeviceSelect ref="gbDeviceSelect"></gbDeviceSelect>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import gbDeviceSelect from "./GbDeviceSelect.vue";
export default {
name: 'linkChannelRecord',
components: {gbDeviceSelect},
data() {
return {
dialogLoading: false,
showDialog: false,
chooseData: {},
channelList: [],
searchSrt: "",
channelType: "",
online: "",
hasLink: "false",
winHeight: window.innerHeight - 250,
currentPage: 1,
count: 15,
total: 0,
loading: false,
planId: null,
loadSnap: {},
multipleSelection: []
};
},
created() {},
destroyed() {},
methods: {
openDialog(planId, closeCallback) {
this.planId = planId
this.showDialog = true
this.closeCallback = closeCallback
this.initData()
},
initData: function () {
this.currentPage= 1;
this.count= 15;
this.total= 0;
this.getChannelList();
},
currentChange: function (val) {
this.currentPage = val;
this.initData();
},
handleSizeChange: function (val) {
this.count = val;
this.getChannelList();
},
getChannelList: function () {
this.$axios({
method: 'get',
url: `/api/record/plan/channel/list`,
params: {
page: this.currentPage,
count: this.count,
query: this.searchSrt,
online: this.online,
channelType: this.channelType,
planId: this.planId,
hasLink: this.hasLink
}
}).then((res)=> {
if (res.data.code === 0) {
this.total = res.data.data.total;
this.channelList = res.data.data.list;
//
this.$nextTick(() => {
this.$refs.channelListTable.doLayout();
})
}
}).catch((error)=> {
console.log(error);
});
},
handleSelectionChange: function (val){
this.multipleSelection = val;
},
linkPlan: function (data){
this.loading = true
return this.$axios({
method: 'post',
url: `/api/record/plan/link`,
data: data
}).then((res)=> {
if (res.data.code === 0) {
this.$message.success({
showClose: true,
message: "保存成功"
})
this.getChannelList()
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
this.loading = false
}).catch((error)=> {
this.$message.error({
showClose: true,
message: error
})
this.loading = false
})
},
add: function (row) {
let channels = []
for (let i = 0; i < this.multipleSelection.length; i++) {
channels.push(this.multipleSelection[i].gbId)
}
if (channels.length === 0) {
this.$message.info({
showClose: true,
message: "请选择通道"
})
return;
}
this.linkPlan({
planId: this.planId,
channelIds: channels
})
},
addAll: function (row) {
this.$confirm("添加所有通道将包括已经添加到其他计划的通道,确定添加所有通道?", '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.linkPlan({
planId: this.planId,
allLink: true
})
}).catch(() => {
});
},
addByDevice: function (row) {
this.$refs.gbDeviceSelect.openDialog((rows)=>{
let deviceIds = []
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
this.linkPlan({
planId: this.planId,
deviceDbIds: deviceIds
})
})
},
removeByDevice: function (row) {
this.$refs.gbDeviceSelect.openDialog((rows)=>{
let deviceIds = []
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
this.linkPlan({
deviceDbIds: deviceIds
})
})
},
remove: function (row) {
let channels = []
for (let i = 0; i < this.multipleSelection.length; i++) {
channels.push(this.multipleSelection[i].gbId)
}
if (channels.length === 0) {
this.$message.info({
showClose: true,
message: "请选择通道"
})
return;
}
this.linkPlan({
channelIds: channels
})
},
removeAll: function (row) {
this.$confirm("确定移除所有通道?", '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.linkPlan({
planId: this.planId,
allLink: false
})
}).catch(() => {
});
},
search: function () {
this.currentPage = 1;
this.total = 0;
this.initData();
},
refresh: function () {
this.initData();
},
}
};
</script>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
</style>

View File

@ -1,121 +0,0 @@
<template>
<div id="onvif搜索" v-loading="isLoging">
<el-dialog
title="onvif搜索"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-top: 1rem;margin-right: 100px;">
<el-form ref="form" :rules="rules" :model="form" label-width="140px" >
<el-form-item label="地址" prop="hostName" >
<el-select v-model="form.hostName" style="float: left; width: 100%" >
<el-option
v-for="item in hostNames"
:key="item"
:label="item.replace('http://', '')"
:value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" clearable></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" clearable></el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit" >确认</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "onvifEdit",
props: {},
computed: {},
created() {},
data() {
return {
listChangeCallback: null,
showDialog: false,
isLoging: false,
hostNames:[],
form: {
hostName: null,
username: "admin",
password: "admin123",
},
rules: {
hostName: [{ required: true, message: "请选择", trigger: "blur" }],
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
},
};
},
methods: {
openDialog: function (hostNamesParam, callback) {
console.log(hostNamesParam)
this.showDialog = true;
this.listChangeCallback = callback;
if (hostNamesParam != null) {
this.hostNames = hostNamesParam;
}
},
onSubmit: function () {
console.log("onSubmit");
console.log(this.form);
this.$axios({
method: 'get',
url:`/api/onvif/rtsp`,
params: {
hostname: this.form.hostName,
timeout: 3000,
username: this.form.username,
password: this.form.password,
}
}).then((res) => {
console.log(res.data)
if (res.data.code === 0) {
if (res.data.data != null) {
this.listChangeCallback(res.data.data)
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
}
}).catch(function (error) {
console.log(error);
});
},
close: function () {
this.showDialog = false;
this.$refs.form.resetFields();
},
},
};
</script>

View File

@ -1,181 +0,0 @@
<template>
<div id="log" style="width: 100%;height: 100%;">
<div style="width: 100%; height: 40px; display: grid; grid-template-columns: 1fr 1fr">
<div style="text-align: left; line-height: 40px;">
<span style="width: 5vw">过滤: </span>
<el-input size="mini" v-model="filter" placeholder="请输入过滤关键字" style="width: 20vw"></el-input>
</div>
<div style="text-align: right; line-height: 40px;">
<el-button size="mini" icon="el-icon-download" @click="downloadFile()">下载
</el-button>
</div>
</div>
<log-viewer :log="logData" :auto-scroll="true" :height="winHeight"/>
</div>
</template>
<script>
import userService from "./../service/UserService";
import moment from "moment/moment";
import stripAnsi from "strip-ansi";
export default {
name: 'log',
props: [ 'fileUrl', 'remoteUrl', 'loadEnd'],
components: {},
data() {
return {
loading: true,
winHeight: window.innerHeight - 300,
data: [],
filter: "",
logData: "",
websocket: null,
destroyedCallback: null
};
},
watch: {
remoteUrl(newValue) {
console.log(newValue);
this.remoteUrl = newValue;
this.initData();
},
fileUrl(newValue) {
this.fileUrl = newValue;
this.initData();
},
filter(newValue) {
this.filter = newValue;
this.logData = this.getLogData();
},
data(newValue) {
this.data = newValue;
this.logData = this.getLogData();
}
},
created() {
this.data = []
if (this.fileUrl || this.remoteUrl) {
this.initData();
}
},
destroyed() {
console.log('destroyed');
if (this.destroyedCallback) {
this.destroyedCallback()
}
},
methods: {
initData: function () {
this.loading = true
this.data = []
console.log(this.loading)
if (this.fileUrl) {
this.$axios({
method: 'get',
url: this.fileUrl,
}).then((res) => {
let dataArray = res.data.split("\n");
dataArray.forEach(item => {
this.data.push(item);
})
this.loading = false
if (this.loadEnd && typeof this.loadEnd === "function") {
this.loadEnd()
}
}).catch((error) => {
console.log(error);
});
}else if (this.remoteUrl) {
console.log('remoteUrl' + this.remoteUrl);
console.log(window.location.host)
window.websocket = new WebSocket(this.remoteUrl, userService.getToken());
window.websocket.onclose = e => {
console.log(`conn closed: code=${e.code}, reason=${e.reason}, wasClean=${e.wasClean}`)
}
window.websocket.onmessage = e => {
this.loading = false
this.data.push(e.data);
}
window.websocket.onerror = e => {
console.log(`conn err`)
console.error(e)
}
window.websocket.onopen = e => {
console.log(`conn open: ${e}`);
this.destroyedCallback = ()=>{
window.websocket.close()
}
}
}
},
getLogData: function () {
this.loading = true;
if (this.data.length === 0) {
this.loading = false;
return "";
}else {
let result = '';
for (let i = 0; i < this.data.length; i++) {
if (this.filter.length === 0) {
result += this.data[i] + "\r\n"
}else {
if (this.data[i].indexOf(this.filter) > -1) {
result += this.data[i] + "\r\n"
}
}
}
this.loading = false;
return result;
}
},
getLogDataWithOutAnsi: function () {
if (this.data.length === 0) {
return "";
}else {
let result = '';
for (let i = 0; i < this.data.length; i++) {
if (this.filter.length === 0) {
result += stripAnsi(this.data[i]) + "\r\n"
}else {
if (this.data[i].indexOf(this.filter) > -1) {
result += stripAnsi(this.data[i]) + "\r\n"
}
}
}
return result;
}
},
downloadFile() {
let blob = new Blob([this.getLogDataWithOutAnsi()], {
type: "text/plain;charset=utf-8"
});
let reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = (e)=> {
let a = document.createElement('a');
a.download = `wvp-${this.filter}-${moment().format('yyyy-MM-DD')}.log`;
a.href = e.target.result;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
},
}
};
</script>
<style>
.log-loading{
position: absolute;
left: 50%;
top: 50%;
display: inline-block;
text-align: center;
background-color: transparent;
font-size: 20px;
color: rgb(255, 255, 255);
z-index: 1000;
}
</style>

View File

@ -1,180 +0,0 @@
<template>
<div id="addStreamProxy" v-loading="isLoging">
<el-dialog
title=" 加入"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div id="shared" style="margin-top: 1rem;margin-right: 100px;">
<el-form ref="streamProxy" :rules="rules" :model="proxyParam" label-width="140px">
<el-form-item label="名称" prop="name">
<el-input v-model="proxyParam.name" clearable></el-input>
</el-form-item>
<el-form-item label="流应用名" prop="app">
<el-input v-model="proxyParam.app" clearable :disabled="edit"></el-input>
</el-form-item>
<el-form-item label="流ID" prop="stream">
<el-input v-model="proxyParam.stream" clearable :disabled="edit"></el-input>
</el-form-item>
<el-form-item label="国标编码" prop="gbId">
<el-input v-model="proxyParam.gbId" placeholder="设置国标编码可推送到国标" clearable></el-input>
</el-form-item>
<el-form-item label="经度" prop="longitude" v-if="proxyParam.gbId">
<el-input v-model="proxyParam.longitude" placeholder="经度" clearable></el-input>
</el-form-item>
<el-form-item label="纬度" prop="latitude" v-if="proxyParam.gbId">
<el-input v-model="proxyParam.latitude" placeholder="经度" clearable></el-input>
</el-form-item>
<el-form-item>
<div style="float: right;">
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="close">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "pushStreamEdit",
props: {},
computed: {},
created() {},
data() {
// var deviceGBIdRules = async (rule, value, callback) => {
// console.log(value);
// if (value === "") {
// callback(new Error(""));
// } else {
// var exit = await this.deviceGBIdExit(value);
// console.log(exit);
// console.log(exit == "true");
// console.log(exit === "true");
// if (exit) {
// callback(new Error(""));
// } else {
// callback();
// }
// }
// };
return {
listChangeCallback: null,
showDialog: false,
isLoging: false,
edit: false,
proxyParam: {
name: null,
app: null,
stream: null,
gbId: null,
longitude: null,
latitude: null,
},
rules: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
app: [{ required: true, message: "请输入应用名", trigger: "blur" }],
stream: [{ required: true, message: "请输入流ID", trigger: "blur" }],
gbId: [{ required: true, message: "请输入国标编码", trigger: "blur" }],
},
};
},
methods: {
openDialog: function (proxyParam, callback) {
this.showDialog = true;
this.listChangeCallback = callback;
if (proxyParam != null) {
this.proxyParam = proxyParam;
this.edit = true
}else{
this.proxyParam= {
name: null,
app: null,
stream: null,
gbId: null,
longitude: null,
latitude: null,
}
this.edit = false
}
},
onSubmit: function () {
console.log("onSubmit");
if (this.edit) {
this.$axios({
method:"post",
url:`/api/push/save_to_gb`,
data: this.proxyParam
}).then( (res) => {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: "success",
});
this.showDialog = false;
if (this.listChangeCallback != null) {
this.listChangeCallback();
}
}
}).catch((error)=> {
console.log(error);
});
}else {
this.$axios({
method:"post",
url:`/api/push/add`,
data: this.proxyParam
}).then( (res) => {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: "success",
});
this.showDialog = false;
if (this.listChangeCallback != null) {
this.listChangeCallback();
}
}
}).catch((error)=> {
console.log(error);
});
}
},
close: function () {
console.log("关闭加入GB");
this.showDialog = false;
this.$refs.streamProxy.resetFields();
},
deviceGBIdExit: async function (deviceGbId) {
var result = false;
var that = this;
await that.$axios({
method:"get",
url:`/api/platform/exit/${deviceGbId}`
}).then(function (res) {
result = res.data;
}).catch(function (error) {
console.log(error);
});
return result;
},
checkExpires: function() {
if (this.platform.enable && this.platform.expires == "0") {
this.platform.expires = "300";
}
},
handleNodeClick: function (node){
}
},
};
</script>

View File

@ -1,107 +0,0 @@
<template>
<div id="queryTrace" >
<el-dialog
title="查询轨迹"
width="40%"
top="2rem"
:close-on-click-modal="false"
:visible.sync="showDialog"
:destroy-on-close="true"
@close="close()"
>
<div v-loading="isLoging">
<el-date-picker v-model="searchFrom" type="datetime" placeholder="选择开始日期时间" default-time="00:00:00" value-format="yyyy-MM-dd HH:mm:ss" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker>
<el-date-picker v-model="searchTo" type="datetime" placeholder="选择结束日期时间" default-time="00:00:00" value-format="yyyy-MM-dd HH:mm:ss" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker>
<el-button icon="el-icon-search" size="mini" type="primary" @click="onSubmit">查询</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import DeviceService from '../service/DeviceService'
export default {
name: "deviceEdit",
props: [],
computed: {},
created() {},
data() {
return {
deviceService: new DeviceService(),
pickerOptions: {
shortcuts: [{
text: '今天',
onClick(picker) {
picker.$emit('pick', new Date());
}
}, {
text: '昨天',
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24);
picker.$emit('pick', date);
}
}, {
text: '一周前',
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', date);
}
}]
},
searchFrom: null,
searchTo: null,
listChangeCallback: null,
showDialog: false,
isLoging: false,
channel: null,
callback: null,
};
},
methods: {
openDialog: function (channel, callback) {
console.log(channel)
this.showDialog = true;
this.callback = callback;
this.channel = channel;
},
onSubmit: function () {
console.log("onSubmit");
this.isLoging = true;
let url = `/api/position/history/${this.channel.deviceId}?start=${this.searchFrom}&end=${this.searchTo}`;
if (this.channel.channelId) {
url+="&channelId=${this.channel.channelId}"
}
this.$axios.get(url, {
}).then((res)=> {
this.isLoging = false;
if (typeof this.callback == "function") {
if (res.data.code == 0) {
this.callback(res.data.data)
this.close()
}else {
this.$message.error({
showClose: true,
message: res.data.msg,
});
}
}
}).catch(function (error) {
this.isLoging = false;
console.error(error);
})
},
close: function () {
this.showDialog = false;
this.isLoging = false;
this.callback = null;
this.channel = null;
},
},
};
</script>

View File

@ -1,156 +0,0 @@
<template>
<div id="recordDownload" >
<el-dialog :title="title" v-if="showDialog" width="45rem" :append-to-body="true" :close-on-click-modal="false" :visible.sync="showDialog" :destroy-on-close="true" @close="close()" center>
<el-row>
<el-col :span="18" style="padding-top: 7px;">
<el-progress :percentage="percentage"></el-progress>
</el-col>
<el-col :span="6" >
<el-button icon="el-icon-download" v-if="downloadFile" size="mini" title="点击下载" @click="downloadFileClientEvent()">下载</el-button>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script>
import moment from "moment";
export default {
name: 'recordDownload',
created() {
window.addEventListener('beforeunload', this.stopDownloadRecord)
},
data() {
return {
title: "下载中...",
deviceId: "",
channelId: "",
app: "",
stream: "",
mediaServerId: "",
showDialog: false,
scale: 1,
percentage: 0.00,
streamInfo: null,
taskId: null,
getProgressRun: false,
timer: null,
downloadFile: null,
};
},
methods: {
openDialog: function (deviceId, channelId, app, stream, mediaServerId) {
this.deviceId = deviceId;
this.channelId = channelId;
this.app = app;
this.stream = stream;
this.mediaServerId = mediaServerId;
this.showDialog = true;
this.getProgressRun = true;
this.percentage = 0.0;
this.downloadFile = null;
this.getProgressTimer()
},
getProgressTimer: function (){
if (!this.getProgressRun) {
return;
}
if (this.downloadFile) {
return;
}
setTimeout( ()=>{
if (!this.showDialog) return;
this.getProgress(this.getProgressTimer)
}, 5000)
},
getProgress: function (callback){
this.$axios({
method: 'get',
url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
}).then((res)=> {
if (res.data.code === 0) {
this.streamInfo = res.data.data;
if (parseFloat(res.data.progress) === 1) {
this.percentage = 100;
}else {
this.percentage = (parseFloat(res.data.data.progress)*100).toFixed(1);
}
if (this.streamInfo.downLoadFilePath) {
if (location.protocol === "https:") {
this.downloadFile = this.streamInfo.downLoadFilePath.httpsPath;
}else {
this.downloadFile = this.streamInfo.downLoadFilePath.httpPath;
}
this.percentage = 100
this.getProgressRun = false;
this.downloadFileClientEvent()
}
if (callback)callback();
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: "error",
});
this.close();
}
}).catch((e) =>{
console.log(e)
});
},
close: function (){
if (this.streamInfo.progress < 1) {
this.stopDownloadRecord();
}
if (this.timer !== null) {
window.clearTimeout(this.timer);
this.timer = null;
}
this.showDialog=false;
this.getProgressRun = false;
},
gbScale: function (scale){
this.scale = scale;
},
stopDownloadRecord: function (callback) {
if (this.deviceId && this.channelId && this.stream) {
this.$axios({
method: 'get',
url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.stream
}).then((res)=> {
if (callback) callback(res)
});
}
},
downloadFileClientEvent: function (){
// window.open(this.downloadFile )
let x = new XMLHttpRequest();
x.open("GET", this.downloadFile, true);
x.responseType = 'blob';
x.onload=(e)=> {
let url = window.URL.createObjectURL(x.response)
let a = document.createElement('a');
a.href = url
a.download = this.deviceId + "-" + this.channelId + ".mp4";
a.click()
}
x.send();
}
},
destroyed() {
window.removeEventListener('beforeunload', this.stopDownloadRecord)
}
};
</script>
<style>
</style>

View File

@ -1,275 +0,0 @@
<template>
<el-dialog
title="生成行政区划编码"
width="65rem"
top="2rem"
center
:close-on-click-modal="false"
:visible.sync="showVideoDialog"
:destroy-on-close="false"
>
<el-tabs v-model="activeKey" style="padding: 0 1rem; margin: auto 0" @tab-click="getRegionList">
<el-tab-pane name="0" >
<div slot="label" >
<div class="show-code-item">{{ allVal[0].val }}</div>
<div style="text-align: center">{{ allVal[0].meaning }}</div>
</div>
<el-radio v-for="item in regionList" v-model="allVal[0].val" :key="item.deviceId" :name="item.name" :label="item.deviceId" @input="deviceChange(item)" style="line-height: 2rem">
{{ item.name }} - {{ item.deviceId }}
</el-radio>
</el-tab-pane>
<el-tab-pane name="1">
<div slot="label">
<div class="show-code-item">{{ allVal[1].val?allVal[1].val:"--" }}</div>
<div style="text-align: center">{{ allVal[1].meaning }}</div>
</div>
<el-radio :key="-1" v-model="allVal[1].val" @input="deviceChange" label="" style="line-height: 2rem">
不添加
</el-radio>
<el-radio v-for="item in regionList" v-model="allVal[1].val" @input="deviceChange(item)" :key="item.deviceId" :label="item.deviceId.substring(2)" style="line-height: 2rem">
{{ item.name }} - {{ item.deviceId.substring(2) }}
</el-radio>
</el-tab-pane>
<el-tab-pane name="2">
<div slot="label">
<div class="show-code-item">{{ allVal[2].val?allVal[2].val:"--" }}</div>
<div style="text-align: center">{{ allVal[2].meaning }}</div>
</div>
<el-radio :key="-1" label="" v-model="allVal[2].val" style="line-height: 2rem" @input="deviceChange">
不添加
</el-radio>
<el-radio v-for="item in regionList" v-model="allVal[2].val" @input="deviceChange(item)" :key="item.deviceId" :label="item.deviceId.substring(4)" style="line-height: 2rem">
{{ item.name }} - {{ item.deviceId.substring(4) }}
</el-radio>
</el-tab-pane>
<el-tab-pane name="3">
请手动输入基层接入单位编码,两位数字
<div slot="label">
<div class="show-code-item">{{ allVal[3].val?allVal[3].val:"--" }}</div>
<div style="text-align: center">{{ allVal[3].meaning }}</div>
</div>
<el-input
type="text"
placeholder="请输入内容"
v-model="allVal[3].val"
maxlength="2"
:disabled="allVal[3].lock"
show-word-limit
@input="deviceChange"
>
</el-input>
</el-tab-pane>
</el-tabs>
<el-form style="">
<el-form-item style="margin-top: 22px; margin-bottom: 0;">
<div style="float:right;">
<el-button type="primary" @click="handleOk">保存</el-button>
<el-button @click="closeModel">取消</el-button>
</div>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script>
export default {
props: {},
computed: {},
data() {
return {
showVideoDialog: false,
activeKey: '0',
allVal: [
{
id: [1, 2],
meaning: '省级编码',
val: '11',
type: '中心编码',
lock: false,
},
{
id: [3, 4],
meaning: '市级编码',
val: '',
type: '中心编码',
lock: false,
},
{
id: [5, 6],
meaning: '区级编码',
val: '',
type: '中心编码',
lock: false,
},
{
id: [7, 8],
meaning: '基层接入单位编码',
val: '',
type: '中心编码',
lock: false,
}
],
regionList: [],
deviceTypeList: [],
industryCodeTypeList: [],
networkIdentificationTypeList: [],
endCallBck: null,
};
},
methods: {
openDialog: function (endCallBck, code, lockContent) {
this.showVideoDialog = true
this.activeKey= '0';
this.regionList = []
this.allVal = [
{
id: [1, 2],
meaning: '省级编码',
val: '11',
type: '中心编码',
lock: false,
},
{
id: [3, 4],
meaning: '市级编码',
val: '',
type: '中心编码',
lock: false,
},
{
id: [5, 6],
meaning: '区级编码',
val: '',
type: '中心编码',
lock: false,
},
{
id: [7, 8],
meaning: '基层接入单位编码',
val: '',
type: '中心编码',
lock: false,
}
]
if (code) {
if (code.length >= 2) {
this.allVal[0].val = code.substring(0, 2)
this.activeKey = "0"
}
if (code.length >= 4) {
this.allVal[1].val = code.substring(2, 4)
this.activeKey = "1"
}
if (code.length >= 6) {
this.allVal[2].val = code.substring(4, 6)
this.activeKey = "2"
}
if (code.length === 8) {
this.allVal[3].val = code.substring(6, 8)
this.activeKey = "3"
}
}
this.getRegionList()
this.endCallBck = endCallBck;
},
getRegionList: function() {
console.log("getRegionList")
if (this.activeKey === '0' ) {
this.queryChildList();
}else if (this.activeKey === '1' || this.activeKey === '2') {
let parent = ''
if (this.activeKey === '1') {
parent = this.allVal[0].val
}
if (this.activeKey === '2') {
if (this.allVal[1].val === ""){
parent = ""
} else {
parent = this.allVal[0].val + this.allVal[1].val
}
}
if (this.activeKey !== '0' && parent === '') {
this.$message.error({
showClose: true,
message: "请先选择上级行政区划"
})
}
if (parent !== "") {
this.queryChildList(parent);
}else {
this.regionList = []
}
}
},
queryChildList: function(parent){
console.log("queryChildList")
this.regionList = []
this.$axios({
method: 'get',
url: "/api/region/base/child/list",
params: {
parent: parent,
}
}).then((res) => {
if (res.data.code === 0) {
this.regionList = res.data.data
} else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
}).catch((error) => {
this.$message.error({
showClose: true,
message: error
});
});
},
handleOk: function (){
const code =
this.allVal[0].val +
this.allVal[1].val +
this.allVal[2].val +
this.allVal[3].val
console.log(code)
if (this.endCallBck) {
this.endCallBck(code)
}
this.showVideoDialog = false
},
closeModel: function (){
this.showVideoDialog = false
},
deviceChange: function (item){
console.log(item)
let code = this.allVal[0].val
if (this.allVal[1].val) {
code += this.allVal[1].val
if (this.allVal[2].val) {
code += this.allVal[2].val
if (this.allVal[3].val) {
code += this.allVal[3].val
}
}else {
this.allVal[3].val = ""
}
}else {
this.allVal[2].val = ""
this.allVal[3].val = ""
}
},
},
};
</script>
<style>
.show-code-item {
text-align: center;
font-size: 3rem;
}
</style>

Some files were not shown because too many files have changed in this diff Show More