add minprointerface

main
old易 2024-10-09 17:38:23 +08:00
parent 6cfd82bab7
commit 794d5ee78d
3 changed files with 298 additions and 60 deletions

180
package-lock.json generated
View File

@ -8,6 +8,8 @@
"name": "sso-web", "name": "sso-web",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@microsoft/signalr": "^8.0.7",
"@types/node": "^22.7.5",
"axios": "^1.7.7", "axios": "^1.7.7",
"sso-web": "file:", "sso-web": "file:",
"vue": "^3.4.37" "vue": "^3.4.37"
@ -433,6 +435,18 @@
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
}, },
"node_modules/@microsoft/signalr": {
"version": "8.0.7",
"resolved": "https://registry.npmmirror.com/@microsoft/signalr/-/signalr-8.0.7.tgz",
"integrity": "sha512-PHcdMv8v5hJlBkRHAuKG5trGViQEkPYee36LnJQx4xHOQ5LL4X0nEWIxOp5cCtZ7tu+30quz5V3k0b1YNuc6lw==",
"dependencies": {
"abort-controller": "^3.0.0",
"eventsource": "^2.0.2",
"fetch-cookie": "^2.0.3",
"node-fetch": "^2.6.7",
"ws": "^7.4.5"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.22.4", "version": "4.22.4",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
@ -647,6 +661,14 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true "dev": true
}, },
"node_modules/@types/node": {
"version": "22.7.5",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-22.7.5.tgz",
"integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "5.1.4", "version": "5.1.4",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz",
@ -751,6 +773,17 @@
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.8.tgz", "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.8.tgz",
"integrity": "sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A==" "integrity": "sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A=="
}, },
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
@ -844,6 +877,31 @@
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
}, },
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/eventsource": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-2.0.2.tgz",
"integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/fetch-cookie": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/fetch-cookie/-/fetch-cookie-2.2.0.tgz",
"integrity": "sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==",
"dependencies": {
"set-cookie-parser": "^2.4.8",
"tough-cookie": "^4.0.0"
}
},
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.15.9", "version": "1.15.9",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
@ -934,6 +992,25 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.0.tgz",
@ -971,6 +1048,29 @@
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
}, },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmmirror.com/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"engines": {
"node": ">=6"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.22.4", "version": "4.22.4",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.22.4.tgz", "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.22.4.tgz",
@ -1006,6 +1106,11 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/set-cookie-parser": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz",
"integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ=="
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
@ -1026,6 +1131,47 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz",
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
},
"node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "5.4.8", "version": "5.4.8",
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.8.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.8.tgz",
@ -1104,6 +1250,40 @@
"optional": true "optional": true
} }
} }
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/ws": {
"version": "7.5.10",
"resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
} }
} }
} }

View File

