BlocklyRegister.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <!-- 编程课注册页 -->
  2. <template>
  3. <!-- 注册输入框 -->
  4. <div class="login-content">
  5. <!-- 注册输入框 -->
  6. <div class="login-wrapper">
  7. <!-- 左侧图片背景盒子 -->
  8. <div class="login-left">
  9. <img :src="registerBG" alt="背景图片" class="bg-image">
  10. </div>
  11. <!-- 右侧输入框盒子 -->
  12. <div class="login-right">
  13. <div class="login-input">
  14. <span>{{ appTitle }}</span>
  15. <el-form
  16. ref="loginFormRef"
  17. :model="loginForm"
  18. :rules="rules"
  19. label-width="0px"
  20. class="input-item"
  21. >
  22. <!-- 学校输入框 -->
  23. <el-form-item prop="tenantName" v-if="false">
  24. <el-input
  25. v-model="loginForm.tenantName"
  26. :prefix-icon="HomeFilled"
  27. placeholder="请输入学校"
  28. />
  29. </el-form-item>
  30. <el-form-item prop="phone">
  31. <el-input
  32. v-model="loginForm.phone"
  33. :prefix-icon="Iphone"
  34. placeholder="请输入手机号"
  35. />
  36. </el-form-item>
  37. <el-form-item prop="password">
  38. <el-input
  39. v-model="loginForm.password"
  40. class="password-input"
  41. type="password"
  42. :prefix-icon="Lock"
  43. placeholder="请输入密码"
  44. show-password
  45. />
  46. </el-form-item>
  47. <el-form-item prop="confirmPassword">
  48. <el-input
  49. v-model="loginForm.confirmPassword"
  50. class="password-input"
  51. type="password"
  52. :prefix-icon="Lock"
  53. placeholder="请再次确认密码"
  54. show-password
  55. />
  56. </el-form-item>
  57. <el-form-item prop="inviteCode">
  58. <el-input v-model="loginForm.inviteCode" :prefix-icon="Key" placeholder="请输入邀请码" />
  59. </el-form-item>
  60. <!-- 注册按钮 -->
  61. <el-form-item>
  62. <div class="login-btn-container">
  63. <router-link to="" @click.prevent="goBackToLogin" class="register-link">已有账号,去登录</router-link>
  64. <el-button @click="handleRegister" class="login-btn" type="primary">注册</el-button>
  65. </div>
  66. </el-form-item>
  67. </el-form>
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. </template>
  73. <script setup>
  74. import { ref, computed, watch } from "vue";
  75. import { Lock, Iphone, Key, HomeFilled } from '@element-plus/icons-vue';
  76. import { ElMessage } from 'element-plus';
  77. import { useRouter } from 'vue-router';
  78. import homeBG from "@/assets/images/homeBG.png";
  79. import blocklyBG from "@/assets/images/blocklyBG.png";
  80. import aiCourseBG from "@/assets/images/aiCourseBG.png";
  81. import { registerSignUp } from '@/api/login/login.js';
  82. import { getTenantId } from '@/utils/loginUtils.js';
  83. // 获取环境变量
  84. const appTitle = ref("注册账号");
  85. const router = useRouter();
  86. const loginFormRef = ref(null);
  87. // 获取查询参数
  88. const bgImageType = computed(() => router.currentRoute.value.query.bgImage || 'homeBG');
  89. const loginTypePage = computed(() => router.currentRoute.value.query.loginType || 'login');
  90. // 注册表单数据
  91. const loginForm = ref({
  92. tenantName: "编程课租户", // 学校
  93. phone: "", // 手机号
  94. password: "", // 密码
  95. confirmPassword: "", // 确认密码
  96. inviteCode: "", // 邀请码
  97. rememberMe: false,
  98. });
  99. // 判断是否为blocklyLogin类型
  100. const isBlocklyLogin = computed(() => loginTypePage.value === 'blocklyLogin');
  101. // 设置默认值并监听变化
  102. watch(isBlocklyLogin, (newVal) => {
  103. if (newVal) {
  104. loginForm.value.tenantName = '编程课租户';
  105. }
  106. }, { immediate: true });
  107. // 动态选择背景图
  108. const registerBG = computed(() => {
  109. switch(bgImageType.value) {
  110. case 'blocklyBG':
  111. return blocklyBG;
  112. case 'aiCourseBG':
  113. return aiCourseBG;
  114. default:
  115. return homeBG;
  116. }
  117. });
  118. // 返回登录页
  119. const goBackToLogin = () => {
  120. let loginPath = '/';
  121. switch(loginTypePage.value) {
  122. case 'blocklyLogin':
  123. loginPath = '/blockly-login';
  124. break;
  125. case 'aiCourseLogin':
  126. loginPath = '/ai-login';
  127. break;
  128. default:
  129. loginPath = '/';
  130. }
  131. router.replace(loginPath);
  132. };
  133. // 表单验证规则
  134. const rules = ref({
  135. tenantName: [
  136. { required: true, message: "请输入学校", trigger: "blur" }
  137. ],
  138. phone: [
  139. { required: true, message: "请输入手机号", trigger: "blur" },
  140. { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号格式", trigger: "blur" }
  141. ],
  142. password: [
  143. { required: true, message: "请输入密码", trigger: "blur" },
  144. { min: 6, max: 20, message: "密码长度应在6-20位之间", trigger: "blur" }
  145. ],
  146. confirmPassword: [
  147. { required: true, message: "请确认密码", trigger: "blur" },
  148. {
  149. validator: (rule, value, callback) => {
  150. if (value !== loginForm.value.password) {
  151. callback(new Error("两次输入的密码不一致"));
  152. } else {
  153. callback();
  154. }
  155. },
  156. trigger: "blur"
  157. }
  158. ],
  159. inviteCode: [{ required: true, message: "请输入邀请码", trigger: "blur" }],
  160. });
  161. // 处理注册
  162. const handleRegister = async () => {
  163. if (!loginFormRef.value) return
  164. await loginFormRef.value.validate(async valid => {
  165. if (valid) {
  166. // 表单验证
  167. try {
  168. // 获取租户名称
  169. const tenantName = loginForm.value.tenantName;
  170. if (tenantName === import.meta.env.VITE_APP_TITLE) {
  171. ElMessage.error('此租户不支持注册!');
  172. return;
  173. }
  174. // 获取租户ID
  175. const tenantId = await getTenantId(tenantName);
  176. if (!tenantId) {
  177. // 租户验证失败
  178. return;
  179. }
  180. // 准备请求数据
  181. const registerData = {
  182. username: loginForm.value.phone,
  183. nickname: loginForm.value.phone,
  184. password: loginForm.value.password,
  185. inviteCode: loginForm.value.inviteCode,
  186. rememberMe: loginForm.value.rememberMe,
  187. tenantId: tenantId
  188. };
  189. // 调用注册接口
  190. const res = await registerSignUp({ 'Tenant-Id': tenantId },registerData);
  191. // 注册成功处理
  192. if (res && res.code === 0) {
  193. ElMessage.success('注册成功,请登录');
  194. goBackToLogin();
  195. } else {
  196. ElMessage.error(res && res.msg);
  197. }
  198. } catch (error) {
  199. console.error('注册失败:', error);
  200. ElMessage.error('注册失败,请检查输入信息或网络连接');
  201. }
  202. }
  203. })
  204. };
  205. </script>
  206. <style scoped lang="scss">
  207. @use "sass:math";
  208. // 定义rpx转换函数
  209. @function rpx($px) {
  210. @return math.div($px, 750) * 100vw;
  211. }
  212. .login-content {
  213. position: fixed;
  214. top: 0;
  215. left: 0;
  216. right: 0;
  217. bottom: 0;
  218. display: flex;
  219. justify-content: center; // 水平居中
  220. align-items: center; // 垂直居中
  221. background: linear-gradient(to bottom, #001169, #8a78d0);
  222. }
  223. .login-wrapper {
  224. width: rpx(500); // 固定宽度
  225. height: rpx(300); // 固定高度
  226. max-width: 90%; // 响应式最大宽度
  227. padding: 0;
  228. border-radius: rpx(15);
  229. box-shadow: 0 rpx(5) rpx(15) rgba(0, 0, 0, 0.3);
  230. display: flex;
  231. overflow: hidden;
  232. background-color: white;
  233. }
  234. /* 左侧图片背景盒子 */
  235. .login-left {
  236. width: 50%;
  237. position: relative;
  238. overflow: hidden;
  239. }
  240. /* 背景图片样式 */
  241. .bg-image {
  242. width: 100%;
  243. height: 100%;
  244. object-fit: cover;
  245. }
  246. /* 右侧输入框盒子 */
  247. .login-right {
  248. width: 50%;
  249. padding: rpx(20);
  250. display: flex;
  251. justify-content: center;
  252. align-items: center;
  253. background-color: white;
  254. }
  255. .login-input {
  256. width: 80%;
  257. display: flex;
  258. justify-content: center;
  259. align-items: center;
  260. flex-direction: column;
  261. text-align: center;
  262. }
  263. .login-input span {
  264. color: black;
  265. font-size: rpx(12);
  266. padding-bottom: rpx(10);
  267. letter-spacing: rpx(1);
  268. }
  269. .input-item {
  270. display: flex;
  271. flex-direction: column;
  272. justify-content: center;
  273. align-items: center;
  274. width: 100%;
  275. }
  276. .el-input ::v-deep(.el-input__wrapper) {
  277. border-radius: rpx(5);
  278. }
  279. .input-item .el-form-item {
  280. margin-bottom: 0;
  281. width: 100%;
  282. }
  283. .input-item .el-input {
  284. width: 100%;
  285. height: rpx(22);
  286. margin-bottom: rpx(15);
  287. font-size: rpx(8);
  288. }
  289. .el-form-item ::v-deep(.el-form-item__error) {
  290. top: rpx(25);
  291. font-size: rpx(7);
  292. }
  293. /* 按钮容器,用于控制按钮位置 */
  294. .login-btn-container {
  295. width: 100%;
  296. display: flex;
  297. justify-content: space-between;
  298. align-items: center;
  299. }
  300. .register-link {
  301. color: black;
  302. font-size: rpx(7);
  303. text-decoration: none;
  304. }
  305. .register-link:hover {
  306. text-decoration: underline;
  307. }
  308. .login-btn {
  309. width: rpx(65); /* 缩小宽度 */
  310. height: rpx(20);
  311. color: black;
  312. font-size: rpx(9);
  313. letter-spacing: rpx(4); /* 调整字间距以适应较小宽度 */
  314. border-radius: rpx(5);
  315. margin: 0;
  316. border: none;
  317. outline: none;
  318. background: linear-gradient(to bottom, #fee78a, #ffce1b);
  319. box-shadow: 0 rpx(4) rpx(4) rgb(0, 0, 0, 0.2);
  320. }
  321. .check-box {
  322. width: 100%;
  323. height: rpx(18);
  324. margin: rpx(10) 0;
  325. display: flex;
  326. justify-content: flex-end;
  327. align-items: center;
  328. }
  329. .check-box .el-checkbox {
  330. color: white;
  331. padding-right: rpx(10);
  332. font-size: rpx(7);
  333. }
  334. .el-checkbox ::v-deep(.el-checkbox__label) {
  335. font-size: rpx(7);
  336. }
  337. .el-checkbox ::v-deep(.el-checkbox__label) {
  338. font-size: 14px;
  339. }
  340. // 移动端响应式设计
  341. @media screen and (max-width: 768px) {
  342. .login-content {
  343. flex-direction: column;
  344. background: url('@/assets/images/homeBG.png') no-repeat center center fixed;
  345. background-size: cover;
  346. }
  347. .bg-image-container {
  348. position: fixed;
  349. top: 0;
  350. left: 0;
  351. width: 100%;
  352. height: 100%;
  353. z-index: -1;
  354. background-size: cover;
  355. background-position: center;
  356. }
  357. .login-wrapper {
  358. flex: none;
  359. width: 70%;
  360. margin: 0 auto;
  361. padding: 30px;
  362. border-radius: 15px;
  363. position: absolute;
  364. top: 50%;
  365. left: 50%;
  366. transform: translate(-50%, -50%);
  367. min-height: 40%;
  368. overflow-y: auto;
  369. }
  370. .login-left{
  371. display: none;
  372. }
  373. .login-right{
  374. width: 100%;
  375. }
  376. .login-input {
  377. width: 100%;
  378. height: 100%;
  379. display: flex;
  380. flex-direction: column;
  381. justify-content: center;
  382. }
  383. .login-input span {
  384. font-size: rpx(18);
  385. padding-bottom: rpx(20);
  386. }
  387. .input-item {
  388. width: 100%;
  389. align-items: stretch;
  390. }
  391. .input-item .el-input {
  392. width: 100% !important;
  393. max-width: none;
  394. height: rpx(45) !important;
  395. font-size: rpx(14) !important;
  396. margin-bottom: rpx(25) !important;
  397. }
  398. .el-input ::v-deep(.el-input__wrapper) {
  399. height: rpx(45) !important;
  400. border-radius: rpx(15);
  401. }
  402. .el-input ::v-deep(.el-input__inner) {
  403. height: rpx(45) !important;
  404. font-size: rpx(14) !important;
  405. }
  406. .login-btn {
  407. width: 40% !important;
  408. max-width: none;
  409. height: rpx(45) !important;
  410. font-size: rpx(16) !important;
  411. margin: rpx(25) 0 !important;
  412. letter-spacing: rpx(15);
  413. border-radius: rpx(10);
  414. }
  415. .check-box {
  416. width: 100% !important;
  417. max-width: none;
  418. height: auto;
  419. margin: rpx(15) 0 !important;
  420. }
  421. .check-box .el-checkbox {
  422. font-size: rpx(12) !important;
  423. }
  424. .el-checkbox ::v-deep(.el-checkbox__label) {
  425. font-size: rpx(12) !important;
  426. }
  427. .sms-code-container {
  428. width: 100% !important;
  429. max-width: none;
  430. height: rpx(45) !important;
  431. }
  432. .sms-input {
  433. width: calc(100% - rpx(75)) !important;
  434. height: rpx(45) !important;
  435. }
  436. .get-code-btn {
  437. width: rpx(75) !important;
  438. height: rpx(45) !important;
  439. font-size: rpx(10) !important;
  440. }
  441. .el-form-item ::v-deep(.el-form-item__error) {
  442. top: rpx(50) !important;
  443. font-size: rpx(10) !important;
  444. }
  445. }
  446. </style>