Jelajahi Sumber

用户管理、机构管理、批次列表

Michael Wang 4 tahun lalu
induk
melakukan
a6fbf95d2b

+ 22 - 0
src/api/exam.js

@@ -0,0 +1,22 @@
+import { httpApp } from "@/plugins/axiosIndex";
+import { pickBy } from "lodash-es";
+import { object2QueryString } from "@/utils/utils";
+
+export function searchExams({
+  role,
+  loginName = "",
+  name = "",
+  enable = "",
+  pageNumber = 1,
+  pageSize = 10,
+}) {
+  const data = pickBy(
+    { role, loginName, name, enable, pageNumber, pageSize },
+    (v) => v !== ""
+  );
+  return httpApp.post("/api/admin/exam/query?" + object2QueryString(data));
+}
+
+export function toggleEnableExam({ id, enable }) {
+  return httpApp.post("/api/admin/exam/toggle", { id, enable });
+}

+ 2 - 25
src/api/login.js

@@ -1,33 +1,10 @@
 import { httpApp } from "@/plugins/axiosIndex";
-import { object2QueryString } from "@/utils/utils";
-
-const CryptoJS = require("crypto-js");
-
-const AES = (content) => {
-  const KEY = "1234567890123456";
-  const IV = "1234567890123456";
-  // const key = CryptoJS.enc.Utf8.parse("1234567890123456");
-  // console.log(key);
-  // const key = "1234567890123456";
-  console.log(content);
-
-  var key = CryptoJS.enc.Utf8.parse(KEY);
-  var iv = CryptoJS.enc.Utf8.parse(IV);
-  var encrypted = CryptoJS.AES.encrypt(content, key, { iv: iv });
-  return encrypted.toString();
-
-  // const enstr = CryptoJS.AES.encrypt(content + "", key, {
-  //   // mode: CryptoJS.mode.ECB,
-  //   // padding: CryptoJS.pad.Pkcs7,
-  // }).toString();
-
-  // return enstr;
-};
+import { object2QueryString, AESString } from "@/utils/utils";
 
 export function loginByUsername({ loginName, password, code }) {
   const data = {
     loginName,
-    password: AES(password),
+    password: AESString(password),
     code,
   };
   return httpApp.post("/api/admin/user/login/account", data, {

+ 10 - 0
src/api/system-info.js

@@ -0,0 +1,10 @@
+import { httpApp } from "@/plugins/axiosIndex";
+import { object2QueryString } from "@/utils/utils";
+
+export function uploadFile({ file }) {
+  return httpApp.post(
+    "/api/admin/user/query?" + object2QueryString({ type: "frontend" }),
+    { file: file },
+    { headers: { "Content-Type": "multipart/form-data" } }
+  );
+}

+ 15 - 6
src/api/system-user.js

@@ -1,8 +1,9 @@
 import { httpApp } from "@/plugins/axiosIndex";
 import { pickBy } from "lodash-es";
-import { object2QueryString } from "@/utils/utils";
+import { object2QueryString, AESString } from "@/utils/utils";
 
 export function searchUsers({
+  orgId = "",
   role,
   loginName = "",
   name = "",
@@ -11,14 +12,16 @@ export function searchUsers({
   pageSize = 10,
 }) {
   const data = pickBy(
-    { role, loginName, name, enable, pageNumber, pageSize },
+    { orgId, role, loginName, name, enable, pageNumber, pageSize },
     (v) => v !== ""
   );
   return httpApp.post("/api/admin/user/query?" + object2QueryString(data));
 }
 
 export function saveUser({
-  roles,
+  orgId = "",
+  id = "",
+  roleCode,
   loginName = "",
   name = "",
   enable = "",
@@ -26,10 +29,13 @@ export function saveUser({
   mobileNumber = "",
 }) {
   const data = pickBy(
-    { roles, loginName, name, enable, password, mobileNumber },
+    { orgId, id, roleCode, loginName, name, enable, password, mobileNumber },
     (v) => v !== ""
   );
-  return httpApp.post("/api/admin/user/save", data);
+  return httpApp.post("/api/admin/user/save", {
+    ...data,
+    ...(password.length > 0 ? { password: AESString(password) } : {}),
+  });
 }
 
 export function toggleEnableUser({ id, enable }) {
@@ -37,5 +43,8 @@ export function toggleEnableUser({ id, enable }) {
 }
 
 export function resetUserPassword({ id, password }) {
-  return httpApp.post("/api/admin/user/updatePwd", { id, password });
+  return httpApp.post("/api/admin/user/updatePwd", {
+    id,
+    password: AESString(password),
+  });
 }

+ 56 - 0
src/components/ExamTypeSelect.vue

@@ -0,0 +1,56 @@
+<template>
+  <el-select
+    v-model="selected"
+    class="size-select"
+    placeholder="请选择"
+    @change="select"
+    style="width: 100px;"
+  >
+    <el-option
+      v-for="item in optionList"
+      :key="item.code"
+      :label="item.name"
+      :value="item.code"
+    >
+      <span>{{ item.name }}</span>
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+export default {
+  name: "ExamTypeSelect",
+  props: {
+    value: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      optionList: [
+        { code: "ANYTIME", name: "随到随考" },
+        { code: "TOGETHER", name: "集中统一" },
+      ],
+      selected: "",
+    };
+  },
+  async created() {},
+  watch: {
+    value: {
+      immediate: true,
+      handler(val) {
+        this.selected = val;
+      },
+    },
+  },
+  methods: {
+    select() {
+      this.$emit("input", this.selected);
+      this.$emit("change", this.selected);
+    },
+  },
+};
+</script>
+
+<style></style>

+ 54 - 0
src/components/OrgSelect.vue

@@ -0,0 +1,54 @@
+<template>
+  <el-select
+    v-model="selected"
+    class="size-select"
+    placeholder="请选择"
+    @change="select"
+    style="width: 100px;"
+  >
+    <el-option
+      v-for="item in optionList"
+      :key="item.id"
+      :label="item.name"
+      :value="item.id"
+    >
+      <span>{{ item.name }}</span>
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+export default {
+  name: "OrgSelect",
+  props: {
+    value: [String, Array],
+  },
+  data() {
+    return {
+      optionList: [],
+      selected: "",
+    };
+  },
+  async created() {
+    const res = await this.$http.post("/api/admin/sys/org/query");
+    // console.log(res.data);
+    this.optionList = res.data.data.records;
+  },
+  watch: {
+    value: {
+      immediate: true,
+      handler(val) {
+        this.selected = val;
+      },
+    },
+  },
+  methods: {
+    select() {
+      this.$emit("input", this.selected);
+      this.$emit("change", this.selected);
+    },
+  },
+};
+</script>
+
+<style></style>

+ 4 - 4
src/components/StateSelect.vue

@@ -22,15 +22,15 @@ export default {
   name: "StateSelect",
   props: {
     value: {
-      type: String,
-      default: "",
+      type: Number,
+      default: 1,
     },
   },
   data() {
     return {
       optionList: [
-        { code: "0", name: "禁用" },
-        { code: "1", name: "启用" },
+        { code: 0, name: "禁用" },
+        { code: 1, name: "启用" },
       ],
       selected: "",
     };

+ 157 - 0
src/features/examwork/ExamManagement/ExamManagement.vue

@@ -0,0 +1,157 @@
+<template>
+  <div>
+    <el-form :model="form" inline>
+      <el-form-item label="批次名称">
+        <el-input v-model.trim="form.name"></el-input>
+      </el-form-item>
+      <el-form-item label="类型">
+        <ExamTypeSelect v-model="form.mode"></ExamTypeSelect>
+      </el-form-item>
+      <el-form-item label="状态">
+        <StateSelect v-model="form.enableState"></StateSelect>
+      </el-form-item>
+      <el-button @click="searchForm">查询</el-button>
+      <el-button @click="add">新增</el-button>
+    </el-form>
+
+    <el-table :data="tableData" stripe style="width: 100%;">
+      <el-table-column type="selection" width="40" />
+      <el-table-column width="55" label="ID">
+        <span slot-scope="scope">{{ scope.row.id }}</span>
+      </el-table-column>
+      <el-table-column width="200" label="批次名称">
+        <span slot-scope="scope">{{ scope.row.name }}</span>
+      </el-table-column>
+      <el-table-column label="模式">
+        <span slot-scope="scope">{{ scope.row.mode }}</span>
+      </el-table-column>
+      <el-table-column width="120" label="状态">
+        <span slot-scope="scope">{{
+          scope.row.enable | zeroOneEnableDisableFilter
+        }}</span>
+      </el-table-column>
+      <el-table-column width="100" label="开始时间">
+        <span slot-scope="scope">{{
+          scope.row.startTime | datetimeFilter
+        }}</span>
+      </el-table-column>
+      <el-table-column width="100" label="结束时间">
+        <span slot-scope="scope">{{ scope.row.endTime | datetimeFilter }}</span>
+      </el-table-column>
+      <el-table-column width="100" label="算分进度">
+        <span slot-scope="scope">{{ scope.row.scoreStatus }}</span>
+      </el-table-column>
+      <el-table-column width="120" label="更新人">
+        <span slot-scope="scope">{{ scope.row.updateName }}</span>
+      </el-table-column>
+      <el-table-column width="100" label="更新时间">
+        <span slot-scope="scope">{{
+          scope.row.updateTime | datetimeFilter
+        }}</span>
+      </el-table-column>
+      <el-table-column :context="_self" label="操作" width="210">
+        <div slot-scope="scope">
+          <el-button size="mini" type="primary" plain @click="edit(scope.row)">
+            编辑
+          </el-button>
+          <el-button
+            size="mini"
+            type="primary"
+            plain
+            @click="resetUserPassword(scope.row)"
+          >
+            重置密码
+          </el-button>
+          <el-button
+            size="mini"
+            type="primary"
+            plain
+            @click="toggleEnableExam(scope.row)"
+          >
+            {{ scope.row.enable ? "禁用" : "启用" }}
+          </el-button>
+        </div>
+      </el-table-column>
+    </el-table>
+    <div class="page float-right">
+      <el-pagination
+        @current-change="handleCurrentChange"
+        :current-page="currentPage"
+        :page-size="pageSize"
+        :page-sizes="[10, 20, 50, 100, 200, 300]"
+        @size-change="handleSizeChange"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="total"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import StateSelect from "@/components/StateSelect";
+import ExamTypeSelect from "@/components/ExamTypeSelect";
+import { searchExams, toggleEnableExam } from "@/api/exam";
+
+export default {
+  name: "ExamManagement",
+  components: {
+    StateSelect,
+    ExamTypeSelect,
+  },
+  data() {
+    return {
+      form: {
+        orgId: "",
+        roleCode: "",
+        loginName: "",
+        name: "",
+        enableState: null,
+      },
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      selectedUser: {},
+    };
+  },
+  async created() {},
+  methods: {
+    async searchForm() {
+      const res = await searchExams({
+        enable: this.form.enableState,
+        name: this.form.name,
+        pageNumber: this.currentPage,
+        pageSize: this.pageSize,
+      });
+      this.tableData = res.data.data.records.records;
+      this.total = res.data.data.records.total;
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchForm();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchForm();
+    },
+    add() {
+      this.selectedUser = {};
+      this.$refs.userDialog.openDialog();
+    },
+    edit(user) {
+      this.selectedUser = user;
+      this.$refs.userDialog.openDialog();
+    },
+    async toggleEnableExam(user) {
+      await toggleEnableExam({
+        id: user.id,
+        enable: user.enable === 0 ? 1 : 0,
+      });
+      this.searchForm();
+    },
+  },
+};
+</script>
+
+<style></style>

+ 1 - 1
src/features/system/OrgManagement/OrgManagement.vue

@@ -12,7 +12,7 @@
       </el-form-item>
       <el-button @click="searchForm">查询</el-button>
       <el-button>新增</el-button>
-      <el-button>导入</el-button>
+      <!-- <el-button>导入</el-button> -->
     </el-form>
 
     <el-table :data="tableData" stripe style="width: 100%;">

+ 74 - 17
src/features/system/UserManagement/UserManagement.vue

@@ -1,6 +1,9 @@
 <template>
   <div>
     <el-form :model="form" inline>
+      <!-- <el-form-item v-if="$store.state.user.orgId === null" label="机构">
+        <OrgSelect v-model="form.orgId"></OrgSelect>
+      </el-form-item> -->
       <el-form-item label="角色">
         <RoleSelect v-model="form.roleCode"></RoleSelect>
       </el-form-item>
@@ -14,8 +17,8 @@
         <StateSelect v-model="form.enableState"></StateSelect>
       </el-form-item>
       <el-button @click="searchForm">查询</el-button>
-      <el-button>新增</el-button>
-      <el-button>导入</el-button>
+      <el-button @click="add">新增</el-button>
+      <!-- <el-button>导入</el-button> -->
     </el-form>
 
     <el-table :data="tableData" stripe style="width: 100%;">
@@ -37,7 +40,7 @@
         <span slot-scope="scope">{{ scope.row.loginName }}</span>
       </el-table-column>
       <el-table-column width="100" label="角色">
-        <span slot-scope="scope">{{ scope.row.roleName }}</span>
+        <span slot-scope="scope">{{ scope.row.roleNameStr }}</span>
       </el-table-column>
       <el-table-column width="120" label="状态">
         <span slot-scope="scope">{{
@@ -52,7 +55,23 @@
       <el-table-column :context="_self" label="操作" width="210">
         <div slot-scope="scope">
           <el-button size="mini" type="primary" plain @click="edit(scope.row)">
-            <i class="el-icon-edit"></i> 编辑
+            编辑
+          </el-button>
+          <el-button
+            size="mini"
+            type="primary"
+            plain
+            @click="resetUserPassword(scope.row)"
+          >
+            重置密码
+          </el-button>
+          <el-button
+            size="mini"
+            type="primary"
+            plain
+            @click="toggleEnableUser(scope.row)"
+          >
+            {{ scope.row.enable ? "禁用" : "启用" }}
           </el-button>
         </div>
       </el-table-column>
@@ -68,48 +87,64 @@
         :total="total"
       />
     </div>
+
+    <UserManagementDialog
+      ref="userDialog"
+      :user="selectedUser"
+      @reload="searchForm"
+    />
   </div>
 </template>
 
 <script>
 import RoleSelect from "@/components/RoleSelect.vue";
 import StateSelect from "@/components/StateSelect";
-import { searchUsers } from "../../../api/system-user";
+// import OrgSelect from "@/components/OrgSelect";
+import {
+  searchUsers,
+  toggleEnableUser,
+  resetUserPassword,
+} from "../../../api/system-user";
+import UserManagementDialog from "./UserManagementDialog";
 
 export default {
   name: "UserManagement",
   components: {
     RoleSelect,
     StateSelect,
+    // OrgSelect,
+    UserManagementDialog,
   },
   data() {
     return {
       form: {
-        orgList: [],
+        orgId: "",
         roleCode: "",
         loginName: "",
         name: "",
-        enableState: "",
+        enableState: null,
       },
       tableData: [],
       currentPage: 1,
       pageSize: 10,
       total: 10,
+      selectedUser: {},
     };
   },
   async created() {},
   methods: {
     async searchForm() {
-      this.tableData = (
-        await searchUsers({
-          role: this.form.roleCode,
-          enable: this.form.enableState,
-          loginName: this.form.loginName,
-          name: this.form.name,
-          pageNumber: this.currentPage,
-          pageSize: this.pageSize,
-        })
-      ).data.data.records.records;
+      const res = await searchUsers({
+        orgId: this.form.orgId,
+        role: this.form.roleCode,
+        enable: this.form.enableState,
+        loginName: this.form.loginName,
+        name: this.form.name,
+        pageNumber: this.currentPage,
+        pageSize: this.pageSize,
+      });
+      this.tableData = res.data.data.records.records;
+      this.total = res.data.data.records.total;
     },
     handleCurrentChange(val) {
       this.currentPage = val;
@@ -120,6 +155,28 @@ export default {
       this.currentPage = 1;
       this.searchForm();
     },
+    add() {
+      this.selectedUser = {};
+      this.$refs.userDialog.openDialog();
+    },
+    edit(user) {
+      this.selectedUser = user;
+      this.$refs.userDialog.openDialog();
+    },
+    async toggleEnableUser(user) {
+      await toggleEnableUser({
+        id: user.id,
+        enable: user.enable === 0 ? 1 : 0,
+      });
+      this.searchForm();
+    },
+    async resetUserPassword(user) {
+      await resetUserPassword({
+        id: user.id,
+        password: "123456",
+      });
+      this.searchForm();
+    },
   },
 };
 </script>

+ 137 - 0
src/features/system/UserManagement/UserManagementDialog.vue

@@ -0,0 +1,137 @@
+<template>
+  <el-dialog
+    ref="dialog"
+    :title="(isEdit ? '编辑' : '新增') + '用户'"
+    width="450px"
+    :visible.sync="visible"
+    @close="closeDialog"
+  >
+    <el-form
+      :model="form"
+      ref="form"
+      :rules="rules"
+      label-position="right"
+      label-width="120px"
+    >
+      <el-row>
+        <el-form-item v-if="$store.state.user.orgId === null" label="机构">
+          <OrgSelect v-model="form.orgId"></OrgSelect>
+        </el-form-item>
+      </el-row>
+      <el-row>
+        <el-form-item label="登录名" prop="loginName">
+          <el-input
+            class="pull_length"
+            v-model="form.loginName"
+            placeholder="登录名"
+          />
+        </el-form-item>
+      </el-row>
+      <el-row>
+        <el-form-item label="姓名" prop="name">
+          <el-input
+            class="pull_length"
+            v-model="form.name"
+            placeholder="姓名"
+          />
+        </el-form-item>
+      </el-row>
+      <el-row>
+        <el-form-item label="密码" prop="password">
+          <el-input
+            class="pull_length"
+            v-model="form.password"
+            placeholder="密码"
+          />
+        </el-form-item>
+      </el-row>
+      <el-row>
+        <el-form-item label="角色" prop="roleCode">
+          <RoleSelect v-model="form.roleCode" multiple />
+        </el-form-item>
+      </el-row>
+      <el-row>
+        <el-form-item label="手机号" prop="mobileNumber">
+          <el-input
+            class="pull_length"
+            v-model="form.mobileNumber"
+            placeholder="联系方式"
+          />
+        </el-form-item>
+      </el-row>
+      <el-row>
+        <el-form-item label="状态" prop="enable">
+          <el-radio-group class="pull_right_sm" v-model="form.enable">
+            <el-radio :label="1">启用</el-radio>
+            <el-radio :label="0">禁用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-row>
+      <el-row class="d-flex justify-content-center">
+        <el-button type="primary" @click="submitForm">保 存</el-button>
+        <el-button @click="closeDialog">取 消</el-button>
+      </el-row>
+    </el-form>
+  </el-dialog>
+</template>
+
+<script>
+import RoleSelect from "@/components/RoleSelect";
+import { saveUser } from "@/api/system-user";
+import OrgSelect from "@/components/OrgSelect";
+export default {
+  name: "UserManagementDialog",
+  components: { RoleSelect, OrgSelect },
+  props: {
+    user: Object,
+  },
+  computed: {
+    isEdit() {
+      return this.user.id;
+    },
+  },
+  data() {
+    return {
+      visible: false,
+      form: {},
+      rules: {},
+    };
+  },
+  watch: {
+    user(val) {
+      let tmp = { ...val };
+      if (!tmp.id) {
+        tmp = {
+          orgId: this.$store.state.user.orgId,
+          name: "",
+          loginName: "",
+          password: "",
+          mobileNumber: "",
+          roleCode: [],
+          enable: 1,
+        };
+      }
+      this.form = tmp;
+    },
+  },
+  methods: {
+    openDialog() {
+      this.visible = true;
+    },
+    closeDialog() {
+      this.visible = false;
+    },
+    async submitForm() {
+      let data = this.form;
+      if (this.isEdit) {
+        data = { ...data, id: this.user.id };
+      }
+      await saveUser(data);
+      this.$emit("reload");
+      this.closeDialog();
+    },
+  },
+};
+</script>
+
+<style></style>

+ 6 - 0
src/filters/index.js

@@ -1,4 +1,5 @@
 import Vue from "vue";
+import { dateFormatForAPI } from "@/utils/utils";
 
 Vue.filter("booleanYesNoFilter", function (val) {
   if (val === null) return "无";
@@ -29,3 +30,8 @@ Vue.filter("zeroOnePassFilter", function (val) {
   if (val === null) return "无";
   return { 1: "通过", 0: "不通过" }[val];
 });
+
+Vue.filter("datetimeFilter", function (val) {
+  if (val === null) return "";
+  return dateFormatForAPI(val);
+});

+ 23 - 0
src/router/index.js

@@ -69,6 +69,29 @@ const routes = [
       },
     ],
   },
+  {
+    path: "/exam",
+    name: "Exam",
+    component: Layout,
+    children: [
+      {
+        path: "exam",
+        name: "ExamManagement",
+        component: () =>
+          import(
+            /* webpackChunkName: "system" */ "../features/examwork/ExamManagement/ExamManagement.vue"
+          ),
+      },
+      // {
+      //   path: "org",
+      //   name: "OrgManagement",
+      //   component: () =>
+      //     import(
+      //       /* webpackChunkName: "system" */ "../features/examwork/OrgManagement/OrgManagement.vue"
+      //     ),
+      // },
+    ],
+  },
   {
     path: "/login",
     name: "Login",

+ 13 - 0
src/utils/utils.js

@@ -24,6 +24,19 @@ export function errorLog(message, { stack = "", code = "" }) {
   ]);
 }
 
+const CryptoJS = require("crypto-js");
+
+export function AESString(content) {
+  const KEY = "1234567890123456";
+  const IV = "1234567890123456";
+  // console.log(content);
+
+  var key = CryptoJS.enc.Utf8.parse(KEY);
+  var iv = CryptoJS.enc.Utf8.parse(IV);
+  var encrypted = CryptoJS.AES.encrypt(content, key, { iv: iv });
+  return encrypted.toString();
+}
+
 export function object2QueryString(obj) {
   return queryString.stringify(obj);
 }

+ 2 - 2
src/views/Layout/components/menu.js

@@ -41,12 +41,12 @@ const systemMenuConfig = [
 const businessMenuConfig = [
   {
     title: "考务管理",
-    name: "Basiness",
+    name: "Exam",
     icon: "icon-business",
     children: [
       {
         title: "批次管理",
-        name: "Base",
+        name: "ExamManagement",
       },
       {
         title: "考生管理",