Commit bc2e5e7a37876b597668db5d8c4af273deca3aea

Authored by 648540858
Committed by GitHub
2 parents 38bf8d6a 273ece96

Merge pull request #14 from lawrencehj/wvp-28181-2.0

修正上级注册的代码
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
... ... @@ -173,7 +173,7 @@ public class SIPRequestHeaderProvider {
173 173 }
174 174  
175 175  
176   - public Request createRegisterRequest(@NotNull ParentPlatform platform, String fromTag, String viaTag) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  176 + public Request createRegisterRequest(@NotNull ParentPlatform platform, long CSeq, String fromTag, String viaTag) throws ParseException, InvalidArgumentException, PeerUnavailableException {
177 177 Request request = null;
178 178 String sipAddress = sipConfig.getSipIp() + ":" + sipConfig.getSipPort();
179 179 //请求行
... ... @@ -206,7 +206,7 @@ public class SIPRequestHeaderProvider {
206 206 MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
207 207  
208 208 //ceq
209   - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.REGISTER);
  209 + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER);
210 210 request = sipFactory.createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader,
211 211 cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
212 212  
... ... @@ -214,12 +214,15 @@ public class SIPRequestHeaderProvider {
214 214 .createSipURI(platform.getDeviceGBId(), sipAddress));
215 215 request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
216 216  
  217 + ExpiresHeader expires = sipFactory.createHeaderFactory().createExpiresHeader(3600);
  218 + request.addHeader(expires);
  219 +
