Browse Source

修改密码和主页面

zhangjie 3 years ago
parent
commit
6c52186797

+ 2 - 0
package.json

@@ -23,6 +23,7 @@
     "moment": "^2.29.1",
     "naive-ui": "^2.26.0",
     "pinia": "^2.0.11",
+    "qs": "^6.10.3",
     "tailwindcss": "^3.0.23",
     "ua-parser-js": "^1.0.2",
     "vfonts": "^0.0.3",
@@ -32,6 +33,7 @@
   "devDependencies": {
     "@types/lodash-es": "^4.17.6",
     "@types/node": "^17.0.21",
+    "@types/qs": "^6.9.7",
     "@types/ua-parser-js": "^0.7.36",
     "@typescript-eslint/eslint-plugin": "^5.13.0",
     "@typescript-eslint/parser": "^5.13.0",

+ 10 - 0
src/api/changePwd.ts

@@ -0,0 +1,10 @@
+import { httpApp } from "@/plugins/axiosApp";
+import qs from "qs";
+
+
+/** 修改密码 */
+export async function changePwdApi(password: string, newPassword: string) {
+  return httpApp.put<any, { data: { content: any } }>(
+    "/api/ecs_core/student/password?" + qs.stringify({ password, newPassword }), {}
+  );
+}

+ 309 - 4
src/components/MainLayout/MainLayout.vue

@@ -1,8 +1,313 @@
-<script lang="ts" setup></script>
+<script lang="ts" setup>
+import { onMounted } from "vue";
+import menuData from "./menu.json";
+console.log(menuData);
+
+type MenuOption = {
+  id: string;
+  code: string;
+  name: string;
+  routeCode: string;
+};
+
+let curMenu = $ref("");
+let menuOptions = $ref<any[]>([]);
+
+const getMenus = () => {
+  menuOptions = menuData.map((item) => {
+    return { ...item };
+  });
+};
+const swithMenu = (menu: MenuOption) => {
+  // console.log(menu);
+  curMenu = menu.routeCode;
+  console.log(curMenu);
+};
+
+const breadcrumbs = $ref([
+  {
+    id: "1",
+    url: "1",
+    name: "考试",
+  },
+]);
+
+const toModifyPwd = () => {
+  console.log("logout");
+};
+const toLogout = () => {
+  console.log("logout");
+};
+
+onMounted(() => {
+  getMenus();
+});
+</script>
 
 <template>
-  <div>layout</div>
-  <div><router-view></router-view></div>
+  <div class="home">
+    <!-- header -->
+    <div class="home-header">
+      <div class="header-infos">
+        <div class="header-info">
+          <n-popover placement="bottom" trigger="hover">
+            <template #trigger>
+              <p class="header-info-cont">手机端登录(Android)</p>
+            </template>
+            <div class="qr-list">
+              <n-tabs type="segment">
+                <n-tab-pane name="android" tab="下载安卓Apk">
+                  <div class="qr-code">
+                    <img src="" alt="" />
+                  </div>
+                </n-tab-pane>
+                <n-tab-pane name="bindUser" tab="绑定用户">
+                  <div class="qr-code">
+                    <img src="" alt="" />
+                  </div>
+                </n-tab-pane>
+              </n-tabs>
+            </div>
+          </n-popover>
+        </div>
+        <div class="header-info">
+          <n-popover placement="bottom" trigger="hover">
+            <template #trigger>
+              <p class="header-info-cont">username</p>
+            </template>
+            <div class="user-info">
+              <div class="user-info-item">
+                <span>底照</span>
+                <img class="user-avatar" src="" alt="" />
+              </div>
+              <div class="user-info-item">
+                <span>学号</span>
+                <span>学号1212</span>
+              </div>
+              <div class="user-info-item">
+                <span>身份证号</span>
+                <span>身份证号11</span>
+              </div>
+              <div class="user-info-item">
+                <span>学习中心</span>
+                <span>学习中心11</span>
+              </div>
+              <div class="user-info-item">
+                <n-button type="success" block @click="toModifyPwd"
+                  >修改密码</n-button
+                >
+              </div>
+            </div>
+          </n-popover>
+        </div>
+        <div class="header-info">
+          <n-button type="error" text @click="toLogout">退出登录</n-button>
+        </div>
+      </div>
+    </div>
+    <!-- navs -->
+    <div class="home-navs">
+      <div class="home-logo">
+        <div class="home-logo-content">
+          <!-- <img v-if="schoolLogo" :src="schoolLogo" alt="知学知考" /> -->
+        </div>
+      </div>
+      <div class="home-menu">
+        <div v-for="menu in menuOptions" :key="menu.id" class="home-menu-item">
+          <div
+            :class="[
+              'home-menu-content',
+              { 'is-active': curMenu === menu.routeCode },
+            ]"
+            @click="swithMenu(menu)"
+          >
+            <span>{{ menu.name }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- body -->
+    <div class="home-body">
+      <div class="home-breadcrumb">
+        <span class="breadcrumb-tips">
+          <span>当前所在位置:</span>
+        </span>
+        <div class="breadcrumb-body">
+          <n-breadcrumb>
+            <n-breadcrumb-item v-for="bread in breadcrumbs" :key="bread.url">
+              {{ bread.name }}
+            </n-breadcrumb-item>
+          </n-breadcrumb>
+        </div>
+      </div>
+      <!-- main -->
+      <div class="home-main">
+        <router-view></router-view>
+      </div>
+    </div>
+  </div>
 </template>
 
-<style scoped></style>
+<style scoped>
+.home {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: auto;
+}
+/* home-header */
+.home-header {
+  position: absolute;
+  width: 100%;
+  height: 50px;
+  top: 0;
+  left: 0;
+  z-index: 99;
+  padding-left: 180px;
+  background-color: var(--app-color-white);
+  overflow: hidden;
+}
+.header-infos {
+  color: var(--app-color-text-light);
+  font-size: 0;
+  padding: 15px;
+  line-height: 20px;
+  text-align: right;
+}
+.header-info {
+  display: inline-block;
+  vertical-align: middle;
+  font-size: var(--app-font-size);
+  position: relative;
+  padding: 0 15px;
+  cursor: pointer;
+}
+.header-info-cont:hover {
+  color: var(--app-color-primary);
+}
+.header-info:not(:last-child)::after {
+  content: "";
+  display: block;
+  position: absolute;
+  height: 16px;
+  width: 0;
+  border-left: 1px solid var(--app-color-border-dark);
+  right: 0;
+  top: 50%;
+  margin-top: -8px;
+}
+.user-info {
+  width: 280px;
+}
+.user-info-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 10px 0;
+  border-bottom: 1px solid var(--app-color-border);
+}
+.user-avatar {
+  display: block;
+  width: 80px;
+  height: 80px;
+  box-shadow: 0 0 1px var(--app-color-border);
+}
+.qr-list {
+  width: 240px;
+}
+.qr-code {
+  width: 200px;
+  height: 200px;
+  margin: 0 auto;
+  padding: 10px 0;
+}
+.qr-code > img {
+  display: block;
+  width: 100%;
+  height: 100%;
+}
+
+/* home-navs */
+.home-navs {
+  position: absolute;
+  width: 180px;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  z-index: 100;
+  overflow: auto;
+  background-color: var(--app-color-header-bg);
+  border-top-right-radius: var(--app-border-radius-large);
+  border-bottom-right-radius: var(--app-border-radius-large);
+}
+.home-logo {
+  width: 80px;
+  height: 68px;
+  margin: 30px auto;
+  background-color: var(--app-color-white);
+  border-radius: var(--app-border-radius);
+}
+.home-menu {
+  color: var(--app-color-white);
+}
+.home-menu-item {
+  margin: 5px 0;
+  padding: 0 10px;
+}
+.home-menu-content {
+  padding: 10px;
+  border-radius: var(--app-border-radius);
+  line-height: 20px;
+  cursor: pointer;
+  position: relative;
+}
+.home-menu-content:hover {
+  color: var(--app-color-primary-light);
+}
+.home-menu-content.is-active {
+  background-color: var(--app-color-primary);
+  color: var(--app-color-white);
+}
+
+.home-menu-content::before {
+  content: "";
+  display: inline-block;
+  vertical-align: middle;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  border: 2px solid var(--app-color-white);
+  margin-right: 10px;
+}
+.home-menu-content > span {
+  display: inline-block;
+  vertical-align: middle;
+}
+
+/* home-body */
+.home-body {
+  position: absolute;
+  left: 0;
+  top: 50px;
+  right: 0;
+  bottom: 0;
+  overflow: auto;
+  background: var(--app-color-white);
+  z-index: 98;
+  padding-left: 180px;
+}
+.home-breadcrumb {
+  background-color: var(--app-color-body-bg);
+  padding: 10px 20px;
+  line-height: 30px;
+  color: var(--app-color-text-light);
+}
+.breadcrumb-body {
+  display: inline-block;
+}
+.home-main {
+  padding: 20px 20px 50px;
+}
+</style>

