diff --git a/src/assets/js/asyncRouter.js b/src/assets/js/asyncRouter.js
index df4c92e..1b6ec36 100644
--- a/src/assets/js/asyncRouter.js
+++ b/src/assets/js/asyncRouter.js
@@ -2146,4 +2146,64 @@ export default [
       },
     ],
   },
+  {
+    path: "/security_investment",
+    redirect: "/security_investment/plan",
+    meta: { title: "安全投入", model: MODEL["5"] },
+    component: "children",
+    children: [
+      {
+        path: "/security_investment/plan",
+        meta: { title: "安全投入计划", isSubMenu: false },
+        component: "security_investment/plan/index",
+      },
+      {
+        path: "/security_investment/plan_review",
+        meta: { title: "安全投入计划审核", isSubMenu: false },
+        component: "security_investment/plan_review/index",
+      },
+      {
+        path: "/security_investment/extraction_and_use",
+        meta: { title: "安全生产费用提取和使用管理", isSubMenu: false },
+        component: "security_investment/extraction_and_use/index",
+      },
+    ],
+  },
+  {
+    path: "/learning_garden",
+    redirect: "/learning_garden/learning_garden",
+    meta: { title: "学习园地", model: MODEL["5"] },
+    component: "children",
+    children: [
+      {
+        path: "/learning_garden/learning_garden",
+        meta: { title: "学习园地", isSubMenu: false },
+        component: "children",
+        children: [
+          {
+            path: "",
+            component: "learning_garden/learning_garden/index",
+          },
+          {
+            path: "/learning_garden/learning_garden/task_list",
+            meta: {
+              title: "任务列表",
+              activeMenu: "/learning_garden/learning_garden",
+            },
+            component: "learning_garden/learning_garden/task_list",
+          },
+        ],
+      },
+      {
+        path: "/learning_garden/enterprise_video",
+        meta: { title: "企业视频", isSubMenu: false },
+        component: "learning_garden/enterprise_video/index",
+      },
+      {
+        path: "/learning_garden/regulatory_library",
+        meta: { title: "法规库", isSubMenu: false },
+        component: "learning_garden/regulatory_library/index",
+      },
+    ],
+  },
 ];
diff --git a/src/assets/js/data_dictionary.js b/src/assets/js/data_dictionary.js
index 6f8d739..7b923ac 100644
--- a/src/assets/js/data_dictionary.js
+++ b/src/assets/js/data_dictionary.js
@@ -258,6 +258,20 @@ export const layoutFnGetEquipmentType = async () => {
   });
   return ref(resData.list);
 };
+// 学习园地类型
+export const layoutFnGetLearningGardenType = async () => {
+  const resData = await getLevels({
+    DICTIONARIES_ID: "5aa989ad8fd54bef862c1b096a3b07d8",
+  });
+  return ref(resData.list);
+};
+// 学习园地学习类型
+export const layoutFnGetLearningGardenLearningType = async () => {
+  const resData = await getLevels({
+    DICTIONARIES_ID: "c4f00e7ed9334789ba2fb4795f54bae2",
+  });
+  return ref(resData.list);
+};
 // 部门树
 export const layoutFnGetDepartmentTree = async (params) => {
   const resData = await getDepartmentTree(params);
diff --git a/src/assets/js/useListData.js b/src/assets/js/useListData.js
index eb5343c..27d21c9 100644
--- a/src/assets/js/useListData.js
+++ b/src/assets/js/useListData.js
@@ -74,6 +74,10 @@ export default function useListData(api, options = {}) {
     list.value = resData[key];
     if (usePagination) pagination.value.total = resData.page.totalResult;
     options.callbackFn && options.callbackFn(list.value, resData);
+    !usePagination &&
+      clearSelection &&
+      tableRef.value &&
+      tableRef.value.clearSelection();
   };
   immediate && fnGetData().then();
   const fnResetPagination = async (otherParams) => {
diff --git a/src/components/multiple_attachment_previews/index.vue b/src/components/multiple_attachment_previews/index.vue
new file mode 100644
index 0000000..9322a00
--- /dev/null
+++ b/src/components/multiple_attachment_previews/index.vue
@@ -0,0 +1,110 @@
+<template>
+  <div style="display: inline-block">
+    <el-button
+      link
+      type="primary"
+      v-if="interceptTheSuffix(filePath, '.txt')"
+      @click="fnPreviewTxt(filePath)"
+    >
+      [预览]
+    </el-button>
+    <el-button
+      text
+      link
+      type="primary"
+      v-if="interceptTheSuffix(filePath, '.pdf')"
+      @click="fnPreviewPdf(filePath)"
+    >
+      [预览]
+    </el-button>
+    <el-button
+      link
+      type="primary"
+      v-if="interceptTheSuffix(filePath, '.mp4')"
+      @click="fnPreviewVideo(filePath)"
+    >
+      [预览]
+    </el-button>
+    <a
+      v-if="
+        interceptTheSuffix(filePath, '.doc') ||
+        interceptTheSuffix(filePath, '.xls') ||
+        interceptTheSuffix(filePath, '.ppt') ||
+        interceptTheSuffix(filePath, '.docx') ||
+        interceptTheSuffix(filePath, '.xlsx') ||
+        interceptTheSuffix(filePath, '.pptx')
+      "
+      :href="
+        'http://view.officeapps.live.com/op/view.aspx?src=' +
+        VITE_FILE_URL +
+        filePath
+      "
+      target="_blank"
+    >
+      [预览]
+    </a>
+    <layout-pdf
+      :src="data.pdfDialog.src"
+      v-model:visible="data.pdfDialog.visible"
+      append-to-body
+    />
+    <layout-txt
+      :src="data.txtDialog.src"
+      v-model:visible="data.txtDialog.visible"
+      append-to-body
+    />
+    <layout-video
+      :src="data.videoDialog.src"
+      v-model:visible="data.videoDialog.visible"
+      append-to-body
+    />
+  </div>
+</template>
+
+<script setup>
+import { interceptTheSuffix } from "@/assets/js/utils.js";
+import { reactive } from "vue";
+import LayoutVideo from "@/components/video/index.vue";
+import LayoutTxt from "@/components/txt/index.vue";
+import LayoutPdf from "@/components/pdf/index.vue";
+
+defineOptions({
+  name: "LayoutMultipleAttachmentPreviews",
+});
+defineProps({
+  filePath: {
+    type: String,
+    required: true,
+    default: "",
+  },
+});
+const VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
+const data = reactive({
+  pdfDialog: {
+    visible: false,
+    src: "",
+  },
+  videoDialog: {
+    visible: false,
+    src: "",
+  },
+  txtDialog: {
+    visible: false,
+    src: "",
+  },
+});
+const fnPreviewTxt = async (FILEPATH) => {
+  data.txtDialog.visible = true;
+  data.txtDialog.src = FILEPATH;
+};
+const fnPreviewPdf = (FILEPATH) => {
+  data.pdfDialog.visible = true;
+  data.pdfDialog.src = FILEPATH;
+};
+const fnPreviewVideo = (FILEPATH) => {
+  data.videoDialog.visible = true;
+  data.videoDialog.src = FILEPATH;
+};
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/components/pdf/index.vue b/src/components/pdf/index.vue
index a422529..795f76e 100644
--- a/src/components/pdf/index.vue
+++ b/src/components/pdf/index.vue
@@ -3,6 +3,7 @@
     title="文档"
     :model-value="visible && model === 'dialog'"
     @update:model-value="visible = false"
+    :append-to-body="appendToBody"
   >
     <div v-if="visible" style="height: 690px; overflow-y: auto">
       <vue-pdf
@@ -58,6 +59,10 @@ const props = defineProps({
     },
     default: "dialog",
   },
+  appendToBody: {
+    type: Boolean,
+    default: false,
+  },
 });
 const emits = defineEmits(["update:visible"]);
 const visible = useVModel(props, "visible", emits);
diff --git a/src/components/three_institutional_libraries/index.vue b/src/components/three_institutional_libraries/index.vue
new file mode 100644
index 0000000..2b08398
--- /dev/null
+++ b/src/components/three_institutional_libraries/index.vue
@@ -0,0 +1,94 @@
+<template>
+  <el-tree-select
+    ref="treeSelectRef"
+    v-model="modelValue"
+    :data="tree"
+    :props="{
+      value: type !== 'label' ? 'DICTIONARIES_ID' : 'BUS_LABEL_FACTORY_ID',
+      children: type !== 'label' ? 'nodes' : 'children',
+      label: 'NAME',
+      isLeaf: 'isLeaf',
+    }"
+    :node-key="type !== 'label' ? 'DICTIONARIES_ID' : 'BUS_LABEL_FACTORY_ID'"
+    :render-after-expand="false"
+    accordion
+    check-strictly
+    :lazy="type !== 'label'"
+    :load="fnLoad"
+    :clearable="true"
+    show-checkbox
+    multiple
+    collapse-tags
+  />
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { useVModel } from "@vueuse/core";
+import { layoutFnGetLevelsAndChildrenNumber } from "@/assets/js/data_dictionary.js";
+import { getThreeInstitutionalLibrariesLabel } from "@/request/three_institutional_libraries.js";
+
+defineOptions({
+  name: "LayoutThreeInstitutionalLibraries",
+});
+const props = defineProps({
+  modelValue: {
+    type: String,
+    required: true,
+    default: "",
+  },
+  type: {
+    type: String,
+    required: true,
+    default: "",
+    validator: (value) => {
+      // regulations: 规程属性
+      // type: 类型
+      // industry: 国民经济行业类型
+      // label: 标签
+      const typeList = ["regulations", "type", "industry", "label"];
+      if (typeList.includes(value)) {
+        return true;
+      } else {
+        throw new Error(`type必须是${typeList.join("、")}之一`);
+      }
+    },
+  },
+});
+const emits = defineEmits(["update:modelValue", "update:checkList"]);
+const modelValue = useVModel(props, "modelValue", emits);
+const treeSelectRef = ref(null);
+const fnLoad = async (node, resolve) => {
+  if (props.type === "label") return;
+  const resData = await layoutFnGetLevelsAndChildrenNumber(
+    node.data.DICTIONARIES_ID || id
+  );
+  resolve(
+    resData.value.map((item) => {
+      return {
+        DICTIONARIES_ID: item.DICTIONARIES_ID,
+        BIANMA: item.BIANMA,
+        NAME: item.NAME,
+        isLeaf: item.zcount === 0,
+      };
+    })
+  );
+};
+const getCheckedNodes = () => {
+  return treeSelectRef.value.getCheckedNodes();
+};
+defineExpose({
+  getCheckedNodes,
+});
+let tree = [];
+let id = "";
+if (props.type === "regulations") id = "84254cb5b2ae40eb9f451509b2d370ae";
+if (props.type === "type") id = "5a7c94b2b9514285b433759edd848b4a";
+if (props.type === "industry") id = "f2598ba72e864eadabf0ca4b664d26b9";
+if (props.type === "label") {
+  const resData = await getThreeInstitutionalLibrariesLabel();
+  tree = resData.tree;
+}
+</script>
+
+<style scoped></style>
diff --git a/src/components/txt/index.vue b/src/components/txt/index.vue
index ba1e9b5..83d97cc 100644
--- a/src/components/txt/index.vue
+++ b/src/components/txt/index.vue
@@ -1,5 +1,5 @@
 <template>
-  <el-dialog title="文本文档" v-model="visible">
+  <el-dialog title="文本文档" v-model="visible" :append-to-body="appendToBody">
     <el-input autosize :model-value="value" readonly type="textarea" />
   </el-dialog>
 </template>
@@ -23,6 +23,10 @@ const props = defineProps({
     required: true,
     default: "",
   },
+  appendToBody: {
+    type: Boolean,
+    default: false,
+  },
 });
 const value = ref("");
 const emits = defineEmits(["update:visible"]);
diff --git a/src/components/video/index.vue b/src/components/video/index.vue
index dd709ee..7542abe 100644
--- a/src/components/video/index.vue
+++ b/src/components/video/index.vue
@@ -1,5 +1,5 @@
 <template>