@ -11,6 +11,8 @@
"build-and-upload": "npm run build && npm run upload" "build-and-upload": "npm run build && npm run upload"
}, },
"dependencies": { "dependencies": {
"@microsoft/signalr": "^8.0.7",
"@types/node": "^22.7.5",
"axios": "^1.7.7", "axios": "^1.7.7",
"sso-web": "file:", "sso-web": "file:",
"vue": "^3.4.37" "vue": "^3.4.37"

View File

@ -17,8 +17,7 @@
{{ countdown !== null ? `${countdown} 秒后重新发送` : '获取验证码' }} {{ countdown !== null ? `${countdown} 秒后重新发送` : '获取验证码' }}
</button> </button>
</div> </div>
<button @click="verifyCodeAndLogin" <button @click="verifyCodeAndLogin" :disabled="!isPhoneValid || !isVerificationCodeValid">登录</button>
:disabled="!isPhoneValid || !isVerificationCodeValid">登录</button>
</div> </div>
<div v-if="selectedTab === 'password'"> <div v-if="selectedTab === 'password'">
@ -30,7 +29,8 @@
<div class="captcha-row"> <div class="captcha-row">
<input type="text" placeholder="验证码" v-model="captchaCode" /> <input type="text" placeholder="验证码" v-model="captchaCode" />
<!-- 显示一个默认的空白图像或设置 v-if 检查 --> <!-- 显示一个默认的空白图像或设置 v-if 检查 -->
<img alt="验证码" @click="refreshCaptcha" :src="state.captchaImage" style="cursor: pointer" width="130px" height="38px" /> <img alt="验证码" @click="refreshCaptcha" :src="state.captchaImage" style="cursor: pointer" width="130px"
height="38px" />
</div> </div>
<div v-if="captchaError" class="error-message">{{ captchaError }}</div> <div v-if="captchaError" class="error-message">{{ captchaError }}</div>
<button @click="login"></button> <button @click="login"></button>
@ -40,13 +40,15 @@
<div class="qr-code" id="login_container"> <div class="qr-code" id="login_container">
<img style="cursor: pointer;width:310px" :src="state.loginimg" /> <img style="cursor: pointer;width:310px" :src="state.loginimg" />
</div> </div>
<div v-if="scanError" class="error-message">{{ scanError }}</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { ref, onMounted, watch, nextTick, computed,reactive } from 'vue'; import * as signalR from '@microsoft/signalr';
import { ref, onMounted, watch, nextTick, computed, reactive } from 'vue';
import axios from 'axios'; import axios from 'axios';
export default { export default {
setup() { setup() {
@ -63,11 +65,13 @@ export default {
const passwordError = ref(''); // const passwordError = ref(''); //
const captchaCode = ref(''); // const captchaCode = ref(''); //
const captchaError = ref(''); // const captchaError = ref(''); //
const scanError = ref('');
const state = reactive({ const state = reactive({
captchaImage: '', captchaImage: '',
codeId:"", codeId: "",
loginimg:"" loginimg: "",
}); usercode: ""
});
// //
const switchTab = (tab) => { const switchTab = (tab) => {
selectedTab.value = tab; selectedTab.value = tab;
@ -93,7 +97,7 @@ export default {
.catch(error => { .catch(error => {
console.error('登录失败:', error); console.error('登录失败:', error);
}); });
}; };
// //
const startCountdown = () => { const startCountdown = () => {
@ -122,7 +126,7 @@ export default {
ref = 'https://www.ycymedu.com'; // ref = 'https://www.ycymedu.com'; //
} else { } else {
console.log('当前是生产环境'); console.log('当前是生产环境');
window.location.href = "https://www.ycymedu.com"; // // window.location.href = "https://www.ycymedu.com"; //
} }
} else { } else {
try { try {
@ -135,6 +139,56 @@ export default {
} }
}; };
const initsignalRConnection = () => {
// 3. Initialize SignalR client
const connection = new signalR.HubConnectionBuilder()
.withUrl('https://api.sso.ycymedu.com/hubs/wechat?usercode=' + state.usercode) // Replace with your SignalR hub URL
.configureLogging(signalR.LogLevel.Information) // Optional: Configure logging
.build();
// 4. Handle connection events
connection.on('connected', () => {
console.log('Connected to SignalR hub');
});
connection.on('disconnected', () => {
console.log('Disconnected from SignalR hub');
});
//
connection.on('ReceiveMessage', (message) => {
//messages.value.push(message);
console.log('message:', message);
if (message.status == 0)//
{
scanError.value = '授权已取消'
}
if (message.status == 1)//
{
scanError.value = '开始扫码中...'
} if (message.status == 2)//
{
scanError.value = '授权成功'
console.log('baseurl:', referer.value + message.baseurl);
// window.location.href = referer + message.baseurl;
}
});
connection.on('error', error => {
console.error('SignalR connection error:', error);
});
// 5. Start the connection
connection.start()
.then(() => {
console.log('SignalR connection started');
})
.catch(error => {
console.error('Error starting SignalR connection:', error);
});
}
// //
const initWeChatLogin = () => { const initWeChatLogin = () => {
@ -142,7 +196,9 @@ export default {
axios.post('https://api.sso.ycymedu.com/api/syswxopen/minprologin') axios.post('https://api.sso.ycymedu.com/api/syswxopen/minprologin')
.then(response => { .then(response => {
if (response.data.code === 200) { if (response.data.code === 200) {
state.loginimg = response.data.result; state.loginimg = response.data.result.img;
state.usercode = response.data.result.code;
initsignalRConnection(); // SignalR
} }
}) })
.catch(error => { .catch(error => {
@ -230,7 +286,7 @@ export default {
return true; return true;
}); });
const isPwdCode=computed(()=>{ const isPwdCode = computed(() => {
if (!captchaCode.value) { if (!captchaCode.value) {
captchaError.value = '请输入验证码'; captchaError.value = '请输入验证码';
return false; return false;
@ -251,11 +307,10 @@ export default {
// //
watch(selectedTab, (newTab) => { watch(selectedTab, (newTab) => {
if (newTab === 'wechat') if (newTab === 'wechat') {
{
initWeChatLogin(); initWeChatLogin();
} }
if(newTab === 'password'){ if (newTab === 'password') {
refreshCaptcha(); refreshCaptcha();
} }
}); });
@ -278,7 +333,7 @@ export default {
.then(response => { .then(response => {
if (response.data.code === 200) { if (response.data.code === 200) {
console.log('登录失败成功:', response.data); console.log('登录失败成功:', response.data);
window.location.href=response.data.result; window.location.href = response.data.result;
// startCountdown(); // // startCountdown(); //
} else { } else {
console.log('登录失败:', response.data); console.log('登录失败:', response.data);
@ -303,7 +358,7 @@ export default {
alert('请检查密码'); alert('请检查密码');
return; return;
} }
if(!isPwdCode.value){ if (!isPwdCode.value) {
alert('请检查验证码'); alert('请检查验证码');
return; return;
} }
@ -317,7 +372,7 @@ export default {
.then(response => { .then(response => {
if (response.data.code === 200) { if (response.data.code === 200) {
console.log('登录失败成功:', response.data); console.log('登录失败成功:', response.data);
window.location.href=response.data.result; window.location.href = response.data.result;
// startCountdown(); // // startCountdown(); //
} else { } else {
console.log('登录失败:', response.data); console.log('登录失败:', response.data);
@ -354,7 +409,9 @@ export default {
refreshCaptcha, refreshCaptcha,
captchaCode, captchaCode,
captchaError, captchaError,
state scanError,
state,
initsignalRConnection
}; };
}, },
}; };
@ -495,20 +552,19 @@ button:disabled {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: 10px;
} }
.captcha-row input { .captcha-row input {
width: calc(100% - 120px); width: calc(100% - 120px);
margin-right: 10px; margin-right: 10px;
} }
.captcha-row img { .captcha-row img {
cursor: pointer; cursor: pointer;
height: 40px; height: 40px;
width: 100px; width: 100px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #ccc; border: 1px solid #ccc;
}
} }
}
</style> </style>