|
@@ -7,6 +7,12 @@ import {
|
|
|
import { store } from "@/store/store";
|
|
|
import { createUserDetailLog } from "@/utils/logger";
|
|
|
import { useRouter } from "vue-router";
|
|
|
+import { getElectronConfig } from "./useElectronConfig";
|
|
|
+import { getGeeTestConfig } from "./useGeeTestConfig";
|
|
|
+import { DOMAIN, VITE_GIT_REPO_VERSION } from "@/constants/constants";
|
|
|
+import { onMounted, onUnmounted, watch } from "vue";
|
|
|
+import { Person, LockClosed, CloseCircleOutline } from "@vicons/ionicons5";
|
|
|
+import { FormRules, FormItemInst } from "naive-ui";
|
|
|
|
|
|
logger({
|
|
|
cnl: ["console", "local", "server"],
|
|
@@ -14,35 +20,124 @@ logger({
|
|
|
act: "首次渲染",
|
|
|
});
|
|
|
|
|
|
-const accountType = $ref("STUDENT_CODE");
|
|
|
-const accountValue = $ref("");
|
|
|
-const password = $ref("");
|
|
|
-// console.log(import.meta.env);
|
|
|
-const domain = import.meta.env.DEV
|
|
|
- ? (import.meta.env.VITE_DEVELOPMENT_DOMAIN as string)
|
|
|
- : "";
|
|
|
-const rootOrgId = 0;
|
|
|
+let newVersionAvailable = $ref(false);
|
|
|
+const checkNewVersionListener = () => {
|
|
|
+ newVersionAvailable = true;
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() =>
|
|
|
+ document.addEventListener("__newSWAvailable", checkNewVersionListener, {
|
|
|
+ once: true,
|
|
|
+ })
|
|
|
+);
|
|
|
+
|
|
|
+onUnmounted(() =>
|
|
|
+ document.removeEventListener("__newSWAvailable", checkNewVersionListener)
|
|
|
+);
|
|
|
+
|
|
|
+let isGeeTestEnabled = $ref(false);
|
|
|
+onMounted(async () => {
|
|
|
+ const conf = await getElectronConfig();
|
|
|
+ store.QECSConfig = conf;
|
|
|
+
|
|
|
+ isGeeTestEnabled = await getGeeTestConfig(store.QECSConfig.ROOT_ORG_ID);
|
|
|
+});
|
|
|
+
|
|
|
+const QECSConfig = store.QECSConfig;
|
|
|
+
|
|
|
+const logoPath = $computed(() => store.QECSConfig.LOGO_FILE_URL || "");
|
|
|
+const backgroundUrl = $computed(
|
|
|
+ () =>
|
|
|
+ `url(${
|
|
|
+ store.QECSConfig.STUDENT_CLIENT_BG_PICTURE_URL ||
|
|
|
+ "https://cdn.qmth.com.cn/ui/ecs-client-bg.jpg!/progressive/true"
|
|
|
+ })`
|
|
|
+);
|
|
|
+const productName = $computed(
|
|
|
+ () => store.QECSConfig.OE_STUDENT_SYS_NAME || "远程教育网络考试"
|
|
|
+);
|
|
|
+const allowLoginType = $computed(() => store.QECSConfig.LOGIN_TYPE ?? []);
|
|
|
+// const isGeeTestEnabled = $computed(() => {
|
|
|
+// const thisOrg = this.GeeTestConfig[QECSConfig.ROOT_ORG_ID];
|
|
|
+// const isThisOrgUndefined = thisOrg === undefined;
|
|
|
+// const allOrg = this.GeeTestConfig["-1"];
|
|
|
+// return isThisOrgUndefined ? allOrg : thisOrg;
|
|
|
+// });
|
|
|
+
|
|
|
+const domain = DOMAIN;
|
|
|
+
|
|
|
+let disableLoginBtn = $computed(
|
|
|
+ () => false
|
|
|
+ // (isElectron() &&
|
|
|
+ // (this.disableLoginBtnBecauseRemoteApp ||
|
|
|
+ // this.disableLoginBtnBecauseVCam)) ||
|
|
|
+ // this.disableLoginBtnBecauseAppVersionChecker ||
|
|
|
+ // this.disableLoginBtnBecauseRefreshServiceWorker ||
|
|
|
+ // this.disableLoginBtnBecauseNotAllowedNative
|
|
|
+);
|
|
|
+
|
|
|
+let loginBtnLoading = $ref(false);
|
|
|
+
|
|
|
+type FormModel = {
|
|
|
+ accountType: "STUDENT_CODE" | "IDENTITY_NUMBER";
|
|
|
+ accountValue: string;
|
|
|
+ password: string;
|
|
|
+ domain: string;
|
|
|
+ rootOrgId: string;
|
|
|
+};
|
|
|
+const formRef: FormItemInst = $ref();
|
|
|
+const formValue: FormModel = $ref({
|
|
|
+ accountType: "STUDENT_CODE",
|
|
|
+ accountValue: "",
|
|
|
+ password: "",
|
|
|
+ domain,
|
|
|
+ rootOrgId: "",
|
|
|
+});
|
|
|
+
|
|
|
+const fromRules: FormRules = {
|
|
|
+ accountValue: {
|
|
|
+ required: true,
|
|
|
+ trigger: "blur",
|
|
|
+ message: "账号必填",
|
|
|
+ },
|
|
|
+ password: {
|
|
|
+ required: true,
|
|
|
+ trigger: "blur",
|
|
|
+ message: "密码必填",
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+let errorInfo = $ref("");
|
|
|
+watch([formValue], () => (errorInfo = ""));
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
-async function handleLoginClick() {
|
|
|
+async function loginForuser() {
|
|
|
+ if (await formRef.validate().catch(() => true)) return;
|
|
|
+
|
|
|
+ logger({
|
|
|
+ pgn: "登录页面",
|
|
|
+ act: "login clicked",
|
|
|
+ ext: { UA: navigator.userAgent },
|
|
|
+ });
|
|
|
+ errorInfo = "";
|
|
|
const res = await loginApi(
|
|
|
- accountType,
|
|
|
- accountValue,
|
|
|
- password,
|
|
|
+ formValue.accountType,
|
|
|
+ formValue.accountValue,
|
|
|
+ formValue.password,
|
|
|
domain,
|
|
|
- rootOrgId
|
|
|
+ store.QECSConfig.ROOT_ORG_ID
|
|
|
);
|
|
|
- console.log(res);
|
|
|
+
|
|
|
if (res.data.code === "200") {
|
|
|
+ errorInfo = "";
|
|
|
// 准备下面的登录token
|
|
|
store.user = res.data.content;
|
|
|
} else {
|
|
|
- // TODO: 放置在登录框
|
|
|
- $message.error(res.data.desc);
|
|
|
+ errorInfo = res.data.desc;
|
|
|
logger({
|
|
|
pgu: "AUTO",
|
|
|
- act: "点击登录",
|
|
|
+ act: "点击登录-res-error",
|
|
|
stk: res.data.code + res.data.desc,
|
|
|
cnl: ["console", "server"],
|
|
|
});
|
|
@@ -65,23 +160,211 @@ async function handleLoginClick() {
|
|
|
|
|
|
void router.push({ name: "ChangePassword" });
|
|
|
}
|
|
|
+
|
|
|
+function closeApp() {
|
|
|
+ logger({
|
|
|
+ pgu: "AUTO",
|
|
|
+ cnl: ["local", "server"],
|
|
|
+ key: "退出应用",
|
|
|
+ act: "点击关闭按钮",
|
|
|
+ });
|
|
|
+ window.close();
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <div>login</div>
|
|
|
- <div>
|
|
|
- <n-form>
|
|
|
- <n-form-item label="学号">
|
|
|
- <n-input v-model:value="accountValue" clearable />
|
|
|
- </n-form-item>
|
|
|
- <n-form-item label="密码">
|
|
|
- <n-input v-model:value="password" clearable />
|
|
|
- </n-form-item>
|
|
|
- <n-form-item>
|
|
|
- <n-button @click="handleLoginClick"> 登录 </n-button>
|
|
|
- </n-form-item>
|
|
|
- </n-form>
|
|
|
+ <div class="tw-flex tw-flex-col tw-h-full">
|
|
|
+ <header class="header">
|
|
|
+ <div class="school-logo-container">
|
|
|
+ <img
|
|
|
+ v-show="logoPath"
|
|
|
+ class="school-logo"
|
|
|
+ :src="logoPath"
|
|
|
+ alt="school logo"
|
|
|
+ style="
|
|
|
+ background: linear-gradient(to bottom, #38f6f5 0%, #8efdf4 100%);
|
|
|
+ "
|
|
|
+ />
|
|
|
+ <!-- 加上它,在logo加载失败的时候有用 -->
|
|
|
+ <!-- @load="(e: any) => (e.target.style = '')" -->
|
|
|
+ </div>
|
|
|
+ <a class="close" @click="closeApp"> 关闭 </a>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <div class="center" :style="{ backgroundImage: backgroundUrl }">
|
|
|
+ <div class="content">
|
|
|
+ <div class="login-types qm-big-text tw-flex tw-overflow-clip">
|
|
|
+ <a
|
|
|
+ v-if="allowLoginType.includes('STUDENT_CODE')"
|
|
|
+ key="STUDENT_CODE"
|
|
|
+ :class="[formValue.accountType === 'STUDENT_CODE' && 'active-type']"
|
|
|
+ @click="formValue.accountType = 'STUDENT_CODE'"
|
|
|
+ >
|
|
|
+ {{ QECSConfig.STUDENT_CODE_LOGIN_ALIAS }}
|
|
|
+ </a>
|
|
|
+ <a
|
|
|
+ v-if="allowLoginType.includes('IDENTITY_NUMBER')"
|
|
|
+ key="IDENTITY_NUMBER"
|
|
|
+ :class="[formValue.accountType !== 'STUDENT_CODE' && 'active-type']"
|
|
|
+ @click="formValue.accountType = 'IDENTITY_NUMBER'"
|
|
|
+ >
|
|
|
+ {{ QECSConfig.IDENTITY_NUMBER_LOGIN_ALIAS }}
|
|
|
+ </a>
|
|
|
+ <a v-if="allowLoginType.length === 0" key="loading"> loading... </a>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="qm-title-text tw-text-center tw-mt-10">
|
|
|
+ {{ productName }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="tw-mx-10">
|
|
|
+ <n-form ref="formRef" :model="formValue" :rules="fromRules">
|
|
|
+ <n-form-item class="form-item-style" path="accountValue">
|
|
|
+ <n-input v-model:value="formValue.accountValue">
|
|
|
+ <template #prefix>
|
|
|
+ <n-icon :component="Person" />
|
|
|
+ </template>
|
|
|
+ </n-input>
|
|
|
+ </n-form-item>
|
|
|
+ <n-form-item
|
|
|
+ prop="password"
|
|
|
+ class="form-item-style"
|
|
|
+ path="password"
|
|
|
+ >
|
|
|
+ <n-input
|
|
|
+ v-model:value="formValue.password"
|
|
|
+ type="password"
|
|
|
+ @onEnter="loginForuser"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <n-icon :component="LockClosed" />
|
|
|
+ </template>
|
|
|
+ </n-input>
|
|
|
+ </n-form-item>
|
|
|
+
|
|
|
+ <n-form-item
|
|
|
+ v-if="isGeeTestEnabled"
|
|
|
+ class="form-item-style"
|
|
|
+ style="height: 40px; margin-top: 0px"
|
|
|
+ >
|
|
|
+ <!-- <GeeTest :reset="resetGeeTime" @onLoad="handleGtResult" /> -->
|
|
|
+ </n-form-item>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-show="errorInfo"
|
|
|
+ class="tw-flex tw-items-center tw-text-red-900"
|
|
|
+ >
|
|
|
+ <n-icon :component="CloseCircleOutline" size="large" />
|
|
|
+ {{ errorInfo }}
|
|
|
+ </div>
|
|
|
+ <n-form-item class="tw-mb-8">
|
|
|
+ <n-button
|
|
|
+ type="success"
|
|
|
+ size="large"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="disableLoginBtn"
|
|
|
+ :loading="loginBtnLoading"
|
|
|
+ @click="loginForuser"
|
|
|
+ >
|
|
|
+ {{ newVersionAvailable ? "点击更新版本" : "登录" }}
|
|
|
+ </n-button>
|
|
|
+ </n-form-item>
|
|
|
+ </n-form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <footer class="footer">
|
|
|
+ <div style="position: absolute; right: 20px; bottom: 20px">
|
|
|
+ 版本: {{ VITE_GIT_REPO_VERSION }}
|
|
|
+ </div>
|
|
|
+ </footer>
|
|
|
+ <!-- <GlobalNotice /> -->
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-<style scoped></style>
|
|
|
+<style scoped>
|
|
|
+.header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ min-height: 120px;
|
|
|
+}
|
|
|
+.school-logo-container {
|
|
|
+ justify-self: flex-start;
|
|
|
+ margin-left: 100px;
|
|
|
+}
|
|
|
+
|
|
|
+.school-logo {
|
|
|
+ height: 100px;
|
|
|
+ width: 400px;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+
|
|
|
+.close {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ right: 0;
|
|
|
+ background-color: #eeeeee;
|
|
|
+ color: #999999;
|
|
|
+ text-align: center;
|
|
|
+ width: 80px;
|
|
|
+ height: 40px;
|
|
|
+ line-height: 40px;
|
|
|
+ border-bottom-left-radius: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.close:hover {
|
|
|
+ color: #444444;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.center {
|
|
|
+ background-position: center;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-size: cover;
|
|
|
+ width: 100vw;
|
|
|
+ min-height: 600px;
|
|
|
+}
|
|
|
+
|
|
|
+.content {
|
|
|
+ margin-top: 100px;
|
|
|
+ margin-left: 60%;
|
|
|
+ width: 340px;
|
|
|
+ border-radius: 6px;
|
|
|
+ overflow: hidden;
|
|
|
+ background-color: white;
|
|
|
+}
|
|
|
+
|
|
|
+/* FIXME: 样式复用候选 */
|
|
|
+.qm-big-text {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: normal;
|
|
|
+ font-stretch: normal;
|
|
|
+ line-height: 22px;
|
|
|
+ letter-spacing: 0px;
|
|
|
+ color: #999999;
|
|
|
+}
|
|
|
+.qm-title-text {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ font-stretch: normal;
|
|
|
+ line-height: 24px;
|
|
|
+ letter-spacing: 0px;
|
|
|
+ color: #444444;
|
|
|
+}
|
|
|
+
|
|
|
+.login-types a {
|
|
|
+ flex: 1;
|
|
|
+ line-height: 40px;
|
|
|
+ background-color: #eeeeee;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+.login-types a.active-type {
|
|
|
+ background-color: #ffffff;
|
|
|
+}
|
|
|
+
|
|
|
+.form-item-style {
|
|
|
+ margin-bottom: 30px;
|
|
|
+ height: 42px;
|
|
|
+}
|
|
|
+</style>
|