-  <el-dialog title="视频" v-model="visible">
+  <el-dialog title="视频" v-model="visible" :append-to-body="appendToBody">
     <ali-player
       :source="fnSrc(src)"
       :vid="vid"
@@ -37,6 +37,10 @@ const props = defineProps({
     required: true,
     default: false,
   },
+  appendToBody: {
+    type: Boolean,
+    default: false,
+  },
 });
 const emits = defineEmits(["update:visible"]);
 const visible = useVModel(props, "visible", emits);
diff --git a/src/request/enterprise_management.js b/src/request/enterprise_management.js
index 5358e93..b94cfbc 100644
--- a/src/request/enterprise_management.js
+++ b/src/request/enterprise_management.js
@@ -1,5 +1,6 @@
 import { post, upload } from "@/request/axios.js";
 
+export const getEnterpriseList = (params) => post("/corpinfo/list", params); // 获取企业列表
 export const getEnterpriseInfo = (params) => post("/corpinfo/goEdit", params); // 获取企业信息
 export const setEnterpriseInfo = (params) => upload("/corpinfo/edit", params); // 修改企业信息
 export const getIndustryQualificationsList = (params) =>
diff --git a/src/request/learning_garden.js b/src/request/learning_garden.js
new file mode 100644
index 0000000..f629f78
--- /dev/null
+++ b/src/request/learning_garden.js
@@ -0,0 +1,28 @@
+import { post, upload } from "@/request/axios.js";
+
+export const getLearningGardenList = (params) =>
+  post("/studysection/list", params); // 学习园地列表
+export const setLearningGardenDelete = (params) =>
+  post("/studysection/delete", params); // 学习园地删除
+export const setLearningGardenAdd = (params) =>
+  upload("/studysection/add", params); // 学习园地新增
+export const getLearningGardenTaskList = (params) =>
+  post("/tempstudy/list", params); // 学习园地任务列表
+export const setLearningGardenTaskDelete = (params) =>
+  post("/tempstudy/delete", params); // 学习园地任务删除
+export const setLearningGardenTaskBatchDelete = (params) =>
+  post("/tempstudy/deleteAll", params); // 学习园地任务批量删除
+export const setLearningGardenTaskDisableOrEnable = (params) =>
+  post("/tempstudy/edit", params); // 学习园地任务禁用或启用
+export const setLearningGardenTaskLearningSituationList = (params) =>
+  post("/tempstudy/studentList", params); // 学习园地任务学习情况列表
+export const setLearningGardenTaskLearningSituationAdd = (params) =>
+  post("/tempstudy/add", params); // 学习园地任务学习情况新增
+export const getEnterpriseVideoList = (params) => post("/video/list", params); // 企业视频列表
+export const setEnterpriseVideoDelete = (params) =>
+  post("/video/delete", params); // 企业视频删除
+export const setEnterpriseVideoRelease = (params) =>
+  post("/video/editZhiding", params); // 企业视频发布
+export const setEnterpriseVideoAdd = (params) => upload("/video/add", params); // 企业视频新增
+export const getRegulatoryLibraryList = (params) =>
+  post("/mfolderStipulate/list", params); // 法规库列表
diff --git a/src/request/security_investment.js b/src/request/security_investment.js
new file mode 100644
index 0000000..55f9bc6
--- /dev/null
+++ b/src/request/security_investment.js
@@ -0,0 +1,21 @@
+import { post } from "@/request/axios.js";
+
+export const getPlanList = (params) =>
+  post("/safetyinvestmentplan/list", params); // 安全投入计划列表
+export const getPlanView = (params) =>
+  post("/safetyinvestmentplan/goEdit", params); // 安全投入计划查看
+export const setPlanDelete = (params) =>
+  post("/safetyinvestmentplan/delete", params); // 安全投入计划删除
+export const setPlanBatchDelete = (params) =>
+  post("/safetyinvestmentplan/deleteAll", params); // 安全投入计划批量删除
+export const setPlanAdd = (params) => post("/safetyinvestmentplan/add", params); // 安全投入计划新增
+export const setPlanEdit = (params) =>
+  post("/safetyinvestmentplan/edit", params); // 安全投入计划编辑
+export const setPlanReview = (params) =>
+  post("/safetyinvestmentplan/editStatus", params); // 安全投入计划审核
+export const getExtractionAndUseList = (params) =>
+  post("/safetyinvestmentuse/listAll", params); // 安全生产费用提取和使用管理列表
+export const setExtractionAndUseBatchDelete = (params) =>
+  post("/safetyinvestmentuse/deleteAll", params); // 安全生产费用提取和使用管理批量删除
+export const setExtractionAndUseAdd = (params) =>
+  post("/safetyinvestmentuse/add", params); // 安全生产费用提取和使用管理新增
diff --git a/src/request/three_institutional_libraries.js b/src/request/three_institutional_libraries.js
new file mode 100644
index 0000000..9e16096
--- /dev/null
+++ b/src/request/three_institutional_libraries.js
@@ -0,0 +1,6 @@
+import { post } from "@/request/axios.js";
+
+export const getThreeInstitutionalLibrariesList = (params) =>
+  post("/textLibrary/list", params); // 三项制度库列表
+export const getThreeInstitutionalLibrariesLabel = (params) =>
+  post("/labelFactory/tree", params); // 三项制度库标签
diff --git a/src/views/hazard_investigation/inventory_management/components/select_risk.vue b/src/views/hazard_investigation/inventory_management/components/select_risk.vue
index e5f62ab..523fae7 100644
--- a/src/views/hazard_investigation/inventory_management/components/select_risk.vue
+++ b/src/views/hazard_investigation/inventory_management/components/select_risk.vue
@@ -94,7 +94,10 @@ const props = defineProps({
 const emits = defineEmits(["update:visible", "submit"]);
 const visible = useVModel(props, "visible", emits);
 const { list, searchForm, pagination, fnGetData, fnResetPagination, tableRef } =
-  useListData(getInventoryManagementOtherRiskList, { immediate: false });
+  useListData(getInventoryManagementOtherRiskList, {
+    immediate: false,
+    clearSelection: false,
+  });
 const riskClassificationList = await layoutFnGetRiskClassification();
 const stop = watch(
   () => props.visible,
diff --git a/src/views/learning_garden/enterprise_video/components/add.vue b/src/views/learning_garden/enterprise_video/components/add.vue
new file mode 100644
index 0000000..b3f2408
--- /dev/null
+++ b/src/views/learning_garden/enterprise_video/components/add.vue
@@ -0,0 +1,83 @@
+<template>
+  <el-dialog v-model="visible" title="新增" :on-close="fnClose">
+    <el-form ref="formRef" :rules="rules" :model="form" label-width="110px">
+      <el-form-item label="视频名称" prop="NAME">
+        <el-input v-model="form.NAME" />
+      </el-form-item>
+      <el-form-item label="内容" prop="DESCR">
+        <el-input
+          v-model="form.DESCR"
+          type="textarea"
+          :autosize="{ minRows: 3 }"
+        />
+      </el-form-item>
+      <el-form-item label="附件路径" prop="file">
+        <layout-upload v-model:file-list="form.file" accept=".mp4">
+          <template #tip>最大支持100M视频</template>
+        </layout-upload>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="fnClose">取 消</el-button>
+      <el-button type="primary" @click="fnSubmit">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useVModel } from "@vueuse/core";
+import { ref } from "vue";
+import { debounce } from "throttle-debounce";
+import useFormValidate from "@/assets/js/useFormValidate.js";
+import { ElMessage } from "element-plus";
+import LayoutUpload from "@/components/upload/index.vue";
+import { setEnterpriseVideoAdd } from "@/request/learning_garden.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+});
+const emits = defineEmits(["update:visible", "get-data"]);
+const visible = useVModel(props, "visible", emits);
+const rules = ref({
+  NAME: [{ required: true, message: "视频名称不能为空", trigger: "blur" }],
+  DESCR: [{ required: true, message: "内容不能为空", trigger: "blur" }],
+  file: [{ required: true, message: "请上传附件", trigger: "change" }],
+});
+const formRef = ref(null);
+const form = ref({
+  NAME: "",
+  DESCR: "",
+  file: [],
+  CORPINFO_TYPE: "1",
+});
+const fnClose = () => {
+  formRef.value.resetFields();
+  visible.value = false;
+};
+const fnSubmit = debounce(
+  1000,
+  async () => {
+    await useFormValidate(formRef);
+    const formData = new FormData();
+    Object.keys(form.value).forEach((key) => {
+      formData.append(key, form.value[key]);
+    });
+    formData.delete("file");
+    if (form.value.file[0].raw) {
+      formData.append("file", form.value.file[0].raw);
+      formData.append("fileName", form.value.file[0].name);
+    }
+    await setEnterpriseVideoAdd(formData);
+    ElMessage.success("提交成功");
+    fnClose();
+    emits("get-data");
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/learning_garden/enterprise_video/index.vue b/src/views/learning_garden/enterprise_video/index.vue
new file mode 100644
index 0000000..b59cbe5
--- /dev/null
+++ b/src/views/learning_garden/enterprise_video/index.vue
@@ -0,0 +1,126 @@
+<template>
+  <div>
+    <el-card>
+      <el-form
+        :model="searchForm"
+        label-width="90px"
+        @submit.prevent="fnResetPagination"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="视频名称" prop="KEYWORDS">
+              <el-input
+                v-model="searchForm.KEYWORDS"
+                placeholder="请输入关键字"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label-width="10px">
+              <el-button type="primary" native-type="submit">搜索</el-button>
+              <el-button native-type="reset" @click="fnResetPagination">
+                重置
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+    <layout-card>
+      <layout-table
+        :data="list"
+        v-model:pagination="pagination"
+        @get-data="fnGetData"
+      >
+        <el-table-column label="序号" width="70">
+          <template v-slot="{ $index }">
+            {{ serialNumber(pagination, $index) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="NAME" label="视频名称" />
+        <el-table-column prop="CREATOR" label="添加人" />
+        <el-table-column prop="CREATTIME" label="添加时间" />
+        <el-table-column label="是否置顶">
+          <template v-slot="{ row }">
+            <span v-if="row.TYPE === '0'">否</span>
+            <span v-if="row.TYPE === '1'">是</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="150">
+          <template v-slot="{ row }">
+            <el-button
+              v-if="row.TYPE === '0' && buttonJurisdiction.add"
+              type="primary"
+              text
+              link
+              @click="fnRelease(row.VIDEO_ID)"
+            >
+              发布
+            </el-button>
+            <el-button
+              v-if="buttonJurisdiction.del"
+              type="primary"
+              text
+              link
+              @click="fnDelete(row.VIDEO_ID)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+        <template #button>
+          <el-button
+            v-if="buttonJurisdiction.add"
+            type="primary"
+            @click="addDialogVisible = true"
+          >
+            新增
+          </el-button>
+        </template>
+      </layout-table>
+    </layout-card>
+    <add v-model:visible="addDialogVisible" @get-data="fnResetPagination" />
+  </div>
+</template>
+
+<script setup>
+import { serialNumber } from "@/assets/js/utils.js";
+import useListData from "@/assets/js/useListData.js";
+import { debounce } from "throttle-debounce";
+import { ElMessage, ElMessageBox } from "element-plus";
+import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
+import {
+  getEnterpriseVideoList,
+  setEnterpriseVideoDelete,
+  setEnterpriseVideoRelease,
+} from "@/request/learning_garden.js";
+import { ref } from "vue";
+import Add from "./components/add.vue";
+
+const addDialogVisible = ref(false);
+const { list, pagination, searchForm, fnGetData, fnResetPagination } =
+  useListData(getEnterpriseVideoList);
+const buttonJurisdiction = await useButtonJurisdiction("video");
+const fnDelete = debounce(
+  1000,
+  async (VIDEO_ID) => {
+    await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
+    await setEnterpriseVideoDelete({ VIDEO_ID });
+    ElMessage.success("删除成功");
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+const fnRelease = debounce(
+  1000,
+  async (VIDEO_ID) => {
+    await ElMessageBox.confirm("确定要发布吗?", { type: "warning" });
+    await setEnterpriseVideoRelease({ VIDEO_ID });
+    ElMessage.success("发布成功");
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped></style>
diff --git a/src/views/learning_garden/learning_garden/components/add.vue b/src/views/learning_garden/learning_garden/components/add.vue
new file mode 100644
index 0000000..697ad7e
--- /dev/null
+++ b/src/views/learning_garden/learning_garden/components/add.vue
@@ -0,0 +1,94 @@
+<template>
+  <el-dialog v-model="visible" title="新增" :on-close="fnClose">
+    <el-form ref="formRef" :rules="rules" :model="form" label-width="110px">
+      <el-form-item label="类型" prop="TYPE">
+        <el-select v-model="form.TYPE">
+          <el-option
+            v-for="item in learningGardenTypeList"
+            :key="item.DICTIONARIES_ID"
+            :label="item.NAME"
+            :value="item.DICTIONARIES_ID"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="标题" prop="TITLE">
+        <el-input v-model="form.TITLE" />
+      </el-form-item>
+      <el-form-item label="内容" prop="CONTENT">
+        <el-input
+          v-model="form.CONTENT"
+          type="textarea"
+          :autosize="{ minRows: 3 }"
+        />
+      </el-form-item>
+      <el-form-item label="附件路径" prop="file">
+        <layout-upload v-model:file-list="form.file" accept=".pdf" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="fnClose">取 消</el-button>
+      <el-button type="primary" @click="fnSubmit">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useVModel } from "@vueuse/core";
+import { ref } from "vue";
+import { debounce } from "throttle-debounce";
+import useFormValidate from "@/assets/js/useFormValidate.js";
+import { ElMessage } from "element-plus";
+import LayoutUpload from "@/components/upload/index.vue";
+import { layoutFnGetLearningGardenType } from "@/assets/js/data_dictionary.js";
+import { setLearningGardenAdd } from "@/request/learning_garden.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+});
+const emits = defineEmits(["update:visible", "get-data"]);
+const visible = useVModel(props, "visible", emits);
+const rules = ref({
+  TYPE: [{ required: true, message: "请选择类型", trigger: "change" }],
+  TITLE: [{ required: true, message: "请输入标题", trigger: "blur" }],
+  CONTENT: [{ required: true, message: "请输入内容", trigger: "blur" }],
+  file: [{ required: true, message: "请上传附件", trigger: "change" }],
+});
+const formRef = ref(null);
+const form = ref({
+  TYPE: "",
+  TITLE: "",
+  CONTENT: "",
+  file: [],
+});
+const learningGardenTypeList = await layoutFnGetLearningGardenType();
+const fnClose = () => {
+  formRef.value.resetFields();
+  visible.value = false;
+};
+const fnSubmit = debounce(
+  1000,
+  async () => {
+    await useFormValidate(formRef);
+    const formData = new FormData();
+    Object.keys(form.value).forEach((key) => {
+      formData.append(key, form.value[key]);
+    });
+    formData.delete("file");
+    if (form.value.file[0].raw) {
+      formData.append("file", form.value.file[0].raw);
+      formData.append("fileName", form.value.file[0].name);
+    }
+    await setLearningGardenAdd(formData);
+    ElMessage.success("提交成功");
+    fnClose();
+    emits("get-data");
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/learning_garden/learning_garden/components/add_task.vue b/src/views/learning_garden/learning_garden/components/add_task.vue
new file mode 100644
index 0000000..6079459
--- /dev/null
+++ b/src/views/learning_garden/learning_garden/components/add_task.vue
@@ -0,0 +1,109 @@
+<template>
+  <el-dialog v-model="visible" title="新增" :on-close="fnClose">
+    <el-form ref="formRef" :rules="rules" :model="form" label-width="110px">
+      <el-form-item label="名称" prop="NAME">
+        <el-input v-model="form.NAME" />
+      </el-form-item>
+      <el-form-item label="学习类型" prop="STUDY_TYPE">
+        <el-select v-model="form.STUDY_TYPE">
+          <el-option
+            v-for="item in learningGardenLearningTypeList"
+            :key="item.BIANMA"
+            :label="item.NAME"
+            :value="item.BIANMA"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="学习人员" prop="STUDY_USER_NAMES">
+        <div>
+          <div>{{ form.STUDY_USER_NAMES.join("、") }}</div>
+          <el-button type="primary" @click="userDialogVisible = true">
+            添加人员
+          </el-button>
+        </div>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="fnClose">取 消</el-button>
+      <el-button type="primary" @click="fnSubmit">确 定</el-button>
+    </template>
+    <user
+      type=""
+      v-model:user-list="form.STUDY_USER_IDS"
+      v-model:user-name="form.STUDY_USER_NAMES"
+      v-model:visible="userDialogVisible"
+    />
+  </el-dialog>
+</template>
+
+<script setup>
+import { useVModel } from "@vueuse/core";
+import { ref } from "vue";
+import { debounce } from "throttle-debounce";
+import useFormValidate from "@/assets/js/useFormValidate.js";
+import { ElMessage } from "element-plus";
+import { layoutFnGetLearningGardenLearningType } from "@/assets/js/data_dictionary.js";
+import User from "./user.vue";
+import { setLearningGardenTaskLearningSituationAdd } from "@/request/learning_garden.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+  id: {
+    type: String,
+    required: true,
+    default: "",
+  },
+});
+const emits = defineEmits(["update:visible", "get-data"]);
+const visible = useVModel(props, "visible", emits);
+const rules = ref({
+  NAME: [{ required: true, message: "名称不能为空", trigger: "blur" }],
+  STUDY_TYPE: [
+    { required: true, message: "学习类型不能为空", trigger: "blur" },
+  ],
+  STUDY_USER_NAMES: [
+    { required: true, message: "请选择学习人员", trigger: "change" },
+  ],
+});
+const formRef = ref(null);
+const form = ref({
+  NAME: "",
+  STUDY_TYPE: "",
+  STUDY_USER_IDS: [],
+  STUDY_USER_NAMES: [],
+  COURSEWARE_TYPE: 2,
+  STATUS: "1",
+});
+const userDialogVisible = ref(false);
+const learningGardenLearningTypeList =
+  await layoutFnGetLearningGardenLearningType();
+const fnClose = () => {
+  formRef.value.resetFields();
+  form.value.STUDY_USER_IDS = [];
+  visible.value = false;
+};
+const fnSubmit = debounce(
+  1000,
+  async () => {
+    await useFormValidate(formRef);
+    const STUDY_USER_IDS = form.value.STUDY_USER_IDS.map(
+      (item) => item.USER_ID
+    ).join(",");
+    await setLearningGardenTaskLearningSituationAdd({
+      ...form.value,
+      COURSEWARE_ID: props.id,
+      STUDY_USER_IDS,
+    });
+    ElMessage.success("提交成功");
+    fnClose();
+    emits("get-data");
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/learning_garden/learning_garden/components/learning_situation.vue b/src/views/learning_garden/learning_garden/components/learning_situation.vue
new file mode 100644
index 0000000..0854db9
--- /dev/null
+++ b/src/views/learning_garden/learning_garden/components/learning_situation.vue
@@ -0,0 +1,68 @@
+<template>
+  <el-dialog v-model="visible" title="学员详情">
+    <layout-table
+      :data="list"
+      v-model:pagination="pagination"
+      @get-data="fnGetDataTransfer"
+    >
+      <el-table-column label="序号" width="70">
+        <template v-slot="{ $index }">
+          {{ serialNumber(pagination, $index) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="USER_NAME" label="姓名" />
+      <el-table-column label="课件类型">
+        <template v-slot="{ row }">
+          <span v-if="row.STUDY_STATUS === '0'">未学习</span>
+          <span v-if="row.STUDY_STATUS === '1'">已学习</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="STUDY_TIME" label="学习时间" />
+    </layout-table>
+    <template #footer>
+      <el-button @click="visible = false">关闭</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useVModel } from "@vueuse/core";
+import { serialNumber } from "@/assets/js/utils.js";
+import { setLearningGardenTaskLearningSituationList } from "@/request/learning_garden.js";
+import useListData from "@/assets/js/useListData.js";
+import { watch } from "vue";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+  id: {
+    type: String,
+    required: true,
+    default: "",
+  },
+});
+const emits = defineEmits(["update:visible"]);
+const visible = useVModel(props, "visible", emits);
+setLearningGardenTaskLearningSituationList();
+const { list, pagination, fnGetData, fnResetPagination } = useListData(
+  setLearningGardenTaskLearningSituationList,
+  { immediate: false }
+);
+const fnGetDataTransfer = () => {
+  fnGetData({ TEMPSTUDY_ID: props.id });
+};
+const fnResetPaginationTransfer = () => {
+  fnResetPagination({ TEMPSTUDY_ID: props.id });
+};
+watch(
+  () => props.visible,
+  () => {
+    if (props.visible) fnResetPaginationTransfer();
+  }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/learning_garden/learning_garden/components/user.vue b/src/views/learning_garden/learning_garden/components/user.vue
new file mode 100644
index 0000000..256f55f
--- /dev/null
+++ b/src/views/learning_garden/learning_garden/components/user.vue
@@ -0,0 +1,124 @@
+<template>
+  <el-dialog v-model="visible" title="选择人员" :on-close="fnClose">
+    <el-form
+      :model="searchForm"
+      label-width="80px"
+      @submit.prevent="fnResetPagination"
+    >
+      <el-row>
+        <el-col :span="9">
+          <el-form-item label="人员名称" prop="KEYWORDS">
+            <el-input v-model="searchForm.KEYWORDS" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="9">
+          <el-form-item label="部门" prop="DEPARTMENT_ID">
+            <layout-department v-model="searchForm.DEPARTMENT_ID" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label-width="10px">
+            <el-button type="primary" native-type="submit">搜索</el-button>
+            <el-button native-type="reset" @click="fnResetPagination">
+              重置
+            </el-button>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <layout-table
+      ref="tableRef"
+      row-key="USER_ID"
+      :data="list"
+      v-model:pagination="pagination"
+      @get-data="fnGetData"
+    >
+      <el-table-column reserve-selection type="selection" width="55" />
+      <el-table-column label="序号" width="60">
+        <template #default="{ $index }">
+          {{ serialNumber(pagination, $index) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="USERNAME" label="用户名" />
+      <el-table-column prop="NAME" label="姓名" />
+      <el-table-column prop="DEPARTMENT_NAME" label="部门" />
+      <el-table-column
+        prop="POST_NAME"
+        label="岗位"
+        width="150"
+        show-overflow-tooltip
+      />
+      <el-table-column prop="ROLE_NAME" label="角色" />
+    </layout-table>
+    <template #footer>
+      <el-button @click="fnClose">关闭</el-button>
+      <el-button type="primary" @click="fnSubmit">确定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { serialNumber } from "@/assets/js/utils.js";
+import LayoutDepartment from "@/components/department/index.vue";
+import useListData from "@/assets/js/useListData.js";
+import { getUserList } from "@/request/enterprise_management.js";
+import { useVModels } from "@vueuse/core";
+import { nextTick, watch } from "vue";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+  userList: {
+    type: Array,
+    required: true,
+    default: () => [],
+  },
+  userName: {
+    type: Array,
+    required: true,
+    default: () => [],
+  },
+});
+const emits = defineEmits([
+  "update:visible",
+  "update:userList",
+  "update:userName",
+]);
+const { visible, userList, userName } = useVModels(props, emits);
+const { list, pagination, fnGetData, fnResetPagination, tableRef, searchForm } =
+  useListData(getUserList, {
+    immediate: false,
+    clearSelection: false,
+    key: "userList",
+  });
+const fnInit = async () => {
+  await nextTick();
+  props.userList.forEach((item) => {
+    tableRef.value.toggleRowSelection(item);
+  });
+  list.value.length === 0 && fnResetPagination();
+};
+const fnClose = () => {
+  tableRef.value.clearSelection();
+  visible.value = false;
+};
+const fnSubmit = () => {
+  const selectionData = tableRef.value.getSelectionRows();
+  userList.value = selectionData;
+  userName.value = selectionData.map((item) => item.NAME);
+  fnClose();
+};
+watch(
+  () => props.visible,
+  (val) => {
+    if (val) {
+      fnInit();
+    }
+  }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/learning_garden/learning_garden/index.vue b/src/views/learning_garden/learning_garden/index.vue
new file mode 100644
index 0000000..251ecb6
--- /dev/null
+++ b/src/views/learning_garden/learning_garden/index.vue
@@ -0,0 +1,123 @@
+<template>
+  <div>
+    <el-card>
+      <el-form
+        :model="searchForm"
+        label-width="90px"
+        @submit.prevent="fnResetPagination"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="标题/内容" prop="KEYWORDS">
+              <el-input
+                v-model="searchForm.KEYWORDS"
+                placeholder="请输入关键字"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label-width="10px">
+              <el-button type="primary" native-type="submit">搜索</el-button>
+              <el-button native-type="reset" @click="fnResetPagination">
+                重置
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+    <layout-card>
+      <layout-table
+        :data="list"
+        v-model:pagination="pagination"
+        @get-data="fnGetData"
+      >
+        <el-table-column label="序号" width="70">
+          <template v-slot="{ $index }">
+            {{ serialNumber(pagination, $index) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="TYPENAME" label="类型" />
+        <el-table-column prop="TITLE" label="标题" />
+        <el-table-column prop="CONTENT" label="内容" />
+        <el-table-column label="附件路径">
+          <template v-slot="{ row }">
+            {{ row.NAME }}
+            <layout-multiple-attachment-previews :file-path="row.FILEPATH" />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="150">
+          <template v-slot="{ row }">
+            <el-button
+              v-if="buttonJurisdiction.add"
+              type="primary"
+              text
+              link
+              @click="
+                router.push({
+                  path: '/learning_garden/learning_garden/task_list',
+                  query: { STUDYSECTION_ID: row.STUDYSECTION_ID },
+                })
+              "
+            >
+              任务列表
+            </el-button>
+            <el-button
+              v-if="buttonJurisdiction.del"
+              type="primary"
+              text
+              link
+              @click="fnDelete(row.STUDYSECTION_ID)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+        <template #button>
+          <el-button
+            v-if="buttonJurisdiction.add"
+            type="primary"
+            @click="addDialogVisible = true"
+          >
+            新增
+          </el-button>
+        </template>
+      </layout-table>
+    </layout-card>
+    <add v-model:visible="addDialogVisible" @get-data="fnResetPagination" />
+  </div>
+</template>
+
+<script setup>
+import { serialNumber } from "@/assets/js/utils.js";
+import useListData from "@/assets/js/useListData.js";
+import { debounce } from "throttle-debounce";
+import { ElMessage, ElMessageBox } from "element-plus";
+import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
+import { useRouter } from "vue-router";
+import {
+  getLearningGardenList,
+  setLearningGardenDelete,
+} from "@/request/learning_garden.js";
+import LayoutMultipleAttachmentPreviews from "@/components/multiple_attachment_previews/index.vue";
+import Add from "./components/add.vue";
+import { ref } from "vue";
+
+const router = useRouter();
+const addDialogVisible = ref(false);
+const { list, pagination, searchForm, fnGetData, fnResetPagination } =
+  useListData(getLearningGardenList);
+const buttonJurisdiction = await useButtonJurisdiction("studysection");
+const fnDelete = debounce(
+  1000,
+  async (STUDYSECTION_ID) => {
+    await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
+    await setLearningGardenDelete({ STUDYSECTION_ID });
+    ElMessage.success("删除成功");
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped></style>
diff --git a/src/views/learning_garden/learning_garden/task_list.vue b/src/views/learning_garden/learning_garden/task_list.vue
new file mode 100644
index 0000000..4c23e1b
--- /dev/null
+++ b/src/views/learning_garden/learning_garden/task_list.vue
@@ -0,0 +1,212 @@
+<template>
+  <div>
+    <el-card>
+      <el-form
+        :model="searchForm"
+        label-width="50px"
+        @submit.prevent="fnResetPagination"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="名称" prop="KEYWORDS">
+              <el-input
+                v-model="searchForm.KEYWORDS"
+                placeholder="请输入关键字"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="状态">
+              <el-select v-model="searchForm.STATUS">
+                <el-option
+                  v-for="item in statusList"
+                  :key="item.ID"
+                  :value="item.ID"
+                  :label="item.NAME"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label-width="10px">
+              <el-button type="primary" native-type="submit">搜索</el-button>
+              <el-button native-type="reset" @click="fnResetPagination">
+                重置
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+    <layout-card>
+      <layout-table
+        ref="tableRef"
+        row-key="TEMPSTUDY_ID"
+        :data="list"
+        v-model:pagination="pagination"
+        @get-data="fnGetData"
+      >
+        <el-table-column reserve-selection type="selection" width="55" />
+        <el-table-column label="序号" width="70">
+          <template v-slot="{ $index }">
+            {{ serialNumber(pagination, $index) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="NAME" label="名称" />
+        <el-table-column label="课件类型">
+          <template v-slot="{ row }">
+            <span v-if="row.COURSEWARE_TYPE === '1'">视频课件</span>
+            <span v-if="row.COURSEWARE_TYPE === '2'">资料课件</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="COURSEWARE_NAME" label="课件" />
+        <el-table-column prop="STUDY_TYPE_NAME" label="学习类型" />
+        <el-table-column prop="USER_NUM" label="人数">
+          <template v-slot="{ row }">
+            <el-button
+              type="primary"
+              text
+              link
+              @click="fnLearningSituation(row.TEMPSTUDY_ID)"
+            >
+              {{ row.done_num }}/{{ row.USER_NUM }}
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态">
+          <template v-slot="{ row }">
+            {{ translationStatus(row.STATUS, statusList) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="150">
+          <template v-slot="{ row }">
+            <el-button
+              type="primary"
+              text
+              link
+              @click="
+                fnDisableOrEnable(row.TEMPSTUDY_ID, row.STATUS === 1 ? 0 : 1)
+              "
+            >
+              {{ row.STATUS === 1 ? "启用" : "禁用" }}
+            </el-button>
+            <el-button
+              v-if="buttonJurisdiction.del"
+              type="primary"
+              text
+              link
+              @click="fnDelete(row.TEMPSTUDY_ID)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+        <template #button>
+          <el-button
+            v-if="buttonJurisdiction.add"
+            type="primary"
+            @click="data.addDialogVisible = true"
+          >
+            新增
+          </el-button>
+          <el-button
+            v-if="buttonJurisdiction.del"
+            type="danger"
+            @click="fnBatchDelete"
+          >
+            批量删除
+          </el-button>
+        </template>
+      </layout-table>
+    </layout-card>
+    <learning-situation
+      v-model:visible="data.learningSituationDialog.visible"
+      :id="data.learningSituationDialog.TEMPSTUDY_ID"
+    />
+    <add-task
+      v-model:visible="data.addDialogVisible"
+      :id="STUDYSECTION_ID"
+      @get-data="fnResetPagination"
+    />
+  </div>
+</template>
+
+<script setup>
+import { serialNumber, translationStatus } from "@/assets/js/utils.js";
+import useListData from "@/assets/js/useListData.js";
+import { debounce } from "throttle-debounce";
+import { ElMessage, ElMessageBox } from "element-plus";
+import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
+import {
+  getLearningGardenTaskList,
+  setLearningGardenTaskBatchDelete,
+  setLearningGardenTaskDelete,
+  setLearningGardenTaskDisableOrEnable,
+} from "@/request/learning_garden.js";
+import { reactive } from "vue";
+import { useRoute } from "vue-router";
+import LearningSituation from "./components/learning_situation.vue";
+import AddTask from "./components/add_task.vue";
+
+const statusList = [
+  { NAME: "启用", ID: 0 },
+  { NAME: "禁用", ID: 1 },
+];
+const route = useRoute();
+const { STUDYSECTION_ID } = route.query;
+const { list, pagination, searchForm, fnGetData, fnResetPagination, tableRef } =
+  useListData(getLearningGardenTaskList, {
+    otherParams: { COURSEWARE_ID: STUDYSECTION_ID },
+  });
+const data = reactive({
+  learningSituationDialog: {
+    visible: false,
+    TEMPSTUDY_ID: "",
+  },
+  addDialogVisible: false,
+});
+const buttonJurisdiction = await useButtonJurisdiction("studysection");
+const fnDelete = debounce(
+  1000,
+  async (TEMPSTUDY_ID) => {
+    await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
+    await setLearningGardenTaskDelete({ TEMPSTUDY_ID });
+    ElMessage.success("删除成功");
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+const fnBatchDelete = debounce(
+  1000,
+  async () => {
+    const selectionData = tableRef.value.getSelectionRows();
+    if (selectionData.length === 0) {
+      ElMessage.warning("请选中要删除的项");
+      return;
+    }
+    await ElMessageBox.confirm("确定要删除选中的数据吗?", { type: "warning" });
+    const DATA_IDS = selectionData.map((item) => item.TEMPSTUDY_ID).join(",");
+    await setLearningGardenTaskBatchDelete({ DATA_IDS });
+    ElMessage.success("删除成功");
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+const fnDisableOrEnable = debounce(
+  1000,
+  async (TEMPSTUDY_ID, STATUS) => {
+    const message = STATUS === 1 ? "禁用" : "启用";
+    await ElMessageBox.confirm(`确定要${message}吗?`, { type: "warning" });
+    await setLearningGardenTaskDisableOrEnable({ TEMPSTUDY_ID, STATUS });
+    ElMessage.success(`${message}成功`);
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+const fnLearningSituation = (TEMPSTUDY_ID) => {
+  data.learningSituationDialog.TEMPSTUDY_ID = TEMPSTUDY_ID;
+  data.learningSituationDialog.visible = true;
+};
+</script>
+
+<style scoped></style>
diff --git a/src/views/learning_garden/regulatory_library/index.vue b/src/views/learning_garden/regulatory_library/index.vue
new file mode 100644
index 0000000..7577e57
--- /dev/null
+++ b/src/views/learning_garden/regulatory_library/index.vue
@@ -0,0 +1,125 @@
+<template>
+  <layout-card>
+    <layout-table
+      :data="list"
+      v-model:pagination="pagination"
+      @get-data="fnGetDataTransfer"
+    >
+      <el-table-column label="序号" width="60">
+        <template #default="{ $index }">
+          {{ serialNumber(pagination, $index) }}
+        </template>
+      </el-table-column>
+      <template v-if="levelType !== '2'">
+        <el-table-column label="目录名" width="220">
+          <template v-slot="{ row }">
+            <el-button
+              link
+              type="primary"
+              @click="
+                router.push({
+                  path: '/learning_garden/regulatory_library',
+                  query: {
+                    MFOLDER_ID: row.MFOLDER_ID,
+                  },
+                })
+              "
+            >
+              {{ row.NAME }}<el-icon><ArrowRight /></el-icon>
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column prop="CTIME" label="创建时间" />
+        <el-table-column label="是否共享">
+          <template v-slot="{ row }">
+            {{ row.SHARE === "no" ? "私有文件" : "公共文件" }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="REMARKS" label="备注说明" />
+      </template>
+      <template v-if="levelType === '2'">
+        <el-table-column label="文件名">
+          <template v-slot="{ row }">
+            {{ row.NAME }}
+            <layout-multiple-attachment-previews :file-path="row.FILEPATH" />
+          </template>
+        </el-table-column>
+        <el-table-column prop="POSTNO" label="发文号" />
+        <el-table-column label="文件大小" width="100">
+          <template v-slot="{ row }">
+            {{ calculateFileSize(row.FILESIZE) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="CTIME" label="上传时间" width="150" />
+        <el-table-column prop="STARTTIME" label="颁布日期" width="150" />
+        <el-table-column prop="UNIT" label="颁布单位" />
+        <el-table-column prop="ENDTIME" label="实施日期" width="150" />
+        <el-table-column label="时效状态" width="100">
+          <template v-slot="{ row }">
+            {{ row.ISVALID === "0" ? "无效" : "有效" }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="80">
+          <template v-slot="{ row }">
+            <el-button
+              v-show="MFOLDER_ID !== '0'"
+              type="primary"
+              text
+              link
+              @click="useDownloadFile(row.FILEPATH)"
+            >
+              下载
+            </el-button>
+          </template>
+        </el-table-column>
+      </template>
+      <template #button>
+        <el-button @click="router.back" v-if="MFOLDER_ID !== '0'">
+          返回
+        </el-button>
+      </template>
+    </layout-table>
+  </layout-card>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { serialNumber, calculateFileSize } from "@/assets/js/utils";
+import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
+import useListData from "@/assets/js/useListData.js";
+import { ArrowRight } from "@element-plus/icons-vue";
+import useDownloadFile from "@/assets/js/useDownloadFile.js";
+import LayoutMultipleAttachmentPreviews from "@/components/multiple_attachment_previews/index.vue";
+import { getRegulatoryLibraryList } from "@/request/learning_garden.js";
+
+const router = useRouter();
+const route = useRoute();
+const mfolderIdDefault = "0";
+const MFOLDER_ID = ref(route.query.MFOLDER_ID || mfolderIdDefault);
+const levelType = ref("0");
+const { list, pagination, fnGetData, fnResetPagination } = useListData(
+  getRegulatoryLibraryList,
+  {
+    otherParams: { MFOLDER_ID: MFOLDER_ID.value, SHARE: "yes" },
+    callbackFn: (list) => {
+      levelType.value = list?.[0]?.TYPE || "0";
+    },
+  }
+);
+const fnGetDataTransfer = () => {
+  fnGetData({
+    MFOLDER_ID: MFOLDER_ID.value,
+  });
+};
+const fnResetPaginationTransfer = () => {
+  fnResetPagination({
+    MFOLDER_ID: MFOLDER_ID.value,
+  });
+};
+onBeforeRouteUpdate((to) => {
+  MFOLDER_ID.value = to.query.MFOLDER_ID || mfolderIdDefault;
+  fnResetPaginationTransfer();
+});
+</script>
+
+<style scoped></style>
diff --git a/src/views/online_learn_exam/my_task/index.vue b/src/views/online_learn_exam/my_task/index.vue
index 7176b5c..1029f9d 100644
--- a/src/views/online_learn_exam/my_task/index.vue
+++ b/src/views/online_learn_exam/my_task/index.vue
@@ -178,15 +178,15 @@ const { list, pagination, searchForm, fnGetData, fnResetPagination } =
   useListData(getMyTaskList, {
     callbackFn: (list) => {
       for (let i = 0; i < list.length; i++) {
-        if (dayjs(list[i].PEIXUE_START_TIME).diff(dayjs(), "day") > 0)
-          list[i].kaishiState = "1";
-        if (dayjs(list[i].PEIXUE_END_TIME).diff(dayjs(), "day") <= 0)
-          list[i].kaishiState = "3";
         if (
           dayjs(list[i].PEIXUE_START_TIME).diff(dayjs(), "day") <= 0 &&
           dayjs(list[i].PEIXUE_END_TIME).diff(dayjs(), "day") >= 0
         )
           list[i].kaishiState = "2";
+        else if (dayjs(list[i].PEIXUE_END_TIME).diff(dayjs(), "day") <= 0)
+          list[i].kaishiState = "3";
+        else if (dayjs(list[i].PEIXUE_START_TIME).diff(dayjs(), "day") > 0)
+          list[i].kaishiState = "1";
         let canExam = "0";
         if (parseFloat(list[i].STAGEEXAMSCORE) >= parseFloat(list[i].PASSSCORE))
           canExam = "0";
diff --git a/src/views/online_learn_exam/task/index.vue b/src/views/online_learn_exam/task/index.vue
index 3b0238d..0468afc 100644
--- a/src/views/online_learn_exam/task/index.vue
+++ b/src/views/online_learn_exam/task/index.vue
@@ -107,11 +107,13 @@
             </span>
             <span
               class="text-green"
-              v-if="dayjs(row.PEIXUE_END_TIME).diff(dayjs(), 'day') <= 0"
+              v-else-if="dayjs(row.PEIXUE_END_TIME).diff(dayjs(), 'day') <= 0"
             >
               已结束
             </span>
-            <span v-if="dayjs(row.PEIXUE_START_TIME).diff(dayjs(), 'day') > 0">
+            <span
+              v-else-if="dayjs(row.PEIXUE_START_TIME).diff(dayjs(), 'day') > 0"
+            >
               未开始
             </span>
           </template>
diff --git a/src/views/security_investment/extraction_and_use/components/add.vue b/src/views/security_investment/extraction_and_use/components/add.vue
new file mode 100644
index 0000000..d8cb207
--- /dev/null
+++ b/src/views/security_investment/extraction_and_use/components/add.vue
@@ -0,0 +1,131 @@
+<template>
+  <el-dialog v-model="visible" title="新增" :on-close="fnClose">
+    <div>
+      <el-form ref="formRef" :rules="rules" :model="form" label-width="80px">
+        <el-row :gutter="12">
+          <el-col :span="12">
+            <el-form-item label="时间" prop="USE_DATE">
+              <el-date-picker
+                v-model="form.USE_DATE"
+                format="YYYY-MM-DD"
+                value-format="YYYY-MM-DD"
+                type="date"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="12">
+          <el-col :span="12">
+            <el-form-item label="使用类型" prop="USE_TYPE">
+              <el-select v-model="form.USE_TYPE">
+                <el-option value="1" label="预提" />
+                <el-option value="2" label="支出" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col v-if="form.USE_TYPE === '1'" :span="12">
+            <el-form-item label="金额(元)" prop="AMOUNT">
+              <el-input-number
+                v-model="form.AMOUNT"
+                :min="1"
+                precision="2"
+                controls-position="right"
+              />
+            </el-form-item>
+          </el-col>
+          <template v-if="form.USE_TYPE === '2'">
+            <el-col :span="12">
+              <el-form-item label="用途摘要" prop="PURPOSESUMMARY">
+                <el-input v-model="form.PURPOSESUMMARY" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="费用项目" prop="TYPE">
+                <el-select v-model="form.TYPE">
+                  <el-option
+                    v-for="(item, index) in costItems"
+                    :key="index"
+                    :value="item"
+                    :label="item"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="金额(元)" prop="AMOUNT">
+                <el-input-number
+                  v-model="form.AMOUNT"
+                  :min="1"
+                  precision="2"
+                  controls-position="right"
+                />
+              </el-form-item>
+            </el-col>
+          </template>
+        </el-row>
+      </el-form>
+    </div>
+    <template #footer>
+      <el-button @click="fnClose">取消</el-button>
+      <el-button type="primary" @click="fnSubmit"> 确 定 </el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useVModel } from "@vueuse/core";
+import { ref } from "vue";
+import { debounce } from "throttle-debounce";
+import useFormValidate from "@/assets/js/useFormValidate.js";
+import { ElMessage } from "element-plus";
+import { setExtractionAndUseAdd } from "@/request/security_investment.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+  costItems: {
+    type: Array,
+    required: true,
+    default: () => [],
+  },
+});
+const emits = defineEmits(["update:visible", "get-data"]);
+const visible = useVModel(props, "visible", emits);
+const rules = {
+  USE_DATE: [{ required: true, message: "请选择使用时间", trigger: "change" }],
+  USE_TYPE: [{ required: true, message: "请选择使用类型", trigger: "change" }],
+  AMOUNT: [{ required: true, message: "金额不能为空", trigger: "blur" }],
+  PURPOSESUMMARY: [
+    { required: true, message: "用途摘要不能为空", trigger: "blur" },
+  ],
+  TYPE: [{ required: true, message: "费用项目不能为空", trigger: "change" }],
+};
+const formRef = ref(null);
+const form = ref({
+  USE_DATE: "",
+  USE_TYPE: "",
+  AMOUNT: "",
+  PURPOSESUMMARY: "",
+  TYPE: "",
+});
+const fnClose = () => {
+  formRef.value.resetFields();
+  visible.value = false;
+};
+const fnSubmit = debounce(
+  1000,
+  async () => {
+    await useFormValidate(formRef);
+    await setExtractionAndUseAdd({ ...form.value });
+    ElMessage.success("新增成功");
+    fnClose();
+    emits("get-data");
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/security_investment/extraction_and_use/index.vue b/src/views/security_investment/extraction_and_use/index.vue
new file mode 100644
index 0000000..a9aedc5
--- /dev/null
+++ b/src/views/security_investment/extraction_and_use/index.vue
@@ -0,0 +1,220 @@
+<template>
+  <div>
+    <el-card>
+      <el-form
+        :model="searchForm"
+        label-width="90px"
+        @submit.prevent="fnGetData"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="计划年份" prop="YEAR">
+              <el-select v-model="searchForm.YEAR">
+                <el-option
+                  v-for="item in yearList"
+                  :key="item"
+                  :label="item"
+                  :value="item"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label-width="10px">
+              <el-button type="primary" native-type="submit">搜索</el-button>
+              <el-button native-type="reset" @click="fnGetData">
+                重置
+              </el-button>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item class="end">
+              <el-button v-print="'#printContent'"> 打印 </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+    <layout-card>
+      <layout-table
+        ref="tableRef"
+        row-key="SAFETYINVESTMENTUSE_ID"
+        :data="list"
+        :show-pagination="false"
+        show-summary
+        :summary-method="fnGetSummaries"
+      >
+        <el-table-column reserve-selection type="selection" width="55" />
+        <el-table-column label="序号" width="60" type="index" />
+        <el-table-column prop="USE_DATE" label="日期" />
+        <el-table-column label="预提额">
+          <template v-slot="{ row }">
+            <span v-if="row.USE_TYPE === '1'">{{ row.AMOUNT }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="用途摘要">
+          <template v-slot="{ row }">
+            <span v-if="row.USE_TYPE === '2'">{{ row.PURPOSESUMMARY }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="费用项目">
+          <el-table-column
+            v-for="(item, index) in costItems"
+            :key="index"
+            :label="item"
+          >
+            <template v-slot="{ row }">
+              <span v-if="row.TYPE === item">
+                {{ row.AMOUNT }}
+              </span>
+            </template>
+          </el-table-column>
+        </el-table-column>
+        <el-table-column prop="TOTAL" label="余额" />
+        <template #button>
+          <el-button type="primary" @click="addDialogVisible = true">
+            新增
+          </el-button>
+          <el-button type="danger" @click="fnDelete">删除</el-button>
+        </template>
+      </layout-table>
+      <div id="printContent">
+        <table class="print_use">
+          <thead>
+            <tr>
+              <th rowspan="2" style="width: 55px">日期</th>
+              <th rowspan="2" style="width: 70px">预提额</th>
+              <th rowspan="2" style="width: 85px">用途摘要</th>
+              <th colspan="10" class="tc">费用项目</th>
+              <th rowspan="2" style="width: 55px">余额</th>
+            </tr>
+            <tr>
+              <th v-for="(item, index) in costItems" :key="index">
+                {{ item }}
+              </th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="(item, index) in list" :key="index">
+              <td>{{ item.USE_DATE }}</td>
+              <td>{{ item.USE_TYPE === "1" ? item.AMOUNT : "" }}</td>
+              <td>{{ item.USE_TYPE === "2" ? item.PURPOSESUMMARY : "" }}</td>
+              <td v-for="(item1, index1) in costItems" :key="index1">
+                {{ item.TYPE === item1 ? item.AMOUNT : "" }}
+              </td>
+              <td>{{ item.TOTAL }}</td>
+            </tr>
+          </tbody>
+        </table>
+        <div v-html="PRINT_STYLE" />
+      </div>
+    </layout-card>
+    <add
+      v-model:visible="addDialogVisible"
+      :cost-items="costItems"
+      @get-data="fnGetData"
+    />
+  </div>
+</template>
+
+<script setup>
+import useListData from "@/assets/js/useListData.js";
+import {
+  getExtractionAndUseList,
+  setExtractionAndUseBatchDelete,
+} from "@/request/security_investment.js";
+import { debounce } from "throttle-debounce";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { ref } from "vue";
+import Add from "./components/add.vue";
+import { PRINT_STYLE } from "@/assets/js/constant.js";
+
+const yearList = (() => {
+  const year = new Date().getFullYear();
+  const arr = [];
+  for (let i = 2021; i <= year + 1; i++) {
+    arr.push(i);
+  }
+  return arr;
+})();
+const costItems = [
+  "完善、改造和维护安全防护设备设施和器具",
+  "安全生产宣传、教育培训、技术研究和推广",
+  "安全评价、标准化建设等安全技术服务",
+  "安全生产风险因素辨识管控和事故隐患排查治理",
+  "重大危险源监控、设备设施安全性能检测检验",
+  "劳动防护用品配备、更换和安全生产津贴、奖金发放",
+  "应急管理、事故救援演练以及救援队伍建设",
+  "安全标志及标识",
+  "配备劳动防护用品",
+  "其它与安全生产直接相关的物品或者活动",
+];
+const addDialogVisible = ref(false);
+const { list, searchForm, fnGetData, tableRef } = useListData(
+  getExtractionAndUseList,
+  {
+    usePagination: false,
+    defaultSearchForm: {
+      YEAR: new Date().getFullYear(),
+    },
+    callbackFn: (list) => {
+      for (let i = 0; i < list.length; i++) {
+        if (i === 0) {
+          if (list[i].USE_TYPE === "1") list[i].TOTAL = list[i].AMOUNT;
+          else list[i].TOTAL = 0 - list[i].AMOUNT;
+        } else {
+          if (list[i].USE_TYPE === "1")
+            list[i].TOTAL = list[i - 1].TOTAL + list[i].AMOUNT;
+          else list[i].TOTAL = list[i - 1].TOTAL - list[i].AMOUNT;
+        }
+      }
+    },
+  }
+);
+const fnGetSummaries = ({ columns, data }) => {
+  const sums = [];
+  columns.forEach((column, index) => {
+    if (index === 0) {
+      sums[index] = "合计";
+      return;
+    }
+    if (index !== columns.length - 1) {
+      sums[index] = "--";
+      return;
+    }
+    const values = data.map((item) => Number(item[column.property]));
+    if (!values.every((value) => isNaN(value))) {
+      sums[index] = values.reduce((prev, curr) => {
+        const value = Number(curr);
+        if (!isNaN(value)) {
+          return prev + curr;
+        } else {
+          return prev;
+        }
+      }, 0);
+      sums[index] = "--";
+    }
+  });
+  return sums;
+};
+const fnDelete = debounce(
+  1000,
+  async () => {
+    const selectionData = tableRef.value.getSelectionRows();
+    if (selectionData.length === 0) {
+      ElMessage.warning("请选中要删除的项");
+      return;
+    }
+    await ElMessageBox.confirm("确定要删除选中的数据吗?", { type: "warning" });
+    const DATA_IDS = selectionData
+      .map((item) => item.SAFETYINVESTMENTUSE_ID)
+      .join(",");
+    await setExtractionAndUseBatchDelete({ DATA_IDS });
+    ElMessage.success("删除成功");
+    fnGetData();
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped></style>
diff --git a/src/views/security_investment/plan/components/add.vue b/src/views/security_investment/plan/components/add.vue
new file mode 100644
index 0000000..4d3a888
--- /dev/null
+++ b/src/views/security_investment/plan/components/add.vue
@@ -0,0 +1,227 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    :title="type === 'add' ? '新增' : '修改'"
+    :on-close="fnClose"
+    width="1500"
+  >
+    <el-form ref="formRef" :rules="rules" :model="form" label-width="110px">
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="计划名称" prop="PLAN_NAME">
+            <el-input v-model="form.PLAN_NAME" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="计划年份" prop="PLAN_YEAR">
+            <el-select v-model="form.PLAN_YEAR">
+              <el-option
+                v-for="item in yearList"
+                :key="item"
+                :label="item"
+                :value="item"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="计划周期" prop="PLAN_PERIOD">
+            <el-select v-model="form.PLAN_PERIOD">
+              <el-option
+                v-for="item in planPeriodList"
+                :key="item.ID"
+                :value="item.ID"
+                :label="item.NAME"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <template
+          v-for="(item, index) in form.infos"
+          :key="item.id || item.SAFETYINVESTMENTPLANINFO_ID"
+        >
+          <el-col :span="8">
+            <el-form-item
+              :label="'费用类型' + (index + 1)"
+              :prop="'infos.' + index + '.TYPE'"
+              :rules="{ required: true, message: '请选择费用类型' }"
+            >
+              <el-select v-model="item.TYPE">
+                <el-option
+                  v-for="item in typeList"
+                  :key="item"
+                  :label="item"
+                  :value="item"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item
+              :label="'明细' + (index + 1)"
+              :prop="'infos.' + index + '.DESCR'"
+              :rules="{
+                required: true,
+                message: '请输入明细',
+                trigger: 'blur',
+              }"
+            >
+              <el-input v-model="item.DESCR" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item
+              :label="'金额' + (index + 1)"
+              :prop="'infos.' + index + '.AMOUNT'"
+              :rules="[
+                {
+                  required: true,
+                  message: '请输入金额',
+                  trigger: 'blur',
+                },
+                {
+                  type: 'number',
+                  message: '请输入数字',
+                  trigger: 'blur',
+                },
+              ]"
+            >
+              <el-input
+                v-model.number="item.AMOUNT"
+                @input="fnCalculateTotalAmount"
+              >
+                <template v-slot:append>元</template>
+              </el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="2">
+            <el-form-item label-width="0" class="end">
+              <el-button v-show="index === 0" type="primary" @click="fnAddInfo">
+                添加使用明细
+              </el-button>
+              <el-button
+                v-show="index > 0"
+                type="danger"
+                @click="fnRemoveInfo(index)"
+              >
+                删除使用明细
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </template>
+        <el-col :span="24">
+          <el-form-item label="总金额" prop="TOTAL_AMOUNT">
+            <el-input
+              v-model="form.TOTAL_AMOUNT"
+              placeholder="这里输入总金额..."
+              readonly
+            >
+              <template v-slot:append>元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="fnClose">取 消</el-button>
+      <el-button type="primary" @click="fnSubmit">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useVModels } from "@vueuse/core";
+import { ref } from "vue";
+import { debounce } from "throttle-debounce";
+import useFormValidate from "@/assets/js/useFormValidate.js";
+import { ElMessage } from "element-plus";
+import { sumBy } from "lodash-es";
+import { setPlanAdd, setPlanEdit } from "@/request/security_investment.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+  form: {
+    type: Object,
+    required: true,
+    default: () => ({}),
+  },
+  type: {
+    type: String,
+    required: true,
+    default: "add",
+  },
+  yearList: {
+    type: Array,
+    required: true,
+    default: () => [],
+  },
+  planPeriodList: {
+    type: Array,
+    required: true,
+    default: () => [],
+  },
+});
+const emits = defineEmits(["update:visible", "update:form", "get-data"]);
+const { visible, form } = useVModels(props, emits);
+const typeList = [
+  "完善、改造和维护安全防护设备设施",
+  "安全生产教育培训教育",
+  "安全评价、重大危险源监控、事故隐患评估和整改",
+  "设备设施安全性能检测检验",
+  "应急救援器材、装备的配备及应急救援演练",
+  "安全标志及标识",
+  "配备劳动防护用品",
+  "他与安全生产直接相关的物品或者活动",
+];
+const rules = {
+  PLAN_NAME: [
+    { required: true, message: "计划名称不能为空", trigger: "change" },
+  ],
+  PLAN_YEAR: [
+    { required: true, message: "计划年份不能为空", trigger: "change" },
+  ],
+  PLAN_PERIOD: [
+    { required: true, message: "计划周期不能为空", trigger: "blur" },
+  ],
+  TOTAL_AMOUNT: [
+    { required: true, message: "总金额不能为空", trigger: "blur" },
+  ],
+};
+const formRef = ref(null);
+const fnAddInfo = () => {
+  form.value.infos.push({ TYPE: "", DESCR: "", AMOUNT: 0, id: Math.random() });
+};
+const fnRemoveInfo = (index) => {
+  form.value.infos.splice(index, 1);
+  fnCalculateTotalAmount();
+};
+const fnCalculateTotalAmount = () => {
+  form.value.TOTAL_AMOUNT = sumBy(form.value.infos, (item) => item.AMOUNT);
+};
+const fnClose = () => {
+  formRef.value.resetFields();
+  visible.value = false;
+};
+const fnSubmit = debounce(
+  1000,
+  async () => {
+    await useFormValidate(formRef);
+    const params = {
+      ...form.value,
+      STATUS: 0,
+      info: JSON.stringify(form.value.infos),
+    };
+    props.type === "add" ? await setPlanAdd(params) : await setPlanEdit(params);
+    ElMessage.success("操作成功");
+    fnClose();
+    emits("get-data");
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/security_investment/plan/components/view.vue b/src/views/security_investment/plan/components/view.vue
new file mode 100644
index 0000000..419649a
--- /dev/null
+++ b/src/views/security_investment/plan/components/view.vue
@@ -0,0 +1,80 @@
+<template>
+  <el-dialog v-model="visible" title="查看" width="1500">
+    <el-descriptions :column="3" border>
+      <el-descriptions-item label="计划名称">
+        {{ info.PLAN_NAME }}
+      </el-descriptions-item>
+      <el-descriptions-item label="计划年份">
+        {{ info.PLAN_YEAR }}
+      </el-descriptions-item>
+      <el-descriptions-item label="计划周期">
+        {{ info.PLAN_PERIOD }}
+      </el-descriptions-item>
+      <template v-for="(item, index) in info.infos" :key="index">
+        <el-descriptions-item label="费用类型">
+          {{ item.TYPE }}
+        </el-descriptions-item>
+        <el-descriptions-item label="明细">
+          {{ item.DESCR }}
+        </el-descriptions-item>
+        <el-descriptions-item label="金额">
+          {{ item.AMOUNT }}元
+        </el-descriptions-item>
+      </template>
+      <el-descriptions-item label="总金额">
+        {{ info.TOTAL_AMOUNT }}元
+      </el-descriptions-item>
+    </el-descriptions>
+    <template #footer>
+      <template v-if="type === 'review'">
+        <el-button type="primary" @click="fnSubmit(1)">通过</el-button>
+        <el-button type="danger" @click="fnSubmit(-1)">打回</el-button>
+      </template>
+      <el-button @click="fnClose">关闭</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useVModel } from "@vueuse/core";
+import { setPlanReview } from "@/request/security_investment.js";
+import { debounce } from "throttle-debounce";
+import { ElMessage } from "element-plus";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({}),
+  },
+  type: {
+    type: String,
+    default: "view",
+  },
+});
+const emits = defineEmits(["update:visible", "get-data"]);
+const visible = useVModel(props, "visible", emits);
+const fnClose = () => {
+  visible.value = false;
+};
+const fnSubmit = debounce(
+  1000,
+  async (STATUS) => {
+    await setPlanReview({
+      SAFETYINVESTMENTPLAN_ID: props.info.SAFETYINVESTMENTPLAN_ID,
+      STATUS,
+    });
+    ElMessage.success(`${STATUS === 1 ? "通过" : "打回"}成功`);
+    fnClose();
+    emits("get-data");
+  },
+  { atBegin: true }
+);
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/security_investment/plan/index.vue b/src/views/security_investment/plan/index.vue
new file mode 100644
index 0000000..e923af3
--- /dev/null
+++ b/src/views/security_investment/plan/index.vue
@@ -0,0 +1,262 @@
+<template>
+  <div>
+    <el-card>
+      <el-form
+        :model="searchForm"
+        label-width="90px"
+        @submit.prevent="fnResetPagination"
+      >
+        <el-row>
+          <el-col :span="5">
+            <el-form-item label="计划名称" prop="KEYWORDS">
+              <el-input v-model="searchForm.KEYWORDS" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="计划年份" prop="PLAN_YEAR">
+              <el-select v-model="searchForm.PLAN_YEAR">
+                <el-option
+                  v-for="item in yearList"
+                  :key="item"
+                  :label="item"
+                  :value="item"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="计划周期" prop="PLAN_PERIOD">
+              <el-select v-model="searchForm.PLAN_PERIOD">
+                <el-option
+                  v-for="item in planPeriodList"
+                  :key="item.ID"
+                  :value="item.ID"
+                  :label="item.NAME"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="状态" prop="STATUS">
+              <el-select v-model="searchForm.STATUS">
+                <el-option
+                  v-for="item in statusList"
+                  :key="item.ID"
+                  :value="item.ID"
+                  :label="item.NAME"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="4">
+            <el-form-item label-width="10px">
+              <el-button type="primary" native-type="submit">搜索</el-button>
+              <el-button native-type="reset" @click="fnResetPagination">
+                重置
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+    <layout-card>
+      <layout-table
+        ref="tableRef"
+        row-key="SAFETYINVESTMENTPLAN_ID"
+        :data="list"
+        @get-data="fnGetData"
+        v-model:pagination="pagination"
+      >
+        <el-table-column reserve-selection type="selection" width="55" />
+        <el-table-column label="序号" width="60">
+          <template #default="{ $index }">
+            {{ serialNumber(pagination, $index) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="PLAN_NAME" label="计划名称" />
+        <el-table-column prop="PLAN_YEAR" label="计划年份" />
+        <el-table-column prop="PLAN_PERIOD" label="计划周期" />
+        <el-table-column prop="INFO_COUNT" label="涉及费用项数" />
+        <el-table-column prop="TOTAL_AMOUNT" label="总金额" />
+        <el-table-column prop="CREATOR" label="编制人" />
+        <el-table-column label="审核人">
+          <template v-slot="{ row }">
+            <span v-if="row.STATUS !== 0">{{ row.OPERATOR }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态">
+          <template v-slot="{ row }">
+            {{ translationStatus(row.STATUS, statusList) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="150">
+          <template v-slot="{ row }">
+            <el-button
+              type="primary"
+              text
+              link
+              @click="fnView(row.SAFETYINVESTMENTPLAN_ID)"
+            >
+              查看
+            </el-button>
+            <el-button
+              v-if="buttonJurisdiction.del && row.STATUS < 1"
+              type="primary"
+              text
+              link
+              @click="fnDelete(row.SAFETYINVESTMENTPLAN_ID)"
+            >
+              删除
+            </el-button>
+            <el-button
+              v-if="buttonJurisdiction.edit && row.STATUS < 1"
+              type="primary"
+              text
+              link
+              @click="fnAddOrEdit(row.SAFETYINVESTMENTPLAN_ID, 'edit')"
+            >
+              编辑
+            </el-button>
+          </template>
+        </el-table-column>
+        <template #button>
+          <el-button
+            v-if="buttonJurisdiction.add"
+            type="primary"
+            @click="fnAddOrEdit('', 'add')"
+          >
+            新增
+          </el-button>
+          <el-button
+            v-if="buttonJurisdiction.del"
+            type="danger"
+            @click="fnBatchDelete"
+          >
+            批量删除
+          </el-button>
+        </template>
+      </layout-table>
+    </layout-card>
+    <view-info
+      v-model:visible="data.viewDialog.visible"
+      :info="data.viewDialog.info"
+    />
+    <add
+      v-model:visible="data.addDialog.visible"
+      v-model:form="data.addDialog.form"
+      :type="data.addDialog.type"
+      :year-list="yearList"
+      :plan-period-list="planPeriodList"
+      @get-data="fnResetPagination"
+    />
+  </div>
+</template>
+
+<script setup>
+import { serialNumber, translationStatus } from "@/assets/js/utils";
+import useListData from "@/assets/js/useListData.js";
+import { debounce } from "throttle-debounce";
+import { ElMessage, ElMessageBox } from "element-plus";
+import {
+  getPlanList,
+  getPlanView,
+  setPlanBatchDelete,
+  setPlanDelete,
+} from "@/request/security_investment.js";
+import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
+import ViewInfo from "./components/view.vue";
+import add from "./components/add.vue";
+import { nextTick, reactive } from "vue";
+
+const yearList = (() => {
+  const year = new Date().getFullYear();
+  const arr = [];
+  for (let i = 2021; i <= year + 1; i++) {
+    arr.push(i);
+  }
+  return arr;
+})();
+const planPeriodList = [
+  { NAME: "季度计划1", ID: "季度计划1" },
+  { NAME: "季度计划2", ID: "季度计划2" },
+  { NAME: "季度计划3", ID: "季度计划3" },
+  { NAME: "季度计划4", ID: "季度计划4" },
+];
+const statusList = [
+  { NAME: "待审核", ID: 0 },
+  { NAME: "已归档", ID: 1 },
+  { NAME: "已打回", ID: -1 },
+];
+const { list, pagination, searchForm, fnGetData, fnResetPagination, tableRef } =
+  useListData(getPlanList);
+const data = reactive({
+  viewDialog: {
+    visible: false,
+    info: {},
+  },
+  addDialog: {
+    visible: false,
+    type: "",
+    form: {
+      PLAN_NAME: "",
+      PLAN_YEAR: "",
+      PLAN_PERIOD: "",
+      TOTAL_AMOUNT: 0,
+      infos: [],
+    },
+  },
+});
+const buttonJurisdiction = await useButtonJurisdiction("safetyinvestmentplan");
+const fnDelete = debounce(
+  1000,
+  async (SAFETYINVESTMENTPLAN_ID) => {
+    await ElMessageBox.confirm(`确定要删除吗?`, {
+      type: "warning",
+    });
+    await setPlanDelete({ SAFETYINVESTMENTPLAN_ID });
+    ElMessage.success(`删除成功`);
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+const fnBatchDelete = debounce(
+  1000,
+  async () => {
+    const selectionData = tableRef.value.getSelectionRows();
+    if (selectionData.length === 0) {
+      ElMessage.warning("请选中要删除的项");
+      return;
+    }
+    await ElMessageBox.confirm("确定要删除选中的数据吗?", { type: "warning" });
+    const DATA_IDS = selectionData
+      .map((item) => item.SAFETYINVESTMENTPLAN_ID)
+      .join(",");
+    await setPlanBatchDelete({ DATA_IDS });
+    ElMessage.success("删除成功");
+    fnResetPagination();
+  },
+  { atBegin: true }
+);
+const fnView = async (SAFETYINVESTMENTPLAN_ID) => {
+  const resData = await getPlanView({ SAFETYINVESTMENTPLAN_ID });
+  data.viewDialog.info = resData.pd;
+  data.viewDialog.info.infos = resData.infoList;
+  data.viewDialog.visible = true;
+};
+const fnAddOrEdit = async (SAFETYINVESTMENTPLAN_ID, type) => {
+  data.addDialog.visible = true;
+  await nextTick();
+  data.addDialog.type = type;
+  if (type === "edit") {
+    const resData = await getPlanView({ SAFETYINVESTMENTPLAN_ID });
+    data.addDialog.form = resData.pd;
+    data.addDialog.form.infos = resData.infoList;
+  } else {
+    data.addDialog.form.infos = [
+      { TYPE: "", DESCR: "", AMOUNT: 0, id: Math.random() },
+    ];
+  }
+};
+</script>
+
+<style scoped></style>
diff --git a/src/views/security_investment/plan_review/index.vue b/src/views/security_investment/plan_review/index.vue
new file mode 100644
index 0000000..8bf325d
--- /dev/null
+++ b/src/views/security_investment/plan_review/index.vue
@@ -0,0 +1,155 @@
+<template>
+  <div>
+    <el-card>
+      <el-form
+        :model="searchForm"
+        label-width="90px"
+        @submit.prevent="fnResetPagination"
+      >
+        <el-row>
+          <el-col :span="5">
+            <el-form-item label="计划名称" prop="KEYWORDS">
+              <el-input v-model="searchForm.KEYWORDS" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="计划年份" prop="PLAN_YEAR">
+              <el-select v-model="searchForm.PLAN_YEAR">
+                <el-option
+                  v-for="item in yearList"
+                  :key="item"
+                  :label="item"
+                  :value="item"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="计划周期" prop="PLAN_PERIOD">
+              <el-select v-model="searchForm.PLAN_PERIOD">
+                <el-option
+                  v-for="item in planPeriodList"
+                  :key="item.ID"
+                  :value="item.ID"
+                  :label="item.NAME"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="状态" prop="STATUS">
+              <el-select v-model="searchForm.STATUS">
+                <el-option
+                  v-for="item in statusList"
+                  :key="item.ID"
+                  :value="item.ID"
+                  :label="item.NAME"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="4">
+            <el-form-item label-width="10px">
+              <el-button type="primary" native-type="submit">搜索</el-button>
+              <el-button native-type="reset" @click="fnResetPagination">
+                重置
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+    <layout-card>
+      <layout-table
+        :data="list"
+        @get-data="fnGetData"
+        v-model:pagination="pagination"
+      >
+        <el-table-column label="序号" width="60">
+          <template #default="{ $index }">
+            {{ serialNumber(pagination, $index) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="PLAN_NAME" label="计划名称" />
+        <el-table-column prop="PLAN_YEAR" label="计划年份" />
+        <el-table-column prop="PLAN_PERIOD" label="计划周期" />
+        <el-table-column prop="INFO_COUNT" label="涉及费用项数" />
+        <el-table-column prop="TOTAL_AMOUNT" label="总金额" />
+        <el-table-column prop="CREATOR" label="编制人" />
+        <el-table-column label="审核人">
+          <template v-slot="{ row }">
+            <span v-if="row.STATUS !== 0">{{ row.OPERATOR }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态">
+          <template v-slot="{ row }">
+            {{ translationStatus(row.STATUS, statusList) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="80">
+          <template v-slot="{ row }">
+            <el-button
+              v-if="row.STATUS === 0"
+              type="primary"
+              text
+              link
+              @click="fnReview(row.SAFETYINVESTMENTPLAN_ID)"
+            >
+              审核
+            </el-button>
+          </template>
+        </el-table-column>
+      </layout-table>
+    </layout-card>
+    <view-info
+      v-model:visible="data.reviewDialog.visible"
+      :info="data.reviewDialog.info"
+      type="review"
+      @get-data="fnResetPagination"
+    />
+  </div>
+</template>
+
+<script setup>
+import { serialNumber, translationStatus } from "@/assets/js/utils";
+import useListData from "@/assets/js/useListData.js";
+import { getPlanList, getPlanView } from "@/request/security_investment.js";
+import ViewInfo from "../plan/components/view.vue";
+import { reactive } from "vue";
+
+const yearList = (() => {
+  const year = new Date().getFullYear();
+  const arr = [];
+  for (let i = 2021; i <= year + 1; i++) {
+    arr.push(i);
+  }
+  return arr;
+})();
+const planPeriodList = [
+  { NAME: "季度计划1", ID: "季度计划1" },
+  { NAME: "季度计划2", ID: "季度计划2" },
+  { NAME: "季度计划3", ID: "季度计划3" },
+  { NAME: "季度计划4", ID: "季度计划4" },
+];
+const statusList = [
+  { NAME: "待审核", ID: 0 },
+  { NAME: "已归档", ID: 1 },
+  { NAME: "已打回", ID: -1 },
+];
+const { list, pagination, searchForm, fnGetData, fnResetPagination } =
+  useListData(getPlanList);
+const data = reactive({
+  reviewDialog: {
+    visible: false,
+    info: {},
+  },
+});
+const fnReview = async (SAFETYINVESTMENTPLAN_ID) => {
+  const resData = await getPlanView({ SAFETYINVESTMENTPLAN_ID });
+  data.reviewDialog.info = resData.pd;
+  data.reviewDialog.info.infos = resData.infoList;
+  data.reviewDialog.visible = true;
+};
+</script>
+
+<style scoped></style>
diff --git a/src/views/system_operation/system_documents/index.vue b/src/views/system_operation/system_documents/index.vue
index f3b73fc..d52e5b3 100644
--- a/src/views/system_operation/system_documents/index.vue
+++ b/src/views/system_operation/system_documents/index.vue
@@ -31,49 +31,7 @@
       <el-table-column v-if="MFOLDER_ID !== '0'" label="文件名">
         <template v-slot="{ row }">
           {{ row.NAME }}
-          <el-button
-            link
-            type="primary"
-            v-if="interceptTheSuffix(row.FILEPATH, '.txt')"
-            @click="fnPreviewTxt(row.FILEPATH)"
-          >
-            [预览]
-          </el-button>
-          <el-button
-            text
-            link
-            type="primary"
-            v-if="interceptTheSuffix(row.FILEPATH, '.pdf')"
-            @click="fnPreviewPdf(row.FILEPATH)"
-          >
-            [预览]
-          </el-button>
-          <el-button
-            link
-            type="primary"
-            v-if="interceptTheSuffix(row.FILEPATH, '.mp4')"
-            @click="fnPreviewVideo(row.FILEPATH)"
-          >
-            [预览]
-          </el-button>
-          <a
-            v-if="
-              interceptTheSuffix(row.FILEPATH, '.doc') ||
-              interceptTheSuffix(row.FILEPATH, '.xls') ||
-              interceptTheSuffix(row.FILEPATH, '.ppt') ||
-              interceptTheSuffix(row.FILEPATH, '.docx') ||
-              interceptTheSuffix(row.FILEPATH, '.xlsx') ||
-              interceptTheSuffix(row.FILEPATH, '.pptx')
-            "
-            :href="
-              'http://view.officeapps.live.com/op/view.aspx?src=' +
-              VITE_FILE_URL +
-              row.FILEPATH
-            "
-            target="_blank"
-          >
-            [预览]
-          </a>
+          <layout-multiple-attachment-previews :file-path="row.FILEPATH" />
         </template>
       </el-table-column>
       <el-table-column v-if="MFOLDER_ID === '0'" prop="num" label="文件数量" />
@@ -139,18 +97,6 @@
         </template>
       </template>
     </layout-table>
-    <layout-pdf
-      :src="data.pdfDialog.src"
-      v-model:visible="data.pdfDialog.visible"
-    />
-    <layout-txt
-      :src="data.txtDialog.src"
-      v-model:visible="data.txtDialog.visible"
-    />
-    <layout-video
-      :src="data.videoDialog.src"
-      v-model:visible="data.videoDialog.visible"
-    />
     <add-folder
       v-model:visible="data.addFolderDialogVisible"
       :mfolder-id="MFOLDER_ID"
@@ -175,14 +121,7 @@ import {
   setSystemDocumentsDelete,
 } from "@/request/system_operation.js";
 import { reactive, ref } from "vue";
-import LayoutPdf from "@/components/pdf/index";
-import LayoutTxt from "@/components/txt/index.vue";
-import LayoutVideo from "@/components/video/index";
-import {
-  serialNumber,
-  calculateFileSize,
-  interceptTheSuffix,
-} from "@/assets/js/utils";
+import { serialNumber, calculateFileSize } from "@/assets/js/utils";
 import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
 import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
 import useListData from "@/assets/js/useListData.js";
@@ -194,8 +133,8 @@ import useDownloadFile from "@/assets/js/useDownloadFile.js";
 import AddFolder from "./components/add_folder.vue";
 import UploadFolder from "./components/upload_file.vue";
 import BatchUploadFile from "./components/batch_upload_file.vue";
+import LayoutMultipleAttachmentPreviews from "@/components/multiple_attachment_previews/index.vue";
 
-const VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
 const router = useRouter();
 const route = useRoute();
 const userStore = useUserStore();
@@ -212,18 +151,6 @@ const { list, pagination, fnGetData, fnResetPagination } = useListData(
   }
 );
 const data = reactive({
-  pdfDialog: {
-    visible: false,
-    src: "",
-  },
-  videoDialog: {
-    visible: false,
-    src: "",
-  },
-  txtDialog: {
-    visible: false,
-    src: "",
-  },
   addFolderDialogVisible: false,
   uploadFileDialogVisible: false,
   batchUploadFileDialogVisible: false,
@@ -255,18 +182,6 @@ const fnDelete = debounce(
   },
   { atBegin: true }
 );
-const fnPreviewTxt = async (FILEPATH) => {
-  data.txtDialog.visible = true;
-  data.txtDialog.src = FILEPATH;
-};
-const fnPreviewPdf = (FILEPATH) => {
-  data.pdfDialog.visible = true;
-  data.pdfDialog.src = FILEPATH;
-};
-const fnPreviewVideo = (FILEPATH) => {
-  data.videoDialog.visible = true;
-  data.videoDialog.src = FILEPATH;
-};
 </script>
 
 <style scoped></style>
diff --git a/src/views/three_institutional_libraries/safety_production_responsibility_system/components/list.vue b/src/views/three_institutional_libraries/safety_production_responsibility_system/components/list.vue
new file mode 100644
index 0000000..084b0d4
--- /dev/null
+++ b/src/views/three_institutional_libraries/safety_production_responsibility_system/components/list.vue
@@ -0,0 +1,177 @@
+<template>
+  <div>
+    <el-form
+      :model="searchForm"
+      label-width="80px"
+      @submit.prevent="fnResetPaginationTransfer"
+    >
+      <el-row>
+        <el-col :span="6">
+          <el-form-item label="名称" prop="KEYWORDS">
+            <el-input
+              v-model="searchForm.KEYWORDS"
+              placeholder="请输入关键字"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="规程属性" prop="CATEGORY_LIST">
+            <layout-three-institutional-libraries
+              ref="regulationsRef"
+              v-model="searchForm.CATEGORY_LIST"
+              type="regulations"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="类型" prop="TYPES">
+            <layout-three-institutional-libraries
+              ref="typeRef"
+              v-model="searchForm.TYPES"
+              type="type"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item
+            label="国民经济行业类型"
+            prop="SPECIFICATION_TYPES"
+            label-width="130px"
+          >
+            <layout-three-institutional-libraries
+              ref="industryRef"
+              v-model="searchForm.SPECIFICATION_TYPES"
+              type="industry"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="标签" prop="LABELS">
+            <layout-three-institutional-libraries
+              ref="labelRef"
+              v-model="searchForm.LABELS"
+              type="label"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="企业" prop="CORPINFO_ID">
+            <el-select
+              v-model="searchForm.CORPINFO_ID"
+              :remote-method="fnRemoteMethod"
+              :loading="selectLoading"
+              filterable
+              remote
+              reserve-keyword
+              placeholder="请输入关键词"
+            >
+              <el-option
+                v-for="item in enterpriseList"
+                :key="item.CORPINFO_ID"
+                :label="item.CORP_NAME"
+                :value="item.CORPINFO_ID"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label-width="10px">
+            <el-button type="primary" native-type="submit">搜索</el-button>
+            <el-button native-type="reset" @click="fnResetPaginationTransfer">
+              重置
+            </el-button>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <layout-table
+      :data="list"
+      v-model:pagination="pagination"
+      @get-data="fnGetDataTransfer"
+    >
+      <el-table-column label="序号" width="70">
+        <template v-slot="{ $index }">
+          {{ serialNumber(pagination, $index) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="REMARKS" label="安全生产管理制度名称" />
+      <el-table-column label="类型">
+        <template v-slot="{ row }">
+          {{
+            row.TYPES.map((item) => item.CATEGORY_NAME)
+              .filter(Boolean)
+              .join("、")
+          }}
+        </template>
+      </el-table-column>
+      <el-table-column label="标签">
+        <template v-slot="{ row }">
+          {{
+            row.labels
+              .map((item) => item.NAME)
+              .filter(Boolean)
+              .join("、")
+          }}
+        </template>
+      </el-table-column>
+      <el-table-column label="国民经济行业类型">
+        <template v-slot="{ row }">
+          <span v-if="row.SPECIFICATION_TYPES?.length > 0">
+            {{
+              row.SPECIFICATION_TYPES.map((item) => item.CATEGORY_NAME)
+                .filter(Boolean)
+                .join("、")
+            }}
+          </span>
+          <span v-else>通用</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="UPLOAD_TIME" label="上传时间" width="150px" />
+    </layout-table>
+  </div>
+</template>
+
+<script setup>
+import { serialNumber } from "@/assets/js/utils.js";
+import useListData from "@/assets/js/useListData.js";
+import { getThreeInstitutionalLibrariesList } from "@/request/three_institutional_libraries.js";
+import LayoutThreeInstitutionalLibraries from "@/components/three_institutional_libraries/index.vue";
+import { ref } from "vue";
+import { getEnterpriseList } from "@/request/enterprise_management.js";
+import { throttle } from "throttle-debounce";
+
+const regulationsRef = ref(null);
+const typeRef = ref(null);
+const industryRef = ref(null);
+const labelRef = ref(null);
+const { list, pagination, searchForm, fnGetData, fnResetPagination } =
+  useListData(getThreeInstitutionalLibrariesList, {
+    otherParams: { ASSOCIATION: "2", ENTERPRISE_SIDE: "0" },
+  });
+const fnGetCheckedNodes = () => {
+  return {
+    CATEGORY_LIST: JSON.stringify(regulationsRef.value.getCheckedNodes()),
+    TYPES: JSON.stringify(typeRef.value.getCheckedNodes()),
+    SPECIFICATION_TYPES: JSON.stringify(industryRef.value.getCheckedNodes()),
+    LABELS: JSON.stringify(labelRef.value.getCheckedNodes()),
+  };
+};
+const fnGetDataTransfer = () => {
+  fnGetData({ ...fnGetCheckedNodes() });
+};
+const fnResetPaginationTransfer = () => {
+  fnResetPagination({ ...fnGetCheckedNodes() });
+};
+const selectLoading = ref(false);
+const enterpriseList = ref([]);
+const fnRemoteMethod = throttle(500, async (query) => {
+  if (query) {
+    selectLoading.value = true;
+    const resData = await getEnterpriseList({ KEYWORDS: query });
+    enterpriseList.value = resData.varList;
+    selectLoading.value = false;
+  }
+});
+</script>
+
+<style scoped></style>
diff --git a/src/views/three_institutional_libraries/safety_production_responsibility_system/index.vue b/src/views/three_institutional_libraries/safety_production_responsibility_system/index.vue
new file mode 100644
index 0000000..fe57496
--- /dev/null
+++ b/src/views/three_institutional_libraries/safety_production_responsibility_system/index.vue
@@ -0,0 +1,39 @@
+<template>
+  <layout-card>
+    <el-tabs v-model="name" @tab-change="fnTabChange">
+      <el-tab-pane label="安全生产责任制" name="responsibility_system" lazy>
+        <list-view />
+      </el-tab-pane>
+      <el-tab-pane
+        label="安全生产责任制平台资源库"
+        name="platform_resource_library"
+        lazy
+      ></el-tab-pane>
+    </el-tabs>
+  </layout-card>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
+import ListView from "./components/list.vue";
+
+const router = useRouter();
+const route = useRoute();
+const defaultName = "responsibility_system";
+const name = ref(route.query.name || defaultName);
+onBeforeRouteUpdate((to, from, next) => {
+  name.value = to.query.name || defaultName;
+  next();
+});
+const fnTabChange = (name) => {
+  router.push({
+    path: "/three_institutional_libraries/safety_production_responsibility_system",
+    query: {
+      name,
+    },
+  });
+};
+</script>
+
+<style scoped lang="scss"></style>