217 220 return request;
218 221 }
219 222  
220 223 public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String viaTag,
221 224 String callId, String realm, String nonce, String scheme) throws ParseException, PeerUnavailableException, InvalidArgumentException {
222   - Request registerRequest = createRegisterRequest(parentPlatform, fromTag, viaTag);
  225 + Request registerRequest = createRegisterRequest(parentPlatform, 2L, fromTag, viaTag);
223 226  
224 227 CallIdHeader callIdHeader = (CallIdHeader)registerRequest.getHeader(CallIdHeader.NAME);
225 228 callIdHeader.setCallId(callId);
... ... @@ -233,8 +236,7 @@ public class SIPRequestHeaderProvider {
233 236 String RESPONSE = DigestUtils.md5DigestAsHex((HA1 + ":" + nonce + ":" + HA2).getBytes());
234 237  
235 238 String authorizationHeaderContent = scheme + " username=\"" + parentPlatform.getDeviceGBId() + "\", " + "realm=\""
236   - + realm + "\", uri=\"" + uri + "\", response=\"" + RESPONSE + "\", nonce=\""
237   - + nonce + "\"";
  239 + + realm + "\", nonce=\"" + nonce + "\", uri=\"" + uri + "\", response=\"" + RESPONSE + "\"" + ", algorithm=MD5";
238 240 AuthorizationHeader authorizationHeader = sipFactory.createHeaderFactory().createAuthorizationHeader(authorizationHeaderContent);
239 241 registerRequest.addHeader(authorizationHeader);
240 242  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
... ... @@ -52,7 +52,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
52 52 try {
53 53 Request request = null;
54 54 if (realm == null || nonce == null) {
55   - request = headerProvider.createRegisterRequest(parentPlatform, null, null);
  55 + request = headerProvider.createRegisterRequest(parentPlatform, 1L, null, null);
56 56 }else {
57 57 request = headerProvider.createRegisterRequest(parentPlatform, null, null, callId, realm, nonce, scheme);
58 58 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/platform/PlatformController.java
1 1 package com.genersoft.iot.vmp.vmanager.platform;
2 2  
  3 +import com.alibaba.fastjson.JSONObject;
3 4 import com.genersoft.iot.vmp.common.PageResult;
4 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
5 6 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
... ... @@ -15,6 +16,8 @@ import org.springframework.http.ResponseEntity;
15 16 import org.springframework.stereotype.Controller;
16 17 import org.springframework.util.StringUtils;
17 18 import org.springframework.web.bind.annotation.*;
  19 +import com.genersoft.iot.vmp.conf.SipConfig;
  20 +
18 21  
19 22 @CrossOrigin
20 23 @RestController
... ... @@ -29,6 +32,19 @@ public class PlatformController {
29 32 @Autowired
30 33 private ISIPCommanderForPlatform commanderForPlatform;
31 34  
  35 + @Autowired
  36 + private SipConfig sipConfig;
  37 +
  38 + @GetMapping("/platforms/serverconfig")
  39 + public ResponseEntity<JSONObject> serverConfig() {
  40 + JSONObject result = new JSONObject();
  41 + result.put("deviceIp", sipConfig.getSipIp());
  42 + result.put("devicePort", sipConfig.getSipPort());
  43 + result.put("username", sipConfig.getSipId());
  44 + result.put("password", sipConfig.getSipPassword());
  45 + return new ResponseEntity<>(result, HttpStatus.OK);
  46 + }
  47 +
32 48 @GetMapping("/platforms/{count}/{page}")
33 49 public PageResult<ParentPlatform> platforms(@PathVariable int page, @PathVariable int count){
34 50  
... ...
web_src/src/components/platformEdit.vue
1 1 <template>
2   -<div id="addlatform" v-loading="isLoging">
3   - <el-dialog title="添加平台" width="70%" top="2rem" :close-on-click-modal="false" :visible.sync="showDialog" :destroy-on-close="true" @close="close()">
4   - <div id="shared" style="text-align: right; margin-top: 1rem;">
5   -
6   - <el-row :gutter="24">
7   - <el-col :span="11">
8   - <el-form ref="platform1" :rules="rules" :model="platform" label-width="160px" >
9   - <el-form-item label="名称" prop="name">
10   - <el-input v-model="platform.name"></el-input>
11   - </el-form-item>
12   - <el-form-item label="SIP服务国标编码" prop="serverGBId">
13   - <el-input v-model="platform.serverGBId" clearable></el-input>
14   - </el-form-item>
15   - <el-form-item label="SIP服务国标域" prop="serverGBDomain">
16   - <el-input v-model="platform.serverGBDomain" clearable></el-input>
17   - </el-form-item>
18   - <el-form-item label="SIP服务IP" prop="serverIP">
19   - <el-input v-model="platform.serverIP" clearable></el-input>
20   - </el-form-item>
21   - <el-form-item label="SIP服务端口" prop="serverPort">
22   - <el-input v-model="platform.serverPort" clearable></el-input>
23   - </el-form-item>
24   - <el-form-item label="设备国标编号" prop="deviceGBId">
25   - <el-input v-model="platform.deviceGBId" clearable></el-input>
26   - </el-form-item>
27   - <el-form-item label="本地IP" prop="deviceIp">
28   - <el-input v-model="platform.deviceIp" :disabled="true"></el-input>
29   - </el-form-item>
30   - <el-form-item label="本地端口" prop="devicePort">
31   - <el-input v-model="platform.devicePort" :disabled="true"></el-input>
32   - </el-form-item>
33   -
34   - </el-form>
35   - </el-col>
36   - <el-col :span="12">
37   - <el-form ref="platform2" :rules="rules" :model="platform" label-width="160px">
38   - <el-form-item label="SIP认证用户名" prop="username">
39   - <el-input v-model="platform.username"></el-input>
40   - </el-form-item>
41   - <el-form-item label="SIP认证密码" prop="password">
42   - <el-input v-model="platform.password" type="password"></el-input>
43   - </el-form-item>
44   - <el-form-item label="注册周期(秒)" prop="expires">
45   - <el-input v-model="platform.expires"></el-input>
46   - </el-form-item>
47   - <el-form-item label="心跳周期(秒)" prop="keepTimeout">
48   - <el-input v-model="platform.keepTimeout"></el-input>
49   - </el-form-item>
50   - <el-form-item label="信令传输" prop="transport">
51   - <el-select v-model="platform.transport" style="width:100%" placeholder="请选择信令传输方式">
52   - <el-option label="UDP" value="UDP"></el-option>
53   - <el-option label="TCP" value="TCP"></el-option>
54   - </el-select>
55   - </el-form-item>
56   - <el-form-item label="字符集" prop="characterSet">
57   - <el-select v-model="platform.characterSet" style="width:100%" placeholder="请选择字符集">
58   - <el-option label="GB2312" value="GB2312"></el-option>
59   - <el-option label="UTF-8" value="UTF-8"></el-option>
60   - </el-select>
61   - </el-form-item>
62   - <el-form-item label="其他选项" >
63   - <el-checkbox label="启用" v-model="platform.enable" ></el-checkbox>
64   - <el-checkbox label="云台控制" v-model="platform.PTZEnable"></el-checkbox>
65   - <el-checkbox label="RTCP保活" v-model="platform.rtcp"></el-checkbox>
66   - </el-form-item>
67   - <el-form-item>
68   - <el-button type="primary" @click="onSubmit">{{onSubmit_text}}</el-button>
69   - <el-button @click="close">取消</el-button>
70   - </el-form-item>
71   - </el-form>
72   - </el-col>
73   - </el-row>
74   - </div>
  2 + <div id="addlatform" v-loading="isLoging">
  3 + <el-dialog
  4 + title="添加平台"
  5 + width="70%"
  6 + top="2rem"
  7 + :close-on-click-modal="false"
  8 + :visible.sync="showDialog"
  9 + :destroy-on-close="true"
  10 + @close="close()"
  11 + >
  12 + <div id="shared" style="text-align: right; margin-top: 1rem">
  13 + <el-row :gutter="24">
  14 + <el-col :span="11">
  15 + <el-form ref="platform1" :rules="rules" :model="platform" label-width="160px">
  16 + <el-form-item label="名称" prop="name">
  17 + <el-input v-model="platform.name"></el-input>
  18 + </el-form-item>
  19 + <el-form-item label="SIP服务国标编码" prop="serverGBId">
  20 + <el-input v-model="platform.serverGBId" clearable></el-input>
  21 + </el-form-item>
  22 + <el-form-item label="SIP服务国标域" prop="serverGBDomain">
  23 + <el-input v-model="platform.serverGBDomain" clearable></el-input>
  24 + </el-form-item>
  25 + <el-form-item label="SIP服务IP" prop="serverIP">
  26 + <el-input v-model="platform.serverIP" clearable></el-input>
  27 + </el-form-item>
  28 + <el-form-item label="SIP服务端口" prop="serverPort">
  29 + <el-input v-model="platform.serverPort" clearable></el-input>
  30 + </el-form-item>
  31 + <el-form-item label="设备国标编号" prop="deviceGBId">
  32 + <el-input v-model="platform.deviceGBId" clearable></el-input>
  33 + </el-form-item>
  34 + <el-form-item label="本地IP" prop="deviceIp">
  35 + <el-input v-model="platform.deviceIp" :disabled="true"></el-input>
  36 + </el-form-item>
  37 + <el-form-item label="本地端口" prop="devicePort">
  38 + <el-input v-model="platform.devicePort" :disabled="true"></el-input>
  39 + </el-form-item>
  40 + </el-form>
  41 + </el-col>
  42 + <el-col :span="12">
  43 + <el-form ref="platform2" :rules="rules" :model="platform" label-width="160px">
  44 + <el-form-item label="SIP认证用户名" prop="username">
  45 + <el-input v-model="platform.username"></el-input>
  46 + </el-form-item>
  47 + <el-form-item label="SIP认证密码" prop="password">
  48 + <el-input v-model="platform.password" type="password"></el-input>
  49 + </el-form-item>
  50 + <el-form-item label="注册周期(秒)" prop="expires">
  51 + <el-input v-model="platform.expires"></el-input>
  52 + </el-form-item>
  53 + <el-form-item label="心跳周期(秒)" prop="keepTimeout">
  54 + <el-input v-model="platform.keepTimeout"></el-input>
  55 + </el-form-item>
  56 + <el-form-item label="信令传输" prop="transport">
  57 + <el-select
  58 + v-model="platform.transport"
  59 + style="width: 100%"
  60 + placeholder="请选择信令传输方式"
  61 + >
  62 + <el-option label="UDP" value="UDP"></el-option>
  63 + <el-option label="TCP" value="TCP"></el-option>
  64 + </el-select>
  65 + </el-form-item>
  66 + <el-form-item label="字符集" prop="characterSet">
  67 + <el-select
  68 + v-model="platform.characterSet"
  69 + style="width: 100%"
  70 + placeholder="请选择字符集"
  71 + >
  72 + <el-option label="GB2312" value="GB2312"></el-option>
  73 + <el-option label="UTF-8" value="UTF-8"></el-option>
  74 + </el-select>
  75 + </el-form-item>
  76 + <el-form-item label="其他选项">
  77 + <el-checkbox label="启用" v-model="platform.enable"></el-checkbox>
  78 + <el-checkbox label="云台控制" v-model="platform.PTZEnable"></el-checkbox>
  79 + <el-checkbox label="RTCP保活" v-model="platform.rtcp"></el-checkbox>
  80 + </el-form-item>
  81 + <el-form-item>
  82 + <el-button type="primary" @click="onSubmit">{{
  83 + onSubmit_text
  84 + }}</el-button>
  85 + <el-button @click="close">取消</el-button>
  86 + </el-form-item>
  87 + </el-form>
  88 + </el-col>
  89 + </el-row>
  90 + </div>
75 91 </el-dialog>
76   -</div>
  92 + </div>
77 93 </template>
78 94  
79 95 <script>
80 96 export default {
81   - name: 'platformEdit',
82   - props: {},
83   - computed: {
84   -
  97 + name: "platformEdit",
  98 + props: {},
  99 + computed: {},
  100 + created() {},
  101 + data() {
  102 + var deviceGBIdRules = async (rule, value, callback) => {
  103 + console.log(value);
  104 + if (value === "") {
  105 + callback(new Error("请输入设备国标编号"));
  106 + } else {
  107 + var exit = await this.deviceGBIdExit(value);
  108 + console.log(exit);
  109 + console.log(exit == "true");
  110 + console.log(exit === "true");
  111 + if (exit) {
  112 + callback(new Error("设备国标编号已存在"));
  113 + } else {
  114 + callback();
  115 + }
  116 + }
  117 + };
  118 + return {
  119 + listChangeCallback: null,
  120 + showDialog: false,
  121 + isLoging: false,
  122 + onSubmit_text: "立即创建",
  123 + // platform: {
  124 + // enable: false,
  125 + // PTZEnable: true,
  126 + // rtcp: false,
  127 + // name: null,
  128 + // serverGBId: null,
  129 + // serverGBDomain: null,
  130 + // serverIP: null,
  131 + // serverPort: null,
  132 + // deviceGBId: null,
  133 + // deviceIp: null,
  134 + // devicePort: null,
  135 + // username: null,
  136 + // password: null,
  137 + // expires: 300,
  138 + // keepTimeout: 60,
  139 + // transport: "UDP",
  140 + // characterSet: "GB2312",
  141 + // },
  142 + platform: {
  143 + enable: true,
  144 + PTZEnable: true,
  145 + rtcp: false,
  146 + name: "测试001",
  147 + serverGBId: "34020000002000000001",
  148 + serverGBDomain: "3402000000",
  149 + serverIP: "192.168.1.141",
  150 + serverPort: "5060",
  151 + deviceGBId: "34020000001320001101",
  152 + deviceIp: "192.168.1.20",
  153 + devicePort: "5060",
  154 + username: "34020000001320001101",
  155 + password: "12345678",
  156 + expires: 300,
  157 + keepTimeout: 60,
  158 + transport: "UDP",
  159 + characterSet: "GB2312",
  160 + },
  161 + rules: {
  162 + name: [{ required: true, message: "请输入平台名称", trigger: "blur" }],
  163 + serverGBId: [
  164 + { required: true, message: "请输入SIP服务国标编码", trigger: "blur" },
  165 + ],
  166 + serverGBDomain: [
  167 + { required: true, message: "请输入SIP服务国标域", trigger: "blur" },
  168 + ],
  169 + serverIP: [{ required: true, message: "请输入SIP服务IP", trigger: "blur" }],
  170 + serverPort: [{ required: true, message: "请输入SIP服务端口", trigger: "blur" }],
  171 + deviceGBId: [{ validator: deviceGBIdRules, trigger: "blur" }],
  172 + username: [{ required: false, message: "请输入SIP认证用户名", trigger: "blur" }],
  173 + password: [{ required: false, message: "请输入SIP认证密码", trigger: "blur" }],
  174 + expires: [{ required: true, message: "请输入注册周期", trigger: "blur" }],
  175 + keepTimeout: [{ required: true, message: "请输入心跳周期", trigger: "blur" }],
  176 + transport: [{ required: true, message: "请选择信令传输", trigger: "blur" }],
  177 + characterSet: [{ required: true, message: "请选择编码字符集", trigger: "blur" }],
  178 + },
  179 + };
  180 + },
  181 + methods: {
  182 + openDialog: function (platform, callback) {
  183 + var that = this;
  184 + this.$axios
  185 + .get(`/api/platforms/serverconfig`)
  186 + .then(function (res) {
  187 + console.log(res);
  188 + that.platform.deviceGBId = res.data.username;
  189 + that.platform.deviceIp = res.data.deviceIp;
  190 + that.platform.devicePort = res.data.devicePort;
  191 + that.platform.username = res.data.username;
  192 + that.platform.password = res.data.password;
  193 + })
  194 + .catch(function (error) {
  195 + console.log(error);
  196 + });
  197 + this.showDialog = true;
  198 + this.listChangeCallback = callback;
  199 + if (platform != null) {
  200 + this.platform = platform;
  201 + this.onSubmit_text = "保存";
  202 + } else {
  203 + }
85 204 },
86   - created() {},
87   - data() {
88   - var deviceGBIdRules = async (rule, value, callback) => {
89   - console.log(value)
90   - if (value === '') {
91   - callback(new Error('请输入设备国标编号'));
92   - } else {
93   - var exit = await this.deviceGBIdExit(value);
94   - console.log(exit)
95   - console.log(exit == "true")
96   - console.log(exit === "true")
97   - if (exit) {
98   - callback(new Error('设备国标编号已存在'));
99   - }else {
100   - callback();
  205 + onSubmit: function () {
  206 + console.log("onSubmit");
  207 + var that = this;
  208 + that.$axios
  209 + .post(`/api/platforms/save`, that.platform)
  210 + .then(function (res) {
  211 + console.log(res);
  212 + console.log(res.data == "success");
  213 + if (res.data == "success") {
  214 + that.$message({
  215 + showClose: true,
  216 + message: "保存成功",
  217 + type: "success",
  218 + });
  219 + that.showDialog = false;
  220 + if (that.listChangeCallback != null) {
  221 + that.listChangeCallback();
101 222 }
102 223 }
103   - };
104   - return {
105   - listChangeCallback: null,
106   - showDialog: false,
107   - isLoging: false,
108   - onSubmit_text:"立即创建",
109   - // platform: {
110   - // enable: false,
111   - // PTZEnable: true,
112   - // rtcp: false,
113   - // name: null,
114   - // serverGBId: null,
115   - // serverGBDomain: null,
116   - // serverIP: null,
117   - // serverPort: null,
118   - // deviceGBId: null,
119   - // deviceIp: null,
120   - // devicePort: null,
121   - // username: null,
122   - // password: null,
123   - // expires: 300,
124   - // keepTimeout: 60,
125   - // transport: "UDP",
126   - // characterSet: "GB2312",
127   - // },
128   - platform: {
129   - enable: true,
130   - PTZEnable: true,
131   - rtcp: false,
132   - name: "测试001",
133   - serverGBId: "34020000002000000001",
134   - serverGBDomain: "3402000000",
135   - serverIP: "192.168.1.141",
136   - serverPort: "5060",
137   - deviceGBId: "34020000001320001101",
138   - deviceIp: "192.168.1.20",
139   - devicePort: "5060",
140   - username: "34020000001320001101",
141   - password: "12345678",
142   - expires: 300,
143   - keepTimeout: 60,
144   - transport: "UDP",
145   - characterSet: "GB2312",
146   - },
147   - rules: {
148   - name: [
149   - { required: true, message:"请输入平台名称", trigger: 'blur' }
150   - ],
151   - serverGBId: [
152   - { required: true, message:"请输入SIP服务国标编码", trigger: 'blur' }
153   - ],
154   - serverGBDomain: [
155   - { required: true, message:"请输入SIP服务国标域", trigger: 'blur' }
156   - ],
157   - serverIP: [
158   - { required: true, message:"请输入SIP服务IP", trigger: 'blur' }
159   - ],
160   - serverPort: [
161   - { required: true, message:"请输入SIP服务端口", trigger: 'blur' }
162   - ],
163   - deviceGBId: [
164   - {validator: deviceGBIdRules, trigger: 'blur' }
165   - ],
166   - username: [
167   - { required: false, message:"请输入SIP认证用户名", trigger: 'blur' }
168   - ],
169   - password: [
170   - { required: false, message:"请输入SIP认证密码", trigger: 'blur' }
171   - ],
172   - expires: [
173   - { required: true, message:"请输入注册周期", trigger: 'blur' }
174   - ],
175   - keepTimeout: [
176   - { required: true, message:"请输入心跳周期", trigger: 'blur' }
177   - ],
178   - transport: [
179   - { required: true, message:"请选择信令传输", trigger: 'blur' }
180   - ],
181   - characterSet: [
182   - { required: true, message:"请选择编码字符集", trigger: 'blur' }
183   - ]
184   - }
185   - };
  224 + })
  225 + .catch(function (error) {
  226 + console.log(error);
  227 + });
186 228 },
187   - methods: {
188   - openDialog: function (platform, callback) {
189   - this.showDialog = true;
190   - this.listChangeCallback = callback;
191   - if (platform != null) {
192   - this.platform = platform;
193   - this.onSubmit_text = "保存"
194   - }
195   -
196   - },
197   - onSubmit: function () {
198   - console.log('onSubmit');
199   - var that = this;
200   - that.$axios.post(`/api/platforms/save`, that.platform)
201   - .then(function (res) {
202   - console.log(res)
203   - console.log(res.data == "success")
204   - if (res.data == "success") {
205   - that.$message({
206   - showClose: true,
207   - message: '保存成功',
208   - type: 'success'
209   - });
210   - that.showDialog = false;
211   - if (that.listChangeCallback != null) {
212   - that.listChangeCallback()
213   - }
214   - }
215   - })
216   - .catch(function (error) {
217   - console.log(error);
218   - });
219   - },
220   - close: function () {
221   - console.log('关闭添加视频平台');
222   - this.showDialog = false;
223   - this.$refs.platform1.resetFields();
224   - this.$refs.platform2.resetFields();
225   - },
226   - deviceGBIdExit: async function (deviceGbId) {
227   - var result = false;
228   - var that = this
229   - await that.$axios.post(`/api/platforms/exit/${deviceGbId}`)
230   - .then(function (res) {
231   - result = res.data;
232   - })
233   - .catch(function (error) {
234   - console.log(error);
235   - });
236   - return result;
237   -
238   - }
239   -
240   - }
  229 + close: function () {
  230 + console.log("关闭添加视频平台");
  231 + this.showDialog = false;
  232 + this.$refs.platform1.resetFields();
  233 + this.$refs.platform2.resetFields();
  234 + },
  235 + deviceGBIdExit: async function (deviceGbId) {
  236 + var result = false;
  237 + var that = this;
  238 + await that.$axios
  239 + .post(`/api/platforms/exit/${deviceGbId}`)
  240 + .then(function (res) {
  241 + result = res.data;
  242 + })
  243 + .catch(function (error) {
  244 + console.log(error);
  245 + });
  246 + return result;
  247 + },
  248 + },
241 249 };
242 250 </script>
243 251  
244 252 <style>
245 253 .control-wrapper {
246   - position: relative;
247   - width: 6.25rem;
248   - height: 6.25rem;
249   - max-width: 6.25rem;
250   - max-height: 6.25rem;
251   - border-radius: 100%;
252   - margin-top: 2.5rem;
253   - margin-left: 0.5rem;
254   - float: left;
  254 + position: relative;
  255 + width: 6.25rem;
  256 + height: 6.25rem;
  257 + max-width: 6.25rem;
  258 + max-height: 6.25rem;
  259 + border-radius: 100%;
  260 + margin-top: 2.5rem;
  261 + margin-left: 0.5rem;
  262 + float: left;
255 263 }
256 264  
257 265 .control-panel {
258   - position: relative;
259   - top: 0;
260   - left: 5rem;
261   - height: 11rem;
262   - max-height: 11rem;
  266 + position: relative;
  267 + top: 0;
  268 + left: 5rem;
  269 + height: 11rem;
  270 + max-height: 11rem;
263 271 }
264 272  
265 273 .control-btn {
266   - display: flex;
267   - justify-content: center;
268   - position: absolute;
269   - width: 44%;
270   - height: 44%;
271   - border-radius: 5px;
272   - border: 1px solid #78aee4;
273   - box-sizing: border-box;
274   - transition: all 0.3s linear;
  274 + display: flex;
  275 + justify-content: center;
  276 + position: absolute;
  277 + width: 44%;
  278 + height: 44%;
  279 + border-radius: 5px;
  280 + border: 1px solid #78aee4;
  281 + box-sizing: border-box;
  282 + transition: all 0.3s linear;
275 283 }
276 284  
277 285 .control-btn i {
278   - font-size: 20px;
279   - color: #78aee4;
280   - display: flex;
281   - justify-content: center;
282   - align-items: center;
  286 + font-size: 20px;
  287 + color: #78aee4;
  288 + display: flex;
  289 + justify-content: center;
  290 + align-items: center;
283 291 }
284 292  
285 293 .control-round {
286   - position: absolute;
287   - top: 21%;
288   - left: 21%;
289   - width: 58%;
290   - height: 58%;
291   - background: #fff;
292   - border-radius: 100%;
  294 + position: absolute;
  295 + top: 21%;
  296 + left: 21%;
  297 + width: 58%;
  298 + height: 58%;
  299 + background: #fff;
  300 + border-radius: 100%;
293 301 }
294 302  
295 303 .control-round-inner {
296   - position: absolute;
297   - left: 13%;
298   - top: 13%;
299   - display: flex;
300   - justify-content: center;
301   - align-items: center;
302   - width: 70%;
303   - height: 70%;
304   - font-size: 40px;
305   - color: #78aee4;
306   - border: 1px solid #78aee4;
307   - border-radius: 100%;
308   - transition: all 0.3s linear;
  304 + position: absolute;
  305 + left: 13%;
  306 + top: 13%;
  307 + display: flex;
  308 + justify-content: center;
  309 + align-items: center;
  310 + width: 70%;
  311 + height: 70%;
  312 + font-size: 40px;
  313 + color: #78aee4;
  314 + border: 1px solid #78aee4;
  315 + border-radius: 100%;
  316 + transition: all 0.3s linear;
309 317 }
310 318  
311 319 .control-inner-btn {
312   - position: absolute;
313   - width: 60%;
314   - height: 60%;
315   - background: #fafafa;
  320 + position: absolute;
  321 + width: 60%;
  322 + height: 60%;
  323 + background: #fafafa;
316 324 }
317 325  
318 326 .control-top {
319   - top: -8%;
320   - left: 27%;
321   - transform: rotate(-45deg);
322   - border-radius: 5px 100% 5px 0;
  327 + top: -8%;
  328 + left: 27%;
  329 + transform: rotate(-45deg);
  330 + border-radius: 5px 100% 5px 0;
323 331 }
324 332  
325 333 .control-top i {
326   - transform: rotate(45deg);
327   - border-radius: 5px 100% 5px 0;
  334 + transform: rotate(45deg);
  335 + border-radius: 5px 100% 5px 0;
328 336 }
329 337  
330 338 .control-top .control-inner {
331   - left: -1px;
332   - bottom: 0;
333   - border-top: 1px solid #78aee4;
334   - border-right: 1px solid #78aee4;
335   - border-radius: 0 100% 0 0;
  339 + left: -1px;
  340 + bottom: 0;
  341 + border-top: 1px solid #78aee4;
  342 + border-right: 1px solid #78aee4;
  343 + border-radius: 0 100% 0 0;
336 344 }
337 345  
338 346 .control-top .fa {
339   - transform: rotate(45deg) translateY(-7px);
  347 + transform: rotate(45deg) translateY(-7px);
340 348 }
341 349  
342 350 .control-left {
343   - top: 27%;
344   - left: -8%;
345   - transform: rotate(45deg);
346   - border-radius: 5px 0 5px 100%;
  351 + top: 27%;
  352 + left: -8%;
  353 + transform: rotate(45deg);
  354 + border-radius: 5px 0 5px 100%;
347 355 }
348 356  
349 357 .control-left i {
350   - transform: rotate(-45deg);
  358 + transform: rotate(-45deg);
351 359 }
352 360  
353 361 .control-left .control-inner {
354   - right: -1px;
355   - top: -1px;
356   - border-bottom: 1px solid #78aee4;
357   - border-left: 1px solid #78aee4;
358   - border-radius: 0 0 0 100%;
  362 + right: -1px;
  363 + top: -1px;
  364 + border-bottom: 1px solid #78aee4;
  365 + border-left: 1px solid #78aee4;
  366 + border-radius: 0 0 0 100%;
359 367 }
360 368  
361 369 .control-left .fa {
362   - transform: rotate(-45deg) translateX(-7px);
  370 + transform: rotate(-45deg) translateX(-7px);
363 371 }
364 372  
365 373 .control-right {
366   - top: 27%;
367   - right: -8%;
368   - transform: rotate(45deg);
369   - border-radius: 5px 100% 5px 0;
  374 + top: 27%;
  375 + right: -8%;
  376 + transform: rotate(45deg);
  377 + border-radius: 5px 100% 5px 0;
370 378 }
371 379  
372 380 .control-right i {
373   - transform: rotate(-45deg);
  381 + transform: rotate(-45deg);
374 382 }
375 383  
376 384 .control-right .control-inner {
377   - left: -1px;
378   - bottom: -1px;
379   - border-top: 1px solid #78aee4;
380   - border-right: 1px solid #78aee4;
381   - border-radius: 0 100% 0 0;
  385 + left: -1px;
  386 + bottom: -1px;
  387 + border-top: 1px solid #78aee4;
  388 + border-right: 1px solid #78aee4;
  389 + border-radius: 0 100% 0 0;
382 390 }
383 391  
384 392 .control-right .fa {
385   - transform: rotate(-45deg) translateX(7px);
  393 + transform: rotate(-45deg) translateX(7px);
386 394 }
387 395  
388 396 .control-bottom {
389   - left: 27%;
390   - bottom: -8%;
391   - transform: rotate(45deg);
392   - border-radius: 0 5px 100% 5px;
  397 + left: 27%;
  398 + bottom: -8%;
  399 + transform: rotate(45deg);
  400 + border-radius: 0 5px 100% 5px;
393 401 }
394 402  
395 403 .control-bottom i {
396   - transform: rotate(-45deg);
  404 + transform: rotate(-45deg);
397 405 }
398 406  
399 407 .control-bottom .control-inner {
400   - top: -1px;
401   - left: -1px;
402   - border-bottom: 1px solid #78aee4;
403   - border-right: 1px solid #78aee4;
404   - border-radius: 0 0 100% 0;
  408 + top: -1px;
  409 + left: -1px;
  410 + border-bottom: 1px solid #78aee4;
  411 + border-right: 1px solid #78aee4;
  412 + border-radius: 0 0 100% 0;
405 413 }
406 414  
407 415 .control-bottom .fa {
408   - transform: rotate(-45deg) translateY(7px);
  416 + transform: rotate(-45deg) translateY(7px);
409 417 }
410 418 </style>
... ...