+ 140 - 0
src/components/MainLayout/menu.json

@@ -0,0 +1,140 @@
+[
+  {
+    "id": 6555,
+    "code": "stu_online_exam_0",
+    "name": "在线考试1343",
+    "parentId": null,
+    "groupId": 20,
+    "groupCode": "STUDENT_CLIENT_MENU_0",
+    "hasPrivilege": true,
+    "description": "在线考试3243",
+    "updateTime": "2020-05-27 16:18:35",
+    "creationTime": "2020-05-13 18:09:54",
+    "weight": 6,
+    "ext1": "ONLINE",
+    "ext2": null,
+    "ext3": null,
+    "ext4": null,
+    "ext5": null,
+    "routeCode": "stu_online_exam",
+    "nodeName": "在线考试1343",
+    "nodeCode": "stu_online_exam_0",
+    "parentNodeId": null,
+    "nodeId": "6555"
+  },
+  {
+    "id": 6556,
+    "code": "stu_online_homework_0",
+    "name": "在线作业",
+    "parentId": null,
+    "groupId": 20,
+    "groupCode": "STUDENT_CLIENT_MENU_0",
+    "hasPrivilege": true,
+    "description": "在线作业",
+    "updateTime": "2020-05-13 18:09:54",
+    "creationTime": "2020-05-13 18:09:54",
+    "weight": 5,
+    "ext1": null,
+    "ext2": null,
+    "ext3": null,
+    "ext4": null,
+    "ext5": null,
+    "routeCode": "stu_online_homework",
+    "nodeName": "在线作业",
+    "nodeCode": "stu_online_homework_0",
+    "parentNodeId": null,
+    "nodeId": "6556"
+  },
+  {
+    "id": 6557,
+    "code": "stu_online_practice_0",
+    "name": "在线练习3",
+    "parentId": null,
+    "groupId": 20,
+    "groupCode": "STUDENT_CLIENT_MENU_0",
+    "hasPrivilege": true,
+    "description": "在线练习",
+    "updateTime": "2020-05-27 16:18:42",
+    "creationTime": "2020-05-13 18:09:54",
+    "weight": 4,
+    "ext1": null,
+    "ext2": null,
+    "ext3": null,
+    "ext4": null,
+    "ext5": null,
+    "routeCode": "stu_online_practice",
+    "nodeName": "在线练习3",
+    "nodeCode": "stu_online_practice_0",
+    "parentNodeId": null,
+    "nodeId": "6557"
+  },
+  {
+    "id": 6558,
+    "code": "stu_offline_exam_0",
+    "name": "离线考试",
+    "parentId": null,
+    "groupId": 20,
+    "groupCode": "STUDENT_CLIENT_MENU_0",
+    "hasPrivilege": true,
+    "description": "离线考试",
+    "updateTime": "2020-05-13 18:09:54",
+    "creationTime": "2020-05-13 18:09:54",
+    "weight": 3,
+    "ext1": null,
+    "ext2": null,
+    "ext3": null,
+    "ext4": null,
+    "ext5": null,
+    "routeCode": "stu_offline_exam",
+    "nodeName": "离线考试",
+    "nodeCode": "stu_offline_exam_0",
+    "parentNodeId": null,
+    "nodeId": "6558"
+  },
+  {
+    "id": 6559,
+    "code": "stu_notice_0",
+    "name": "公告通知",
+    "parentId": null,
+    "groupId": 20,
+    "groupCode": "STUDENT_CLIENT_MENU_0",
+    "hasPrivilege": true,
+    "description": "公告通知",
+    "updateTime": "2020-05-13 18:09:54",
+    "creationTime": "2020-05-13 18:09:54",
+    "weight": 2,
+    "ext1": null,
+    "ext2": null,
+    "ext3": null,
+    "ext4": null,
+    "ext5": null,
+    "routeCode": "stu_notice",
+    "nodeName": "公告通知",
+    "nodeCode": "stu_notice_0",
+    "parentNodeId": null,
+    "nodeId": "6559"
+  },
+  {
+    "id": 6560,
+    "code": "stu_modify_pwd_0",
+    "name": "修改密码",
+    "parentId": null,
+    "groupId": 20,
+    "groupCode": "STUDENT_CLIENT_MENU_0",
+    "hasPrivilege": true,
+    "description": "修改密码",
+    "updateTime": "2020-05-13 18:09:54",
+    "creationTime": "2020-05-13 18:09:54",
+    "weight": 1,
+    "ext1": null,
+    "ext2": null,
+    "ext3": null,
+    "ext4": null,
+    "ext5": null,
+    "routeCode": "stu_modify_pwd",
+    "nodeName": "修改密码",
+    "nodeCode": "stu_modify_pwd_0",
+    "parentNodeId": null,
+    "nodeId": "6560"
+  }
+]

