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>