merge-requests/1/head
qqq 4 years ago
parent e139267fcb
commit a365a4f9a4

213
package-lock.json generated

@ -1859,6 +1859,16 @@
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
"dev": true "dev": true
}, },
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"cacache": { "cacache": {
"version": "13.0.1", "version": "13.0.1",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
@ -1885,6 +1895,34 @@
"unique-filename": "^1.1.1" "unique-filename": "^1.1.1"
} }
}, },
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"debug": { "debug": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@ -1915,6 +1953,25 @@
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
} }
}, },
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": { "locate-path": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@ -1985,6 +2042,16 @@
"minipass": "^3.1.1" "minipass": "^3.1.1"
} }
}, },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "2.3.8", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
@ -2001,6 +2068,18 @@
"terser": "^4.6.12", "terser": "^4.6.12",
"webpack-sources": "^1.4.3" "webpack-sources": "^1.4.3"
} }
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
} }
} }
}, },
@ -3102,9 +3181,9 @@
"dev": true "dev": true
}, },
"are-we-there-yet": { "are-we-there-yet": {
"version": "1.1.5", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
"dev": true, "dev": true,
"requires": { "requires": {
"delegates": "^1.0.0", "delegates": "^1.0.0",
@ -7051,9 +7130,9 @@
} }
}, },
"globule": { "globule": {
"version": "1.3.2", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz",
"integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "~7.1.1", "glob": "~7.1.1",
@ -13783,87 +13862,6 @@
} }
} }
}, },
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-ref": { "vue-ref": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/vue-ref/-/vue-ref-2.0.0.tgz", "resolved": "https://registry.npmjs.org/vue-ref/-/vue-ref-2.0.0.tgz",
@ -14621,45 +14619,12 @@
"dev": true "dev": true
}, },
"wide-align": { "wide-align": {
"version": "1.1.3", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dev": true, "dev": true,
"requires": { "requires": {
"string-width": "^1.0.2 || 2" "string-width": "^1.0.2 || 2 || 3 || 4"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
} }
}, },
"word-wrap": { "word-wrap": {

@ -1395,7 +1395,7 @@ tr.ant-table-expanded-row:hover {background: #fbfbfb;}
.ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;} .ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;}
.ant-tag-blue {color: #1890ff;background: #e6f7ff;border-color: #91d5ff;} .ant-tag-blue {color: #1890ff;background: #e6f7ff;border-color: #91d5ff;}
.ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;} .ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;}
.ant-tag-geekblue {color: #2f54eb;background: color(~`colorPalette("@{text-color-secondary}", 3)`);border-color: #adc6ff;} .ant-tag-geekblue {color: #2f54eb;background: #f0f5ff;border-color: #adc6ff;}
.ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;} .ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;}
.ant-tag-purple {color: #722ed1;background: #f9f0ff;border-color: #d3adf7;} .ant-tag-purple {color: #722ed1;background: #f9f0ff;border-color: #d3adf7;}
.ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;} .ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;}

@ -95,7 +95,7 @@ export default {
.table{ .table{
background: #ffffff; background: #ffffff;
margin: 8px 20px; margin: 8px 20px;
min-height: calc(100vh - 190px); min-height: calc(100vh - 100px);
padding: 20px; padding: 20px;
} }
} }