+ 105 - 3
src/features/ChangePassword/ChangePassword.vue

@@ -1,7 +1,109 @@
-<script lang="ts" setup></script>
+<script lang="ts" setup>
+import { FormInst, FormItemRule, FormRules, useMessage } from "naive-ui";
+import { changePwdApi } from "@/api/changePwd";
+const modelFormRef = $ref<FormInst | null>(null);
+const message = useMessage();
+let loading: boolean = $ref(false);
+
+type FormModel = {
+  password: string;
+  newPassword: string;
+  renewPassword: string;
+};
+let formModel: FormModel = $ref({
+  password: "",
+  newPassword: "",
+  renewPassword: "",
+});
+const password = {
+  required: true,
+  pattern: /^[a-zA-Z0-9]{6,18}$/,
+  message: "密码只能由数字、字母组成,长度6-18个字符",
+  trigger: "input",
+};
+const equalToOldPswd = (rule: FormItemRule, value: string): Error | boolean => {
+  if (value && formModel.password && value === formModel.password) {
+    return new Error("新旧密码不可以相同");
+  }
+  return true;
+};
+const equalToPswd = (rule: FormItemRule, value: string): Error | boolean => {
+  if (value && formModel.newPassword && value !== formModel.newPassword) {
+    return new Error("两次输入的密码不一致");
+  }
+  return true;
+};
+const formRules: FormRules = {
+  password: [password],
+  newPassword: [
+    password,
+    {
+      validator: equalToOldPswd,
+      trigger: "input",
+    },
+  ],
+  renewPassword: [password, { validator: equalToPswd, trigger: "input" }],
+};
+const inputChange = async () => {
+  await modelFormRef?.validate();
+};
+
+const toSubmit = async () => {
+  await modelFormRef?.validate();
+
+  if (loading) return;
+  loading = true;
+  const res = await changePwdApi(formModel.password, formModel.newPassword);
+  console.log(res.data);
+  message.success("修改成功!");
+};
+</script>
 
 <template>
