Browse Source

feat: 答题管理模块页面初始化及接口联调;题目列表及已选题目组件封装

mnisting 4 months ago
parent
commit
a1634e9d05

+ 44 - 0
src/api/exerciseManage/category/index.js

@@ -0,0 +1,44 @@
+import request from "@/utils/request";
+
+// 查询列表
+export function getList(query) {
+  return request({
+    url: "/question/category/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 查询详情  /question/category/{id}
+export function getDetail(id) {
+  return request({
+    url: `/question/category/${id}`,
+    method: "get",
+  });
+}
+
+// 新增  /question/category
+export function add(data) {
+  return request({
+    url: "/question/category",
+    method: "post",
+    data,
+  });
+}
+
+// 修改
+export function update(data) {
+  return request({
+    url: "/question/category",
+    method: "put",
+    data,
+  });
+}
+
+// 删除
+export function del(ids) {
+  return request({
+    url: `/question/category/${ids}`,
+    method: "delete",
+  });
+}

+ 62 - 0
src/api/exerciseManage/question/index.js

@@ -0,0 +1,62 @@
+import request from "@/utils/request";
+
+// 查询列表
+export function getList(query) {
+  return request({
+    url: "/question/info/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 根据id查询列表
+export function getListByIds(query) {
+  return request({
+    url: "/question/info/listByIds",
+    method: "get",
+    params: query,
+  });
+}
+
+// 查询详情  /question/category/{id}
+export function getDetail(id) {
+  return request({
+    url: `/question/info/${id}`,
+    method: "get",
+  });
+}
+
+// 新增  /question/category
+export function add(data) {
+  return request({
+    url: "/question/info",
+    method: "post",
+    data,
+  });
+}
+
+// 修改
+export function update(data) {
+  return request({
+    url: "/question/info",
+    method: "put",
+    data,
+  });
+}
+
+// 删除
+export function del(ids) {
+  return request({
+    url: `/question/info/${ids}`,
+    method: "delete",
+  });
+}
+
+// 导出
+export function exportData(data) {
+  return request({
+    url: `/question/info/export`,
+    method: "post",
+    data,
+  });
+}

+ 44 - 0
src/api/exerciseManage/specialManage/index.js

@@ -0,0 +1,44 @@
+import request from "@/utils/request";
+
+// 查询列表  /question/special/list
+export function getList(query) {
+  return request({
+    url: "/question/special/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 查询详情
+export function getDetail(id) {
+  return request({
+    url: `/question/special/${id}`,
+    method: "get",
+  });
+}
+
+// 新增
+export function add(data) {
+  return request({
+    url: "/question/special",
+    method: "post",
+    data,
+  });
+}
+
+// 修改
+export function update(data) {
+  return request({
+    url: "/question/special",
+    method: "put",
+    data,
+  });
+}
+
+// 删除
+export function del(ids) {
+  return request({
+    url: `/question/special/${ids}`,
+    method: "delete",
+  });
+}

+ 44 - 0
src/api/exerciseManage/taskManage/index.js

@@ -0,0 +1,44 @@
+import request from "@/utils/request";
+
+// 查询列表  /question/task/list
+export function getList(query) {
+  return request({
+    url: "/question/task/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 查询详情
+export function getDetail(id) {
+  return request({
+    url: `/question/task/${id}`,
+    method: "get",
+  });
+}
+
+// 新增
+export function add(data) {
+  return request({
+    url: "/question/task",
+    method: "post",
+    data,
+  });
+}
+
+// 修改
+export function update(data) {
+  return request({
+    url: "/question/task",
+    method: "put",
+    data,
+  });
+}
+
+// 删除
+export function del(ids) {
+  return request({
+    url: `/question/task/${ids}`,
+    method: "delete",
+  });
+}

+ 138 - 0
src/components/ImportFiles/index.vue

@@ -0,0 +1,138 @@
+<template>
+  <!-- 导入文件组件 -->
+  <el-dialog
+    append-to-body
+    :close-on-click-modal="false"
+    :before-close="cancel"
+    :visible.sync="dialog"
+    :title="title"
+    custom-class="common_dialog"
+  >
+    <el-form ref="form" size="small" label-width="auto">
+      <el-form-item :label="$t('button.importList')">
+        <el-upload
+          :data="dataObj"
+          :multiple="true"
+          class="image-uploader"
+          :action="apiurl"
+          :headers="headers"
+          :on-success="success"
+          :on-error="error"
+          :before-upload="beforeUpload"
+          :accept="acceptFileType"
+          :file-list="fileList"
+        >
+          <el-button size="small" type="primary">{{
+            $t("button.import")
+          }}</el-button>
+          <div slot="tip" class="el-upload__tip">
+            {{ $t("tag.fileTips") }}{{ $t("tag.fileAcceptXlsx") }}
+          </div>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label>
+        <el-button @click="downTemplate" :loading="downloading">{{
+          $t("button.downloadTemplate")
+        }}</el-button>
+      </el-form-item>
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="text" @click="cancel">
+        {{ $t("button.cancel") }}</el-button
+      >
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+export default {
+  props: {
+    uploadUrl: {
+      type: String,
+      required: true,
+    },
+    downloadFunc: {
+      type: Function,
+      required: true,
+    },
+    type: {
+      type: Number,
+    },
+  },
+  computed: {
+    apiurl() {
+      return process.env.VUE_APP_BASE_API + this.uploadUrl;
+    },
+  },
+  data() {
+    return {
+      title: "",
+      loading: false,
+      dialog: false,
+      fileList: [],
+      dataObj: { moduleId: "", moduleName: "" },
+      headers: {
+        Authorization: `xytoken_${getToken()}`,
+      },
+      acceptFileType: ".xls,.xlsx",
+      downloading: false,
+    };
+  },
+  methods: {
+    cancel() {
+      this.dialog = false;
+      this.fileList = [];
+      this.$parent.init();
+    },
+    downTemplate() {
+      this.downloading = true;
+      this.downloadFunc()
+        .then((res) => {
+          this.downloading = false;
+        })
+        .catch(() => {
+          this.downloading = false;
+        });
+    },
+    success(response, file, fileList) {
+      let { code, msg } = response;
+      if (code === "200") {
+        this.$message.success("导入成功(Import succeeded)");
+        this.cancel();
+      } else {
+        this.$message.error(msg || "导入失败(Import failed)");
+        this.fileList = [];
+      }
+    },
+    error(err, file, fileList) {
+      this.$message.error("导入失败(Import failed)");
+      this.fileList = [];
+    },
+    beforeUpload(file) {
+      if (!this.uploadUrl) {
+        this.$message.error("未开发");
+        return false;
+      }
+      if (this.type) {
+        this.dataObj.type = this.type;
+      }
+      let type = file.name.substring(file.name.lastIndexOf("."));
+
+      let fileType = this.acceptFileType.split(",");
+      if (!fileType.includes(type)) {
+        this.$message.error("文件类型错误(File type error)");
+        return false;
+      }
+      if (file.size > 20 * 1024 * 1024) {
+        this.$message.error(
+          "文件大小最大为20M(The maximum file size is 20M)"
+        );
+        return false;
+      }
+    },
+  },
+};
+</script>
+
+<style scoped></style>

+ 275 - 0
src/components/QuestionSelect/index.vue

@@ -0,0 +1,275 @@
+<template>
+  <div>
+    <!-- 表单筛选 -->
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      inline
+      v-show="showSearch"
+      label-width="auto"
+    >
+      <el-row :gutter="10">
+        <el-col :span="12">
+          <el-form-item label="问题类型">
+            <el-select
+              style="width: 100%"
+              v-model="queryParams.type"
+              placeholder="请选择状态"
+              clearable
+            >
+              <el-option
+                v-for="dict in typeOption"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="问题描述">
+            <el-input
+              v-model="queryParams.descripiton"
+              placeholder="请输入问题描述"
+              clearable
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="dateRange"
+              value-format="yyyy-MM-dd"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+            ></el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item>
+            <el-button
+              type="primary"
+              icon="el-icon-search"
+              size="mini"
+              @click="handleQuery"
+              >搜索</el-button
+            >
+            <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
+              >重置</el-button
+            >
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <!-- 按钮组 -->
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getTableList"
+      ></right-toolbar>
+    </el-row>
+    <!-- 表格 -->
+    <el-table
+      ref="multipleTable"
+      v-loading="loading"
+      :data="tableList"
+      row-key="id"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="问题类型" align="center" prop="type">
+        <template slot-scope="scope">
+          {{ scope.row.type === "1" ? "单选" : "多选" }}
+        </template>
+      </el-table-column>
+      <el-table-column label="问题描述" align="center" prop="descripiton" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+            >查看</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getTableList"
+    />
+    <div class="footer">
+      <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      <el-button @click="handleCancel">取 消</el-button>
+    </div>
+
+    <!-- 详情弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="dlgVisible"
+      append-to-body
+      width="40%"
+    >
+      <AddAndEdit
+        :loading="dlgLoading"
+        :data="formData"
+        :dlgType="3"
+        @cancel="dlgVisible = false"
+      ></AddAndEdit>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getList, getDetail } from "@/api/exerciseManage/question";
+import AddAndEdit from "@/views/exerciseManage/question/components/addAndEdit.vue";
+export default {
+  components: { AddAndEdit },
+  props: {
+    selectedIds: {
+      type: Array,
+      default: () => [],
+    },
+  },
+  data() {
+    return {
+      // 显示搜索条件
+      showSearch: true,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        type: "",
+        descripiton: "",
+      },
+      // 时间
+      dateRange: [],
+      // 状态组
+      typeOption: [
+        { value: "1", label: "单选" },
+        { value: "2", label: "多选" },
+      ],
+      // 表格相关
+      loading: false,
+      tableList: [],
+      selectedRow: [],
+      total: 0,
+      //  弹窗相关
+      title: "",
+      dlgVisible: false,
+      formData: {},
+      dlgLoading: false,
+    };
+  },
+  created() {
+    this.getTableList();
+  },
+  watch: {
+    selectedIds: {
+      handler(newVal) {
+        // console.log(newVal);
+        if (newVal && newVal.length > 0) {
+          this.$nextTick(() => {
+            this.tableList.forEach((row) => {
+              if (newVal.includes(String(row.id))) {
+                // console.log(111);
+                this.$refs.multipleTable.toggleRowSelection(row, true);
+              }
+            });
+          });
+        }
+      },
+      deep: true,
+      immediate: true,
+    },
+  },
+  methods: {
+    // 确认选择
+    handleConfirm() {
+      this.$emit("confirm", this.selectedRow);
+      this.handleCancel();
+    },
+    // 关闭弹窗
+    handleCancel() {
+      this.$emit("close");
+      // 清空选择
+      this.$refs.multipleTable.clearSelection();
+    },
+    // 搜索
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getTableList();
+    },
+    // 重置
+    resetQuery() {
+      this.dateRange = [];
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        type: "",
+        descripiton: "",
+      };
+      this.getTableList();
+    },
+    // 表格选中
+    handleSelectionChange(selection) {
+      this.selectedRow = selection;
+    },
+    // 查看
+    handleDetail(row) {
+      this.title = "查看";
+      this.dlgVisible = true;
+      this.dlgLoading = true;
+      getDetail(row.id)
+        .then((res) => {
+          if (res.code === 200) {
+            this.formData = { ...res.data, categoryName: row.categoryName };
+          }
+        })
+        .finally(() => (this.dlgLoading = false));
+    },
+    // 查询列表
+    getTableList() {
+      let params = this.addDateRange(this.queryParams, this.dateRange);
+      this.loading = true;
+      getList(params)
+        .then((res) => {
+          if (res.code === 200) {
+            this.tableList = res.rows;
+            this.total = res.total;
+            this.$nextTick(() => {
+              this.tableList.forEach((row) => {
+                if (this.selectedIds.includes(String(row.id))) {
+                  // console.log(222);
+                  this.$refs.multipleTable.toggleRowSelection(row, true);
+                }
+              });
+            });
+          }
+        })
+        .finally(() => (this.loading = false));
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.footer {
+  text-align: right;
+  margin-top: 40px;
+}
+</style>

+ 235 - 0
src/components/SelectedQuestion/index.vue

@@ -0,0 +1,235 @@
+<template>
+  <div>
+    <!-- 表单筛选 -->
+    <!-- <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      inline
+      v-show="showSearch"
+      label-width="auto"
+    >
+      <el-row :gutter="10">
+        <el-col :span="12">
+          <el-form-item label="问题类型">
+            <el-select
+              style="width: 100%"
+              v-model="queryParams.type"
+              placeholder="请选择状态"
+              clearable
+            >
+              <el-option
+                v-for="dict in typeOption"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="问题描述">
+            <el-input
+              v-model="queryParams.descripiton"
+              placeholder="请输入问题描述"
+              clearable
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="dateRange"
+              value-format="yyyy-MM-dd"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+            ></el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item>
+            <el-button
+              type="primary"
+              icon="el-icon-search"
+              size="mini"
+              @click="handleQuery"
+              >搜索</el-button
+            >
+            <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
+              >重置</el-button
+            >
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form> -->
+    <!-- 按钮组 -->
+    <!-- <el-row :gutter="10" class="mb8">
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getTableList"
+      ></right-toolbar>
+    </el-row> -->
+    <!-- 表格 -->
+    <el-table v-loading="loading" :data="tableList" max-height="400">
+      <el-table-column label="问题类型" align="center" prop="type">
+        <template slot-scope="scope">
+          {{ scope.row.type === "1" ? "单选" : "多选" }}
+        </template>
+      </el-table-column>
+      <el-table-column label="问题描述" align="center" prop="descripiton" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+            >查看</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <!-- <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getTableList"
+    /> -->
+    <div class="footer">
+      <el-button @click="handleCancel">关 闭</el-button>
+    </div>
+
+    <!-- 详情弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="dlgVisible"
+      append-to-body
+      width="40%"
+    >
+      <AddAndEdit
+        :loading="dlgLoading"
+        :data="formData"
+        :dlgType="3"
+        @cancel="dlgVisible = false"
+      ></AddAndEdit>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getListByIds, getDetail } from "@/api/exerciseManage/question";
+import AddAndEdit from "@/views/exerciseManage/question/components/addAndEdit.vue";
+export default {
+  components: { AddAndEdit },
+  props: {
+    selectedIds: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      // 显示搜索条件
+      // showSearch: true,
+      // 查询参数
+      // queryParams: {
+      //   pageNum: 1,
+      //   pageSize: 10,
+      //   type: "",
+      //   descripiton: "",
+      // },
+      // 时间
+      // dateRange: [],
+      // 状态组
+      // typeOption: [
+      //   { value: "1", label: "单选" },
+      //   { value: "2", label: "多选" },
+      // ],
+      // 表格相关
+      loading: false,
+      tableList: [],
+      // selectedRow: [],
+      // total: 0,
+      //  弹窗相关
+      title: "",
+      dlgVisible: false,
+      formData: {},
+      dlgLoading: false,
+    };
+  },
+  watch: {
+    selectedIds: {
+      handler(newVal) {
+        // console.log(newVal);
+        if (newVal) {
+          this.getTableList();
+        }
+      },
+      immediate: true,
+    },
+  },
+  methods: {
+    // 关闭弹窗
+    handleCancel() {
+      this.$emit("close");
+    },
+    // 搜索
+    // handleQuery() {
+    //   this.queryParams.pageNum = 1;
+    //   this.getTableList();
+    // },
+    // 重置
+    // resetQuery() {
+    //   this.dateRange = [];
+    //   this.queryParams = {
+    //     pageNum: 1,
+    //     pageSize: 10,
+    //     type: "",
+    //     descripiton: "",
+    //   };
+    //   this.getTableList();
+    // },
+    // 查看
+    handleDetail(row) {
+      this.title = "查看";
+      this.dlgVisible = true;
+      this.dlgLoading = true;
+      getDetail(row.id)
+        .then((res) => {
+          if (res.code === 200) {
+            this.formData = { ...res.data, categoryName: row.categoryName };
+          }
+        })
+        .finally(() => (this.dlgLoading = false));
+    },
+    // 查询列表
+    getTableList() {
+      this.loading = true;
+      getListByIds({ questioqnIds: this.selectedIds })
+        .then((res) => {
+          if (res.code === 200) {
+            this.tableList = res.data;
+          }
+        })
+        .finally(() => (this.loading = false));
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.footer {
+  text-align: right;
+  margin-top: 40px;
+}
+</style>

+ 104 - 0
src/views/exerciseManage/category/components/addAndEdit.vue

@@ -0,0 +1,104 @@
+<template>
+  <div v-loading="loading">
+    <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+      <el-form-item label="分类名称" prop="name">
+        <el-input v-model="form.name" placeholder="请输入分类名称" />
+      </el-form-item>
+      <el-form-item label="排序" prop="sort">
+        <el-input-number
+          style="width: 100%"
+          v-model="form.sort"
+          :min="1"
+          :max="9999"
+          label="请输入排序"
+        ></el-input-number>
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          type="textarea"
+          :rows="4"
+          v-model="form.remark"
+          placeholder="请输入备注"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="text-align: right">
+      <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      <el-button @click="handleCancel">取 消</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    data: {
+      type: Object,
+      default: () => {},
+    },
+    loading: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      // 状态组
+      statusOption: [
+        { value: "0", label: "显示" },
+        { value: "1", label: "隐藏" },
+      ],
+      form: {
+        name: "",
+        sort: "",
+        remark: "",
+      },
+      rules: {
+        name: [{ required: true, trigger: "blur", message: "请输入分类名称" }],
+        sort: [{ required: true, trigger: "blur", message: "请输入排序" }],
+      },
+    };
+  },
+  watch: {
+    data: {
+      handler(newVal) {
+        if (newVal) {
+          this.form = {
+            id: newVal.id || "",
+            name: newVal.name || "",
+            sort: newVal.sort || "",
+            remark: newVal.remark || "",
+          };
+          this.$nextTick(() => {
+            this.$refs.form && this.$refs.form.clearValidate();
+          });
+        }
+      },
+    },
+  },
+  methods: {
+    // 确定
+    handleConfirm() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          this.$emit("confirm", this.form);
+        }
+      });
+    },
+    // 取消
+    handleCancel() {
+      this.form = {
+        name: "",
+        sort: "",
+        remark: "",
+      };
+      this.$nextTick(() => {
+        this.$refs.form && this.$refs.form.clearValidate();
+      });
+      this.$emit("cancel");
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 278 - 0
src/views/exerciseManage/category/index.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="app-container">
+    <!-- 表单筛选 -->
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      inline
+      v-show="showSearch"
+      label-width="80px"
+    >
+      <el-form-item label="分类名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入分类名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 100%"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQuery"
+          >搜索</el-button
+        >
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
+          >重置</el-button
+        >
+      </el-form-item>
+    </el-form>
+    <!-- 按钮组 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          >新增</el-button
+        >
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="selectedRow.length < 1"
+          @click="handleDelete"
+          >删除</el-button
+        >
+      </el-col>
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getTableList"
+      ></right-toolbar>
+    </el-row>
+    <!-- 表格 -->
+    <el-table
+      v-loading="loading"
+      :data="tableList"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="名称" align="center" prop="name" />
+      <el-table-column label="排序" align="center" prop="sort" />
+      <el-table-column
+        label="备注"
+        align="center"
+        prop="remark"
+        show-overflow-tooltip
+      />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            >修改</el-button
+          >
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getTableList"
+    />
+    <!-- 新增编辑弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="dlgVisible"
+      append-to-body
+      width="30%"
+    >
+      <AddAndEdit
+        :loading="dlgLoading"
+        :data="formData"
+        @confirm="handleConfirm"
+        @cancel="handleCancel"
+      ></AddAndEdit>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  getList,
+  getDetail,
+  add,
+  update,
+  del,
+} from "@/api/exerciseManage/category";
+import AddAndEdit from "./components/addAndEdit.vue";
+export default {
+  name: "Category",
+  components: {
+    AddAndEdit,
+  },
+  data() {
+    return {
+      // 显示搜索条件
+      showSearch: true,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: "",
+      },
+      // 时间
+      dateRange: [],
+      // 状态组
+      statusOption: [
+        { value: "0", label: "显示" },
+        { value: "1", label: "隐藏" },
+      ],
+      // 表格相关
+      loading: false,
+      tableList: [],
+      selectedRow: [],
+      total: 0,
+      //  弹窗相关
+      title: "",
+      dlgVisible: false,
+      formData: {},
+      dlgLoading: false,
+    };
+  },
+  created() {
+    this.getTableList();
+  },
+  methods: {
+    // 搜索
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getTableList();
+    },
+    // 重置
+    resetQuery() {
+      this.dateRange = [];
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        name: "",
+      };
+      this.getTableList();
+    },
+    // 新增
+    handleAdd() {
+      this.title = "新增";
+      this.dlgVisible = true;
+      this.formData = {};
+    },
+    // 修改
+    handleUpdate(row) {
+      this.title = "编辑";
+      this.dlgVisible = true;
+      this.dlgLoading = true;
+      // const id = row.id || this.selectedRow[0].id;
+      getDetail(row.id)
+        .then((res) => {
+          if (res.code === 200) {
+            this.formData = res.data;
+          }
+        })
+        .finally(() => (this.dlgLoading = false));
+    },
+    // 弹窗确认
+    handleConfirm(val) {
+      if (val.id) {
+        update(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("编辑成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      } else {
+        add(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("新增成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      }
+    },
+    // 弹窗取消
+    handleCancel() {
+      this.dlgVisible = false;
+    },
+    // 删除
+    handleDelete(row) {
+      const selectIds = this.selectedRow.map((item) => item.id);
+      const ids = row.id || selectIds;
+      this.$modal
+        .confirm("是否确认删除数据?")
+        .then(function () {
+          return del(ids);
+        })
+        .then(() => {
+          this.getTableList();
+          this.$modal.msgSuccess("删除成功");
+        })
+        .catch(() => {});
+    },
+    // 表格选中
+    handleSelectionChange(selection) {
+      this.selectedRow = selection;
+    },
+    // 查询列表
+    getTableList() {
+      let params = this.addDateRange(this.queryParams, this.dateRange);
+      this.loading = true;
+      getList(params)
+        .then((res) => {
+          if (res.code === 200) {
+            this.tableList = res.rows;
+            this.total = res.total;
+          }
+        })
+        .finally(() => (this.loading = false));
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 349 - 0
src/views/exerciseManage/question/components/addAndEdit.vue

@@ -0,0 +1,349 @@
+<template>
+  <div v-loading="loading">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="80px"
+      :disabled="dlgType === 3"
+    >
+      <el-form-item label="问题分类" prop="categoryId">
+        <SelectRemote
+          url="/question/category/list"
+          field="name"
+          v-model="form.categoryId"
+          :selectObj="selectObj"
+          :searchParams="searchParams"
+        />
+      </el-form-item>
+      <el-form-item label="问题类型" prop="type">
+        <el-radio-group v-model="form.type" @input="changeType">
+          <el-radio label="1">单选</el-radio>
+          <el-radio label="2">多选</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="问题描述" prop="descripiton">
+        <el-input
+          type="textarea"
+          :rows="4"
+          v-model="form.descripiton"
+          placeholder="请输入问题描述"
+        />
+      </el-form-item>
+      <el-form-item label="选项列表" prop="itemList">
+        <el-button
+          type="primary"
+          @click="addOption"
+          style="margin-bottom: 6px"
+          v-if="dlgType !== 3"
+          >添加选项</el-button
+        >
+        <el-table :data="form.itemList" style="width: 100%">
+          <el-table-column prop="code" label="选项" width="80" align="center" />
+          <el-table-column prop="optVal" label="值" align="center" />
+          <el-table-column
+            label="操作"
+            width="120"
+            align="center"
+            v-if="dlgType !== 3"
+          >
+            <template #default="{ row }">
+              <el-button type="text" size="small" @click="editOption(row)"
+                >修改</el-button
+              >
+              <el-button type="text" size="small" @click="deleteOption(row)"
+                >删除</el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form-item>
+      <el-form-item label="正确选项" prop="correctItem">
+        <!--  value-key="code" -->
+        <el-select
+          ref="selectRef"
+          :multiple="form.type === '2'"
+          v-model="form.correctItem"
+          placeholder="请选择正确选项"
+          style="width: 100%"
+        >
+          <!-- :label="`${item.code} - ${item.value}`" -->
+          <el-option
+            v-for="item in form.itemList"
+            :key="item.code"
+            :label="item.code"
+            :value="item.code"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="问题解析" prop="analysis">
+        <el-input
+          type="textarea"
+          :rows="4"
+          v-model="form.analysis"
+          placeholder="请输入问题解析"
+        />
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          type="textarea"
+          :rows="4"
+          v-model="form.remark"
+          placeholder="请输入备注"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="text-align: right" v-if="dlgType !== 3">
+      <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      <el-button @click="handleCancel">取 消</el-button>
+    </div>
+    <div style="text-align: right" v-else>
+      <el-button @click="handleCancel">关 闭</el-button>
+    </div>
+    <!-- 新增/编辑选项框 -->
+    <el-dialog
+      :title="dlgTitle"
+      :visible.sync="optionVisible"
+      width="30%"
+      append-to-body
+    >
+      <el-form
+        ref="optionForm"
+        :model="optionForm"
+        :rules="optionRules"
+        label-width="80px"
+      >
+        <el-form-item label="选项">
+          <el-input v-model="optionForm.code" disabled />
+        </el-form-item>
+        <el-form-item label="值" prop="optVal">
+          <el-input
+            type="textarea"
+            :rows="4"
+            v-model="optionForm.optVal"
+            placeholder="请输入选项值"
+          />
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="confirmOption">确 定</el-button>
+        <el-button @click="optionVisible = false">取 消</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import SelectRemote from "@/components/SelectRemote";
+export default {
+  components: { SelectRemote },
+  props: {
+    data: {
+      type: Object,
+      default: () => {},
+    },
+    loading: {
+      type: Boolean,
+      default: false,
+    },
+    dlgType: {
+      // 弹窗类型  1 - 新增   2 - 修改   3 - 查看
+      type: Number,
+      default: 1,
+    },
+  },
+  data() {
+    return {
+      // 选中的问题分类
+      selectObj: {},
+      // 远程搜索框的查询条件
+      searchParams: {},
+      // 选项弹窗相关参数
+      dlgTitle: "",
+      optionVisible: false,
+      optionForm: {},
+      optionRules: {
+        optVal: [
+          { required: true, trigger: "blur", message: "选项值不能为空" },
+        ],
+      },
+      form: {
+        categoryId: "",
+        type: "1",
+        descripiton: "",
+        itemList: [],
+        correctItem: "",
+        analysis: "",
+        remark: "",
+      },
+      rules: {
+        categoryId: [
+          { required: true, trigger: "change", message: "请选择问题分类" },
+        ],
+        descripiton: [
+          { required: true, trigger: "blur", message: "请输入问题描述" },
+        ],
+        itemList: [{ required: true, trigger: "blur", message: "请添加选项" }],
+        correctItem: [
+          { required: true, trigger: "change", message: "请选择正确选项" },
+        ],
+        analysis: [
+          { required: true, trigger: "blur", message: "请输入问题解析" },
+        ],
+      },
+    };
+  },
+  watch: {
+    data: {
+      handler(newVal) {
+        if (newVal) {
+          this.form = {
+            id: newVal.id || "",
+            categoryId: newVal.categoryId || "",
+            type: newVal.type || "1",
+            descripiton: newVal.descripiton || "",
+            itemList: newVal.itemList ? JSON.parse(newVal.itemList) : [],
+            correctItem: newVal.correctItem
+              ? newVal.type === "1"
+                ? newVal.correctItem
+                : newVal.correctItem.split(",")
+              : "",
+            analysis: newVal.analysis || "",
+            remark: newVal.remark || "",
+          };
+          this.selectObj = {
+            id: newVal.categoryId || "",
+            selectLabel: newVal.categoryName || "",
+          };
+          this.$nextTick(() => {
+            this.$refs.form && this.$refs.form.clearValidate();
+          });
+        }
+      },
+    },
+  },
+  methods: {
+    // 切换单选多选
+    changeType(val) {
+      console.log(this.form.correctItem);
+      // console.log(val, this.form.correctItem, this.$refs.selectRef);
+      if (val === "2") {
+        this.form.correctItem = [];
+        this.$refs.selectRef.selectedLabel = "";
+      } else {
+        this.form.correctItem = undefined;
+      }
+      this.$nextTick(() => {
+        this.$refs.form && this.$refs.form.clearValidate();
+      });
+    },
+    // 确认添加选项
+    confirmOption() {
+      this.$refs.optionForm.validate((valid) => {
+        if (valid) {
+          if (this.dlgTitle === "编辑选项") {
+            let targetIndex = this.form.itemList.findIndex(
+              (item) => item.code === this.optionForm.code
+            );
+            this.$set(this.form.itemList, targetIndex, this.optionForm);
+          } else {
+            this.form.itemList.push(this.optionForm);
+          }
+          this.optionVisible = false;
+        }
+      });
+    },
+    // 删除选项(需要更新一下code值)
+    deleteOption(row) {
+      console.log("删除的当前行数据", row);
+      let targetIndex = this.form.itemList.findIndex(
+        (item) => item.code === row.code
+      );
+      // 先判断一下删除的选项是否被选中为正确选项
+      // 是的话 需要确认是否删除
+      if (this.form.correctItem.includes(row.code)) {
+        this.$modal
+          .confirm("当前选项已被选中为正确选项,是否确认删除?")
+          .then(() => {
+            this.form.itemList.splice(targetIndex, 1);
+            // 清除正确选项
+            this.form.correctItem = this.form.type === "1" ? "" : [];
+            // 更新选项列表的code
+            this.form.itemList.forEach((item, index) => {
+              item.code = String.fromCharCode(index + 65);
+            });
+          })
+          .catch(() => {});
+      } else {
+        this.form.itemList.splice(targetIndex, 1);
+        // 更新选项列表的code
+        this.form.itemList.forEach((item, index) => {
+          item.code = String.fromCharCode(index + 65);
+        });
+      }
+    },
+    // 编辑选项
+    editOption(row) {
+      console.log("编辑的当前行数据", row);
+      this.dlgTitle = "编辑选项";
+      this.optionVisible = true;
+      this.optionForm = { ...row };
+    },
+    // 添加选项
+    addOption() {
+      // 首先拿到选项列表的最后一项的code,然后根据这个code生成下一项的code
+      // 也可以拿到选项列表的长度,根据长度判断下一个的code, A ----  65
+      let index = this.form.itemList.length;
+      let nextCode = String.fromCharCode(index + 65);
+      console.log("下一项的code", nextCode);
+      this.dlgTitle = "新增选项";
+      this.optionVisible = true;
+      this.optionForm = { code: nextCode, optVal: "" };
+      // 清除一下校验
+      this.$refs.optionForm && this.$refs.optionForm.clearValidate();
+    },
+    // 确定
+    handleConfirm() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          let obj = {
+            ...this.form,
+            itemList: JSON.stringify(this.form.itemList),
+            correctItem:
+              this.form.type === "1"
+                ? this.form.correctItem
+                : this.sortEn(this.form.correctItem).join(","),
+          };
+          console.log(obj);
+          this.$emit("confirm", obj);
+        }
+      });
+    },
+    // 数组的值按照英文字母排序
+    sortEn(arr) {
+      arr.sort(function (a, b) {
+        return a.charCodeAt(0).toString(16) - b.charCodeAt(0).toString(16);
+      });
+      return arr;
+    },
+    // 取消
+    handleCancel() {
+      this.form = {
+        categoryId: "",
+        type: "1",
+        descripiton: "",
+        itemList: [],
+        correctItem: "",
+        analysis: "",
+        remark: "",
+      };
+      this.$nextTick(() => {
+        this.$refs.form && this.$refs.form.clearValidate();
+      });
+      this.$emit("cancel");
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 377 - 0
src/views/exerciseManage/question/index.vue

@@ -0,0 +1,377 @@
+<template>
+  <div class="app-container">
+    <!-- 表单筛选 -->
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      inline
+      v-show="showSearch"
+      label-width="80px"
+    >
+      <el-form-item label="问题类型">
+        <el-select
+          v-model="queryParams.type"
+          placeholder="请选择状态"
+          clearable
+        >
+          <el-option
+            v-for="dict in typeOption"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="问题描述">
+        <el-input
+          v-model="queryParams.descripiton"
+          placeholder="请输入问题描述"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 100%"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQuery"
+          >搜索</el-button
+        >
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
+          >重置</el-button
+        >
+      </el-form-item>
+    </el-form>
+    <!-- 按钮组 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          >新增</el-button
+        >
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="selectedRow.length < 1"
+          @click="handleDelete"
+          >删除</el-button
+        >
+      </el-col>
+      <!-- <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-upload"
+          size="mini"
+          @click="handleImport"
+          >导入</el-button
+        >
+      </el-col> -->
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          >导出</el-button
+        >
+      </el-col>
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getTableList"
+      ></right-toolbar>
+    </el-row>
+    <!-- 表格 -->
+    <el-table
+      v-loading="loading"
+      :data="tableList"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="问题类型" align="center" prop="type">
+        <template slot-scope="scope">
+          {{ scope.row.type === "1" ? "单选" : "多选" }}
+        </template>
+      </el-table-column>
+      <el-table-column label="问题描述" align="center" prop="descripiton" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+            >查看</el-button
+          >
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            >修改</el-button
+          >
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getTableList"
+    />
+    <!-- 新增编辑弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="dlgVisible"
+      append-to-body
+      width="40%"
+    >
+      <AddAndEdit
+        :loading="dlgLoading"
+        :data="formData"
+        :dlgType="dlgType"
+        @confirm="handleConfirm"
+        @cancel="handleCancel"
+      ></AddAndEdit>
+    </el-dialog>
+
+    <!-- 导入弹窗 -->
+    <el-dialog
+      title="导入"
+      :visible.sync="uploadVisiable"
+      width="400px"
+      append-to-body
+    >
+      <ImportFiles />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary">确 定</el-button>
+        <el-button @click="uploadVisiable = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  getList,
+  getDetail,
+  add,
+  update,
+  del,
+  exportData,
+} from "@/api/exerciseManage/question";
+import AddAndEdit from "./components/addAndEdit.vue";
+import ImportFiles from "@/components/ImportFiles";
+export default {
+  name: "Question",
+  components: {
+    AddAndEdit,
+    ImportFiles,
+  },
+  data() {
+    return {
+      // 显示搜索条件
+      showSearch: true,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        type: "",
+        descripiton: "",
+      },
+      // 时间
+      dateRange: [],
+      // 状态组
+      typeOption: [
+        { value: "1", label: "单选" },
+        { value: "2", label: "多选" },
+      ],
+      // 表格相关
+      loading: false,
+      tableList: [],
+      selectedRow: [],
+      total: 0,
+      //  弹窗相关
+      title: "",
+      dlgVisible: false,
+      formData: {},
+      dlgLoading: false,
+      dlgType: 1, // 弹窗类型  1 - 新增   2 - 修改   3 - 查看
+      // 导入弹窗相关
+      uploadVisiable: false,
+    };
+  },
+  created() {
+    this.getTableList();
+  },
+  methods: {
+    // 导入
+    handleImport() {
+      this.uploadVisiable = true;
+    },
+    // 导出
+    handleExport() {
+      let params = {
+        type: this.queryParams.type,
+        descripiton: this.queryParams.descripiton,
+      };
+      let newParams = this.addDateRange(params, this.dateRange);
+      // exportData(newParams).then((res) => {
+      //   console.log(res);
+      // });
+      this.download(
+        "/question/info/export",
+        {
+          ...newParams,
+        },
+        `题目管理_${new Date().getTime()}.xlsx`
+      );
+    },
+    // 搜索
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getTableList();
+    },
+    // 重置
+    resetQuery() {
+      this.dateRange = [];
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        type: "",
+        descripiton: "",
+      };
+      this.getTableList();
+    },
+    // 新增
+    handleAdd() {
+      this.title = "新增";
+      this.dlgVisible = true;
+      this.dlgType = 1;
+      this.formData = {};
+    },
+    // 修改
+    handleUpdate(row) {
+      this.title = "编辑";
+      this.dlgType = 2;
+      this.dlgVisible = true;
+      this.dlgLoading = true;
+      getDetail(row.id)
+        .then((res) => {
+          if (res.code === 200) {
+            this.formData = { ...res.data, categoryName: row.categoryName };
+          }
+        })
+        .finally(() => (this.dlgLoading = false));
+    },
+    // 查看
+    handleDetail(row) {
+      this.title = "查看";
+      this.dlgType = 3;
+      this.dlgVisible = true;
+      this.dlgLoading = true;
+      getDetail(row.id)
+        .then((res) => {
+          if (res.code === 200) {
+            this.formData = { ...res.data, categoryName: row.categoryName };
+          }
+        })
+        .finally(() => (this.dlgLoading = false));
+    },
+    // 弹窗确认
+    handleConfirm(val) {
+      if (val.id) {
+        update(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("编辑成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      } else {
+        add(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("新增成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      }
+    },
+    // 弹窗取消
+    handleCancel() {
+      this.dlgVisible = false;
+    },
+    // 删除
+    handleDelete(row) {
+      const selectIds = this.selectedRow.map((item) => item.id);
+      const ids = row.id || selectIds;
+      this.$modal
+        .confirm("是否确认删除数据?")
+        .then(function () {
+          return del(ids);
+        })
+        .then(() => {
+          this.getTableList();
+          this.$modal.msgSuccess("删除成功");
+        })
+        .catch(() => {});
+    },
+    // 表格选中
+    handleSelectionChange(selection) {
+      this.selectedRow = selection;
+    },
+    // 查询列表
+    getTableList() {
+      let params = this.addDateRange(this.queryParams, this.dateRange);
+      this.loading = true;
+      getList(params)
+        .then((res) => {
+          if (res.code === 200) {
+            this.tableList = res.rows;
+            this.total = res.total;
+          }
+        })
+        .finally(() => (this.loading = false));
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 191 - 0
src/views/exerciseManage/specialManage/components/addAndEdit.vue

@@ -0,0 +1,191 @@
+<template>
+  <div v-loading="loading">
+    <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+      <el-form-item label="名称" prop="title">
+        <el-input
+          v-model="form.title"
+          placeholder="请输入名称"
+          :disabled="dlgType === 3"
+        />
+      </el-form-item>
+      <el-form-item label="题目列表" prop="questionIds">
+        <el-button type="primary" @click="selectQuestion">
+          {{ selectNumber ? `已选${selectNumber}条题目` : "选择题目" }}
+        </el-button>
+      </el-form-item>
+      <el-form-item label="显示状态" prop="status">
+        <el-select
+          :disabled="dlgType === 3"
+          v-model="form.status"
+          placeholder="请选择显示状态"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="dict in statusOption"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          :disabled="dlgType === 3"
+          type="textarea"
+          :rows="4"
+          v-model="form.remark"
+          placeholder="请输入备注"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="text-align: right" v-if="dlgType !== 3">
+      <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      <el-button @click="handleCancel">取 消</el-button>
+    </div>
+    <div style="text-align: right" v-else>
+      <el-button @click="handleCancel">关 闭</el-button>
+    </div>
+    <!-- 题目列表(可选) -->
+    <el-dialog title="选择题目" :visible.sync="dlgVisible" append-to-body>
+      <QuestionSelect
+        :selectedIds="selectedIdArr"
+        @close="handleClose"
+        @confirm="confirmQuestion"
+      />
+    </el-dialog>
+
+    <!-- 题目列表(不可选,用于查看的) -->
+    <el-dialog title="已选题目" :visible.sync="selectedVisible" append-to-body>
+      <SelectedQuestion
+        :selectedIds="selectedIds"
+        @close="selectedVisible = false"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import QuestionSelect from "@/components/QuestionSelect";
+import SelectedQuestion from "@/components/SelectedQuestion";
+export default {
+  components: { QuestionSelect, SelectedQuestion },
+  props: {
+    data: {
+      type: Object,
+      default: () => {},
+    },
+    loading: {
+      type: Boolean,
+      default: false,
+    },
+    dlgType: {
+      type: Number,
+      default: 1,
+    },
+  },
+  data() {
+    return {
+      // 状态组
+      statusOption: [
+        { value: "0", label: "显示" },
+        { value: "1", label: "隐藏" },
+      ],
+      // 题目列表弹窗相关参数
+      dlgVisible: false,
+      selectedVisible: false,
+      selectedIdArr: [],
+      selectedIds: "",
+      // 已选题目的条数
+      selectNumber: 0,
+      form: {
+        title: "",
+        questionIds: "",
+        status: "",
+        remark: "",
+      },
+      rules: {
+        title: [{ required: true, trigger: "blur", message: "请输入名称" }],
+        questionIds: [
+          { required: true, trigger: "blur", message: "请选择题目" },
+        ],
+        status: [
+          { required: true, trigger: "change", message: "请选择显示状态" },
+        ],
+      },
+    };
+  },
+  watch: {
+    data: {
+      handler(newVal) {
+        if (newVal) {
+          this.form = {
+            id: newVal.id || "",
+            title: newVal.title || "",
+            questionIds: newVal.questionIds || "",
+            status: newVal.status || "",
+            remark: newVal.remark || "",
+          };
+          let selects = newVal.questionIds ? newVal.questionIds.split(",") : [];
+          this.selectNumber = selects.length;
+          this.$nextTick(() => {
+            this.$refs.form && this.$refs.form.clearValidate();
+          });
+        }
+      },
+    },
+  },
+  methods: {
+    // 关闭题库弹窗
+    handleClose() {
+      this.dlgVisible = false;
+    },
+    // 选择题目,打开题库弹窗
+    selectQuestion() {
+      if (this.dlgType !== 3) {
+        this.dlgVisible = true;
+        this.selectedIdArr = this.form.questionIds
+          ? this.form.questionIds.split(",")
+          : [];
+      } else {
+        this.selectedVisible = true;
+        this.selectedIds = this.form.questionIds;
+      }
+    },
+    // 确认添加题目
+    confirmQuestion(val) {
+      // console.log(val);
+      this.form.questionIds = val.map((t) => t.id).join(",");
+      this.selectNumber = val.length;
+      // console.log(this.form.questionIds);
+    },
+    // 确定
+    handleConfirm() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          let obj = {
+            ...this.form,
+          };
+          console.log(obj);
+          this.$emit("confirm", obj);
+        }
+      });
+    },
+
+    // 取消
+    handleCancel() {
+      this.form = {
+        title: "",
+        questionIds: "",
+        status: "",
+        remark: "",
+      };
+      this.$nextTick(() => {
+        this.$refs.form && this.$refs.form.clearValidate();
+      });
+      this.$emit("cancel");
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 319 - 0
src/views/exerciseManage/specialManage/index.vue

@@ -0,0 +1,319 @@
+<template>
+  <div class="app-container">
+    <!-- 表单筛选 -->
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      inline
+      v-show="showSearch"
+      label-width="80px"
+    >
+      <el-form-item label="问题类型">
+        <el-select
+          v-model="queryParams.type"
+          placeholder="请选择状态"
+          clearable
+        >
+          <el-option
+            v-for="dict in typeOption"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="问题描述">
+        <el-input
+          v-model="queryParams.descripiton"
+          placeholder="请输入问题描述"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 100%"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQuery"
+          >搜索</el-button
+        >
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
+          >重置</el-button
+        >
+      </el-form-item>
+    </el-form>
+    <!-- 按钮组 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          >新增</el-button
+        >
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="selectedRow.length < 1"
+          @click="handleDelete"
+          >删除</el-button
+        >
+      </el-col>
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getTableList"
+      ></right-toolbar>
+    </el-row>
+    <!-- 表格 -->
+    <el-table
+      v-loading="loading"
+      :data="tableList"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="名称" align="center" prop="title" />
+      <el-table-column label="显示状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <el-tag
+            effect="dark"
+            :type="scope.row.status === '0' ? 'success' : 'danger'"
+          >
+            {{ scope.row.status === "0" ? "显示" : "隐藏" }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+            >查看</el-button
+          >
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            >修改</el-button
+          >
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getTableList"
+    />
+    <!-- 新增编辑弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="dlgVisible"
+      append-to-body
+      width="40%"
+    >
+      <AddAndEdit
+        :loading="dlgLoading"
+        :data="formData"
+        :dlgType="dlgType"
+        @confirm="handleConfirm"
+        @cancel="handleCancel"
+      ></AddAndEdit>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  getList,
+  getDetail,
+  add,
+  update,
+  del,
+} from "@/api/exerciseManage/specialManage";
+import AddAndEdit from "./components/addAndEdit.vue";
+export default {
+  name: "SpecialManage",
+  components: {
+    AddAndEdit,
+  },
+  data() {
+    return {
+      // 显示搜索条件
+      showSearch: true,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        type: "",
+        descripiton: "",
+      },
+      // 时间
+      dateRange: [],
+      // 状态组
+      typeOption: [
+        { value: "1", label: "单选" },
+        { value: "2", label: "多选" },
+      ],
+      // 表格相关
+      loading: false,
+      tableList: [],
+      selectedRow: [],
+      total: 0,
+      //  弹窗相关
+      title: "",
+      dlgVisible: false,
+      formData: {},
+      dlgLoading: false,
+      dlgType: 1, // 1 - 新增  2 - 编辑   3 - 详情
+    };
+  },
+  created() {
+    this.getTableList();
+  },
+  methods: {
+    // 搜索
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getTableList();
+    },
+    // 重置
+    resetQuery() {
+      this.dateRange = [];
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        type: "",
+        descripiton: "",
+      };
+      this.getTableList();
+    },
+    // 新增
+    handleAdd() {
+      this.title = "新增";
+      this.dlgVisible = true;
+      this.dlgType = 1;
+      this.formData = {};
+    },
+    // 修改
+    handleUpdate(row) {
+      this.title = "编辑";
+      this.dlgVisible = true;
+      this.dlgType = 2;
+      this.getDetailData(row);
+    },
+    // 查看
+    handleDetail(row) {
+      this.title = "查看";
+      this.dlgVisible = true;
+      this.dlgType = 3;
+      this.getDetailData(row);
+    },
+    // 请求详情数据
+    getDetailData(row) {
+      this.dlgLoading = true;
+      getDetail(row.id)
+        .then((res) => {
+          if (res.code === 200) {
+            this.formData = { ...res.data, categoryName: row.categoryName };
+          }
+        })
+        .finally(() => (this.dlgLoading = false));
+    },
+    // 弹窗确认
+    handleConfirm(val) {
+      if (val.id) {
+        update(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("编辑成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      } else {
+        add(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("新增成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      }
+    },
+    // 弹窗取消
+    handleCancel() {
+      this.dlgVisible = false;
+    },
+    // 删除
+    handleDelete(row) {
+      const selectIds = this.selectedRow.map((item) => item.id);
+      const ids = row.id || selectIds;
+      this.$modal
+        .confirm("是否确认删除数据?")
+        .then(function () {
+          return del(ids);
+        })
+        .then(() => {
+          this.getTableList();
+          this.$modal.msgSuccess("删除成功");
+        })
+        .catch(() => {});
+    },
+    // 表格选中
+    handleSelectionChange(selection) {
+      this.selectedRow = selection;
+    },
+    // 查询列表
+    getTableList() {
+      let params = this.addDateRange(this.queryParams, this.dateRange);
+      this.loading = true;
+      getList(params)
+        .then((res) => {
+          if (res.code === 200) {
+            this.tableList = res.rows;
+            this.total = res.total;
+          }
+        })
+        .finally(() => (this.loading = false));
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 190 - 0
src/views/exerciseManage/taskManage/components/addAndEdit.vue

@@ -0,0 +1,190 @@
+<template>
+  <div v-loading="loading">
+    <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+      <el-form-item label="任务名称" prop="title">
+        <el-input
+          v-model="form.title"
+          placeholder="请输入任务名称"
+          :disabled="dlgType === 3"
+        />
+      </el-form-item>
+      <el-form-item label="答题日期" prop="answerTime">
+        <el-date-picker
+          :disabled="dlgType === 3"
+          v-model="form.answerTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择答题日期"
+          :picker-options="pickerOptions"
+        >
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="题目列表" prop="questionIds">
+        <el-button type="primary" @click="selectQuestion">
+          {{ selectNumber ? `已选${selectNumber}条题目` : "选择题目" }}
+        </el-button>
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          :disabled="dlgType === 3"
+          type="textarea"
+          :rows="4"
+          v-model="form.remark"
+          placeholder="请输入备注"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="text-align: right" v-if="dlgType !== 3">
+      <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      <el-button @click="handleCancel">取 消</el-button>
+    </div>
+    <div style="text-align: right" v-else>
+      <el-button @click="handleCancel">关 闭</el-button>
+    </div>
+    <!-- 题目列表 -->
+    <el-dialog title="选择题目" :visible.sync="dlgVisible" append-to-body>
+      <QuestionSelect
+        :selectedIds="selectedIds"
+        @close="handleClose"
+        @confirm="confirmQuestion"
+      />
+    </el-dialog>
+
+    <!-- 题目列表(不可选,用于查看的) -->
+    <el-dialog title="已选题目" :visible.sync="selectedVisible" append-to-body>
+      <SelectedQuestion
+        :selectedIds="selectedIds"
+        @close="selectedVisible = false"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import QuestionSelect from "@/components/QuestionSelect";
+import SelectedQuestion from "@/components/SelectedQuestion";
+export default {
+  components: { QuestionSelect, SelectedQuestion },
+  props: {
+    data: {
+      type: Object,
+      default: () => {},
+    },
+    loading: {
+      type: Boolean,
+      default: false,
+    },
+    dlgType: {
+      type: Number,
+      default: 1,
+    },
+  },
+  data() {
+    return {
+      pickerOptions: {
+        disabledDate(time) {
+          return time.getTime() < Date.now() - 8.64e7;
+        },
+      },
+      // 题目列表弹窗相关参数
+      dlgVisible: false,
+      selectedVisible: false,
+      selectedIdArr: [],
+      selectedIds: "",
+      // 已选题目的条数
+      selectNumber: 0,
+      form: {
+        title: "",
+        questionIds: "",
+        answerTime: "",
+        remark: "",
+        type: "1", // 创建类型。默认为1 手动    2 自动
+      },
+      rules: {
+        title: [{ required: true, trigger: "blur", message: "请输入任务名称" }],
+        answerTime: [
+          { required: true, trigger: "blur", message: "请选择答题日期" },
+        ],
+        questionIds: [
+          { required: true, trigger: "blur", message: "请选择题目" },
+        ],
+      },
+    };
+  },
+  watch: {
+    data: {
+      handler(newVal) {
+        if (newVal) {
+          this.form = {
+            id: newVal.id || "",
+            title: newVal.title || "",
+            questionIds: newVal.questionIds || "",
+            answerTime: newVal.answerTime || "",
+            remark: newVal.remark || "",
+            type: newVal.type || "1",
+          };
+          let selects = newVal.questionIds ? newVal.questionIds.split(",") : [];
+          this.selectNumber = selects.length;
+          this.$nextTick(() => {
+            this.$refs.form && this.$refs.form.clearValidate();
+          });
+        }
+      },
+    },
+  },
+  methods: {
+    // 关闭题库弹窗
+    handleClose() {
+      this.dlgVisible = false;
+    },
+    // 选择题目,打开题库弹窗
+    selectQuestion() {
+      if (this.dlgType !== 3) {
+        this.dlgVisible = true;
+        this.selectedIdArr = this.form.questionIds
+          ? this.form.questionIds.split(",")
+          : [];
+      } else {
+        this.selectedVisible = true;
+        this.selectedIds = this.form.questionIds;
+      }
+    },
+    // 确认添加题目
+    confirmQuestion(val) {
+      // console.log(val);
+      this.form.questionIds = val.map((t) => t.id).join(",");
+      this.selectNumber = val.length;
+      // console.log(this.form.questionIds);
+    },
+    // 确定
+    handleConfirm() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          let obj = {
+            ...this.form,
+          };
+          console.log(obj);
+          this.$emit("confirm", obj);
+        }
+      });
+    },
+
+    // 取消
+    handleCancel() {
+      this.form = {
+        title: "",
+        questionIds: "",
+        answerTime: "",
+        remark: "",
+        type: "1",
+      };
+      this.$nextTick(() => {
+        this.$refs.form && this.$refs.form.clearValidate();
+      });
+      this.$emit("cancel");
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 333 - 0
src/views/exerciseManage/taskManage/index.vue

@@ -0,0 +1,333 @@
+<template>
+  <div class="app-container">
+    <!-- 表单筛选 -->
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      inline
+      v-show="showSearch"
+      label-width="80px"
+    >
+      <el-form-item label="任务名称">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入任务名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="答题日期">
+        <el-date-picker
+          v-model="answerDateRange"
+          style="width: 100%"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 100%"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQuery"
+          >搜索</el-button
+        >
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
+          >重置</el-button
+        >
+      </el-form-item>
+    </el-form>
+    <!-- 按钮组 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          >新增</el-button
+        >
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="selectedRow.length < 1"
+          @click="handleDelete"
+          >删除</el-button
+        >
+      </el-col>
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getTableList"
+      ></right-toolbar>
+    </el-row>
+    <!-- 表格 -->
+    <el-table
+      v-loading="loading"
+      :data="tableList"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column
+        type="selection"
+        width="55"
+        align="center"
+        :selectable="selectable"
+      />
+      <el-table-column label="任务名称" align="center" prop="title" />
+      <el-table-column label="答题日期" align="center" prop="answerTime" />
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column
+        label="创建类型"
+        align="center"
+        prop="type"
+        :formatter="typeFormatter"
+      />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+            >查看</el-button
+          >
+          <el-button
+            v-if="scope.row.answerTime >= parseTime(new Date(), '{y}-{m}-{d}')"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            >修改</el-button
+          >
+          <el-button
+            v-if="scope.row.answerTime > parseTime(new Date(), '{y}-{m}-{d}')"
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getTableList"
+    />
+    <!-- 新增编辑弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="dlgVisible"
+      append-to-body
+      width="40%"
+    >
+      <AddAndEdit
+        :loading="dlgLoading"
+        :data="formData"
+        :dlgType="dlgType"
+        @confirm="handleConfirm"
+        @cancel="handleCancel"
+      ></AddAndEdit>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  getList,
+  getDetail,
+  add,
+  update,
+  del,
+} from "@/api/exerciseManage/taskManage";
+import AddAndEdit from "./components/addAndEdit.vue";
+export default {
+  name: "TaskManage",
+  components: {
+    AddAndEdit,
+  },
+  data() {
+    return {
+      // 显示搜索条件
+      showSearch: true,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        title: "",
+      },
+      // 时间
+      dateRange: [],
+      answerDateRange: [],
+      // 状态组
+      typeOption: [
+        { value: "1", label: "手动" },
+        { value: "2", label: "自动" },
+      ],
+      // 表格相关
+      loading: false,
+      tableList: [],
+      selectedRow: [],
+      total: 0,
+      //  弹窗相关
+      title: "",
+      dlgVisible: false,
+      formData: {},
+      dlgLoading: false,
+      dlgType: 1, // 1 - 新增  2 - 编辑   3 - 详情
+    };
+  },
+  created() {
+    this.getTableList();
+  },
+  methods: {
+    // 表格当前行是否可选
+    selectable(row, index) {
+      return row.answerTime > this.parseTime(new Date(), "{y}-{m}-{d}");
+    },
+    // 来源值映射
+    typeFormatter(row) {
+      return this.selectDictLabel(this.typeOption, row.type);
+    },
+    // 搜索
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getTableList();
+    },
+    // 重置
+    resetQuery() {
+      this.dateRange = [];
+      this.answerDateRange = [];
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        title: "",
+      };
+      this.getTableList();
+    },
+    // 新增
+    handleAdd() {
+      this.title = "新增";
+      this.dlgType = 1;
+      this.dlgVisible = true;
+      this.formData = {};
+    },
+    // 修改
+    handleUpdate(row) {
+      this.title = "编辑";
+      this.dlgType = 1;
+      this.dlgVisible = true;
+      this.getDetailData(row);
+    },
+    // 查看
+    handleDetail(row) {
+      this.title = "查看";
+      this.dlgType = 3;
+      this.dlgVisible = true;
+      this.getDetailData(row);
+    },
+    // 请求详情数据
+    getDetailData(row) {
+      this.dlgLoading = true;
+      getDetail(row.id)
+        .then((res) => {
+          if (res.code === 200) {
+            this.formData = { ...res.data, categoryName: row.categoryName };
+          }
+        })
+        .finally(() => (this.dlgLoading = false));
+    },
+    // 弹窗确认
+    handleConfirm(val) {
+      if (val.id) {
+        update(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("编辑成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      } else {
+        add(val).then((res) => {
+          if (res.code === 200) {
+            this.$modal.msgSuccess("新增成功");
+            this.handleCancel();
+            this.getTableList();
+          }
+        });
+      }
+    },
+    // 弹窗取消
+    handleCancel() {
+      this.dlgVisible = false;
+    },
+    // 删除
+    handleDelete(row) {
+      const selectIds = this.selectedRow.map((item) => item.id);
+      const ids = row.id || selectIds;
+      this.$modal
+        .confirm("是否确认删除数据?")
+        .then(function () {
+          return del(ids);
+        })
+        .then(() => {
+          this.getTableList();
+          this.$modal.msgSuccess("删除成功");
+        })
+        .catch(() => {});
+    },
+    // 表格选中
+    handleSelectionChange(selection) {
+      this.selectedRow = selection;
+    },
+    // 查询列表
+    getTableList() {
+      let params1 = this.addDateRange(this.queryParams, this.dateRange);
+      let params2 = this.addDateRange(
+        params1,
+        this.answerDateRange,
+        "answerTime"
+      );
+      this.loading = true;
+      getList(params2)
+        .then((res) => {
+          if (res.code === 200) {
+            this.tableList = res.rows;
+            this.total = res.total;
+          }
+        })
+        .finally(() => (this.loading = false));
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>