@ -9,6 +9,7 @@
</a-button> </a-button>
</div> </div>
<a-table <a-table
style="margin-top:40px"
:columns="columns" :columns="columns"
:row-key="record => record.id" :row-key="record => record.id"
:data-source="data" :data-source="data"
@ -170,8 +171,8 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.button-box{ .button-box{
position: absolute; position: absolute;
top:0; top:20px;
right: 20px; right: 40px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

@ -23,6 +23,7 @@
</a-button> </a-button>
</div> </div>
<a-table <a-table
style="margin-top:40px"
:columns="columns" :columns="columns"
:row-key="record => record.id" :row-key="record => record.id"
:data-source="data" :data-source="data"
@ -202,8 +203,8 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.button-box{ .button-box{
position: absolute; position: absolute;
top:0; top:20px;
right: 20px; right: 40px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

@ -14,7 +14,13 @@
marginBottom: ((selectTab.videoStyleRow * selectTab.videoStyleColumn - index - 1) >= selectTab.videoStyleColumn) ? '20px': 0 marginBottom: ((selectTab.videoStyleRow * selectTab.videoStyleColumn - index - 1) >= selectTab.videoStyleColumn) ? '20px': 0
}" }"
> >
<video :id="`camera${item.id}`" autoplay controls muted ></video> <video
:style="{
height: videoHeight,
}"
style="width: 100%;object-fit: cover;"
:id="`camera${item.id}`"
autoplay controls muted ></video>
<!-- <video-player <!-- <video-player
v-if="showVideo" v-if="showVideo"
@showModel="showModel(item)" @showModel="showModel(item)"

@ -8,52 +8,52 @@ class WebRtcPlayer {
stream = new MediaStream(); stream = new MediaStream();
uuid = null; uuid = null;
options={ options={
onStatusChange:null onStatusChange:null
}; };
constructor(video1, uuid, options={}) { constructor(video1, uuid, options={}) {
this.server = WebRtcPlayer.server; this.server = WebRtcPlayer.server;
//this.video = document.getElementById(id); //this.video = document.getElementById(id);
this.video = video1 this.video = video1
this.uuid = uuid; this.uuid = uuid;
Object.assign(this.options, options); Object.assign(this.options, options);
this.createLinks(); this.createLinks();
this.play(); this.play();
} }
createLinks() { createLinks() {
this.codecLink = "//" + this.server + "/stream/codec/" + this.uuid this.codecLink = "//" + this.server + "/stream/codec/" + this.uuid
this.rsdpLink = "//" + this.server + "/stream/receiver/" + this.uuid this.rsdpLink = "//" + this.server + "/stream/receiver/" + this.uuid
} }
play() { play() {
this.webrtc = new RTCPeerConnection({ this.webrtc = new RTCPeerConnection({
iceServers: [{ iceServers: [{
urls: ["stun:stun.l.google.com:19302"] urls: ["stun:stun.l.google.com:19302"]
}] }]
}); });
if(this.webrtc){ if(this.webrtc){
}else{ }else{
console.log("no") console.log("no")
} }
this.webrtc.onnegotiationneeded = this.handleNegotiationNeeded.bind(this); this.webrtc.onnegotiationneeded = this.handleNegotiationNeeded.bind(this);
this.webrtc.ontrack = this.onTrack.bind(this); this.webrtc.ontrack = this.onTrack.bind(this);
fetch(this.codecLink) fetch(this.codecLink)
.then((response) => { .then((response) => {
response.json().then((data) => { response.json().then((data) => {
data.forEach((item, i) => { data.forEach((item, i) => {
this.webrtc.addTransceiver(item.Type, { this.webrtc.addTransceiver(item.Type, {
'direction': 'sendrecv' 'direction': 'sendrecv'
}); });
});
});
})
.catch((error) => {
console.log(error);
}); });
});
})
.catch((error) => {
console.log(error);
});
this.webrtc.onconnectionstatechange = () => { this.webrtc.onconnectionstatechange = () => {
if(this.webrtc.connectionState == 'connected' || this.webrtc.connectionState == 'connecting'){ if(this.webrtc.connectionState == 'connected' || this.webrtc.connectionState == 'connecting'){
}else{ }else{
@ -61,63 +61,63 @@ class WebRtcPlayer {
this.load(this.uuid); this.load(this.uuid);
} }
} }
} }
async handleNegotiationNeeded() { async handleNegotiationNeeded() {
let offer = await this.webrtc.createOffer(); let offer = await this.webrtc.createOffer();
await this.webrtc.setLocalDescription(offer); await this.webrtc.setLocalDescription(offer);
let formData = new FormData(); let formData = new FormData();
formData.append('suuid', this.uuid); formData.append('suuid', this.uuid);
formData.append('data', btoa(this.webrtc.localDescription.sdp)); formData.append('data', btoa(this.webrtc.localDescription.sdp));
fetch(this.rsdpLink, { fetch(this.rsdpLink, {
method: 'POST', method: 'POST',
body: formData body: formData
})
.then((response) => {
response.text().then((data) => {
this.webrtc.setRemoteDescription(new RTCSessionDescription({
type: 'answer',
sdp: atob(data)
}))
});
}) })
.catch((err) => {}) .then((response) => {
response.text().then((data) => {
this.webrtc.setRemoteDescription(new RTCSessionDescription({
type: 'answer',
sdp: atob(data)
}))
});
})
.catch((err) => {})
} }
onTrack(event) { onTrack(event) {
this.stream.addTrack(event.track); this.stream.addTrack(event.track);
this.video.srcObject = this.stream; this.video.srcObject = this.stream;
this.video.play(); this.video.play();
} }
load(uuid) { load(uuid) {
this.destroy(); this.destroy();
this.uuid = uuid; this.uuid = uuid;
this.createLinks(); this.createLinks();
this.play(); this.play();
} }
destroy() { destroy() {
this.webrtc.close(); this.webrtc.close();
this.webrtc = null; this.webrtc = null;
this.video.srcObject = null; this.video.srcObject = null;
this.stream = new MediaStream(); this.stream = new MediaStream();
} }
getImageUrl() { getImageUrl() {
let canvas = document.createElement("canvas"); let canvas = document.createElement("canvas");
canvas.width = this.video.videoWidth; canvas.width = this.video.videoWidth;
canvas.height = this.video.videoHeight; canvas.height = this.video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
let dataURL = canvas.toDataURL(); let dataURL = canvas.toDataURL();
canvas.remove(); canvas.remove();
return dataURL; return dataURL;
} }
static setServer(serv) { static setServer(serv) {
this.server = serv; this.server = serv;
} }
} }
export default WebRtcPlayer; export default WebRtcPlayer;

@ -19,6 +19,7 @@
</a-upload> </a-upload>
</div> </div>
<a-table <a-table
style="margin-top:40px"
:columns="columns" :columns="columns"
:row-key="record => record.id" :row-key="record => record.id"
:data-source="data" :data-source="data"
@ -153,8 +154,8 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.button-box{ .button-box{
position: absolute; position: absolute;
top:0; top:20px;
right: 20px; right: 40px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

@ -4,6 +4,7 @@
新增巷道 新增巷道
</a-button> </a-button>
<a-table <a-table
style="margin-top:40px"
:columns="columns" :columns="columns"
:row-key="record => record.id" :row-key="record => record.id"
:data-source="data" :data-source="data"
@ -175,7 +176,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.add{ .add{
position: absolute; position: absolute;
top:0; top:20px;
right: 20px; right: 40px;
} }
</style> </style>

Loading…
Cancel
Save