-  <div>change pwd</div>
+  <div class="pwd-box">
+    <n-form
+      ref="modelFormRef"
+      :model="formModel"
+      :rules="formRules"
+      :showLabel="false"
+      size="medium"
+    >
+      <n-form-item label="旧秘密" path="password">
+        <n-input
+          v-model:value="formModel.password"
+          placeholder="请输入旧秘密"
+          @change="inputChange"
+        />
+      </n-form-item>
+      <n-form-item label="新密码" path="newPassword">
+        <n-input
+          v-model:value="formModel.newPassword"
+          placeholder="请输入新密码(6到18位的数字或字母)"
+          @change="inputChange"
+        />
+      </n-form-item>
+      <n-form-item label="新密码" path="renewPassword">
+        <n-input
+          v-model:value="formModel.renewPassword"
+          placeholder="请再次输入新密码"
+          @change="inputChange"
+        />
+      </n-form-item>
+      <n-form-item>
+        <n-button type="primary" :disabled="loading" @click="toSubmit">
+          保存
+        </n-button>
+      </n-form-item>
+    </n-form>
+  </div>
 </template>
 
-<style scoped></style>
+<style scoped>
+.pwd-box {
+  padding: 60px 40px;
+  border: 1px solid var(--app-color-border);
+  border-radius: var(--app-border-radius);
+  max-width: 600px;
+  /* margin: 0 auto; */
+}
+</style>

+ 5 - 0
src/main.ts

@@ -13,4 +13,9 @@ app.use(createPinia());
 app.config.globalProperties.$filters = filters;
 app.config.errorHandler = vueErrorHandler;
 
+/** 解决tailwind样式覆盖问题 */
+const meta = document.createElement('meta');
+meta.name = 'naive-ui-style';
+document.head.appendChild(meta);
+
 app.mount("#app");

+ 34 - 12
src/styles/cssvar.css

@@ -1,15 +1,37 @@
 :root {
-  --header-bg-color: #191b37;
-  --app-container-bg-color: white;
-  --app-main-bg-color: #edf2fa;
-  --app-main-text-color: #283e76;
+  --app-color-header-bg: #43434d;
+  --app-color-body-bg: #eff0f5;
+  --app-color-border: #eff0f5;
+  --app-color-border-dark: #d3d5e0;
+
+  --app-color-text-main: #1f2225;
+  --app-color-text-sub: #333639;
+  --app-color-text-light: #8b8fa1;
+
+  --app-color-primary: #18a058;
+  --app-color-primary-light: #46b379;
+  --app-color-success: #18a058;
+  --app-color-success-light: #46b379;
+  --app-color-warning: #f0a020;
+  --app-color-warning-light: #f3b34d;
+  --app-color-danger: #d03050;
+  --app-color-danger-light: #d95973;
+  --app-color-info: #767c82;
+  --app-color-white: #fff;
+
+  --app-font-weight: 400;
+  --app-font-weight-bold: 600;
+
+  --app-border-radius: 5px;
+  --app-border-radius-large: 10px;
+  --app-border-radius-huge: 20px;
+
+  --app-font-size: 14px;
+  --app-font-size-tiny: 12px;
+  --app-font-size-large: 16px;
+
   --app-min-width: 1280px;
-  --app-bold-text-color: #435488;
-  --app-small-header-text-color: #7584ac;
-  --app-primary-button-bg-color: #5d65ff;
-  --app-undo-button-bg-color: #4ed885;
-
-  --app-main-font-size: 14px;
-  --app-secondary-font-size: 12px;
-  --app-title-font-size: 16px;
+
+  --app-font-family: "Helvetica Neue", Helvetica, "PingFang SC",
+    "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
 }

+ 11 - 8
src/styles/global.css

@@ -3,29 +3,32 @@
 @import "./cssvar.css";
 
 body {
-  margin: 0;
-  font-size: var(--app-main-font-size);
+  font-size: var(--app-font-size);
+  color: var(--app-color-text-main);
   min-width: var(--app-min-width);
   min-height: 600px;
+  font-family: var(--app-font-family);
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
   user-select: none;
 }
 
 .main-text-color {
-  color: var(--app-main-text-color);
+  color: var(--app-color-text-main);
 }
 
 .secondary-text {
-  color: var(--app-small-header-text-color);
-  font-size: var(--app-secondary-font-size);
+  color: var(--app-color-text-sub);
+  font-size: var(--app-font-size-tiny);
 }
 
 /* for MarkHeader */
 .header-small-text {
-  font-size: var(--app-secondary-font-size);
+  font-size: var(--app-font-size-tiny);
 }
 .highlight-text {
   color: white;
-  font-size: var(--app-title-font-size);
+  font-size: var(--app-font-size);
 }
 
 .score-number-animation-enter-active,
@@ -56,5 +59,5 @@ button.ant-btn span[role="img"] {
 }
 
 button.ant-btn-primary {
-  background-color: var(--app-primary-button-bg-color);
+  background-color: var(--app-color-primary);
 }

+ 1 - 1
src/types/global.d.ts

@@ -3,7 +3,7 @@
 
 import { MessageApiInjection } from "naive-ui/lib/message/src/MessageProvider";
 
-export {};
+export { };
 // declare module "@vue/runtime-core" {
 // interface ComponentCustomProperties {
 // $filters: typeof filters;