Merge branch 'dev_New' of http://114.132.189.42:9002/r/product-inventory-management into dev_New
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function safeTrainingListPage(query) { |
| | | return request({ |
| | | url: "/safeTraining/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | |
| | | export function safeTrainingAdd(query) { |
| | | return request({ |
| | | url: '/safeTraining', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeTrainingUpdate(query) { |
| | | return request({ |
| | | url: '/safeTraining', |
| | | method: 'put', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeTrainingDel(ids) { |
| | | return request({ |
| | | url: '/safeTraining/' + ids, |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | | // å¯¼åº |
| | | export function safeTrainingExport(query) { |
| | | return request({ |
| | | url: '/safeTraining/export', |
| | | method: 'post', |
| | | data: query, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢éä»¶å表 |
| | | export function safeTrainingFileListPage(query) { |
| | | return request({ |
| | | url: "/safeTrainingFile/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ·»å éä»¶ |
| | | export function safeTrainingFileAdd(query) { |
| | | return request({ |
| | | url: '/safeTrainingFile/add', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | // å é¤éä»¶ |
| | | export function safeTrainingFileDel(ids) { |
| | | return request({ |
| | | url: '/safeTrainingFile/del', |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | |
| | | |
| | | // æ·»å 表è¡ç±»åæ¹æ³ |
| | | const tableRowClassName = ({ row }) => { |
| | | switch (row.deliveryDaysDiff) { |
| | | case 15: |
| | | return 'yellow' |
| | | case 10: |
| | | return 'red' |
| | | case 2: |
| | | return 'purple' |
| | | const diff = row.deliveryDaysDiff; |
| | | |
| | | if (diff === 15) { |
| | | return 'yellow'; |
| | | } else if (diff === 10) { |
| | | return 'pink'; |
| | | } else if (diff === 2) { |
| | | return 'purple'; |
| | | } else if (diff < 2) { |
| | | return 'red'; |
| | | } |
| | | }; |
| | | |
| | |
| | | background-color: #FAF0DE; |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | ::v-deep .pink { |
| | | background-color: #FAE1DE; |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | background-color: #f80202; |
| | | } |
| | | |
| | | ::v-deep .purple{ |
| | | background-color: #F4DEFA; |
| | | } |
| | |
| | | class="mb10"> |
| | | <el-button type="primary" |
| | | @click="showNewModal">æ°å¢å·¥åº</el-button> |
| | | <el-button type="info" plain @click="handleImport">导å
¥</el-button> |
| | | <el-button type="info" |
| | | plain |
| | | @click="handleImport">导å
¥</el-button> |
| | | <el-button type="danger" |
| | | @click="handleDelete" |
| | | :disabled="selectedRows.length === 0" |
| | |
| | | v-model:visible="isShowEditModal" |
| | | :record="record" |
| | | @completed="getList" /> |
| | | <ImportDialog |
| | | ref="importDialogRef" |
| | | v-model="importDialogVisible" |
| | | title="导å
¥å·¥åº" |
| | | :action="importAction" |
| | | :headers="importHeaders" |
| | | :auto-upload="false" |
| | | :on-success="handleImportSuccess" |
| | | :on-error="handleImportError" |
| | | @confirm="handleImportConfirm" |
| | | @download-template="handleDownloadTemplate" |
| | | @close="handleImportClose" |
| | | /> |
| | | <ImportDialog ref="importDialogRef" |
| | | v-model="importDialogVisible" |
| | | title="导å
¥å·¥åº" |
| | | :action="importAction" |
| | | :headers="importHeaders" |
| | | :auto-upload="false" |
| | | :on-success="handleImportSuccess" |
| | | :on-error="handleImportError" |
| | | @confirm="handleImportConfirm" |
| | | @download-template="handleDownloadTemplate" |
| | | @close="handleImportClose" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import NewProcess from "@/views/productionManagement/productionProcess/New.vue"; |
| | | import EditProcess from "@/views/productionManagement/productionProcess/Edit.vue"; |
| | | import ImportDialog from "@/components/Dialog/ImportDialog.vue"; |
| | | import { listPage, del, importData, downloadTemplate } from "@/api/productionManagement/productionProcess.js"; |
| | | import { |
| | | listPage, |
| | | del, |
| | | importData, |
| | | downloadTemplate, |
| | | } from "@/api/productionManagement/productionProcess.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | | |
| | | const data = reactive({ |
| | |
| | | label: "å·¥åºåç§°", |
| | | prop: "name", |
| | | }, |
| | | |
| | | |
| | | { |
| | | label: "å·¥èµå®é¢", |
| | | prop: "salaryQuota", |
| | |
| | | label: "夿³¨", |
| | | prop: "remark", |
| | | }, |
| | | { |
| | | { |
| | | label: "æ´æ°æ¶é´", |
| | | prop: "updateTime", |
| | | }, |
| | |
| | | total: 0, |
| | | }); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | |
| | | // 导å
¥ç¸å
³é
ç½® |
| | | const importAction = import.meta.env.VITE_APP_BASE_API + "/productProcess/importData"; |
| | | const importAction = |
| | | import.meta.env.VITE_APP_BASE_API + "/productProcess/importData"; |
| | | const importHeaders = { Authorization: "Bearer " + getToken() }; |
| | | |
| | | // æ¥è¯¢å表 |
| | |
| | | }; |
| | | |
| | | // 导å
¥æå |
| | | const handleImportSuccess = (response) => { |
| | | const handleImportSuccess = response => { |
| | | if (response.code === 200) { |
| | | proxy.$modal.msgSuccess("导å
¥æå"); |
| | | importDialogVisible.value = false; |
| | |
| | | }; |
| | | |
| | | // 导å
¥å¤±è´¥ |
| | | const handleImportError = (error) => { |
| | | const handleImportError = error => { |
| | | proxy.$modal.msgError("导å
¥å¤±è´¥ï¼" + (error.message || "æªç¥é误")); |
| | | }; |
| | | |
| | |
| | | process: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | testStandardId: [{required: false, message: "è¯·éæ©ææ ", trigger: "change"}], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="model"> |
| | | <el-input v-model="form.model" placeholder="请è¾å
¥" clearable/> |
| | | <el-select v-model="form.model" placeholder="è¯·éæ©" clearable :disabled="operationType === 'edit'" |
| | | filterable readonly @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {productTreeList} from "@/api/basicData/product.js"; |
| | | import {modelList, productTreeList} from "@/api/basicData/product.js"; |
| | | import { |
| | | getQualityUnqualifiedInfo, |
| | | qualityUnqualifiedAdd, |
| | |
| | | process: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | model: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | model: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]) |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | |
| | | }; |
| | | const getModels = (value) => { |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }) |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | |
| | | process: [{ required: true, message: "请è¾å
¥å·¥åº", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | testStandardId: [{required: false, message: "è¯·éæ©ææ ", trigger: "change"}], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | supplier: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | checkName: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | productId: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | productModelId: [{required: false, message: "è¯·éæ©äº§ååå·", trigger: "change"}], |
| | | productModelId: [{required: true, message: "è¯·éæ©äº§ååå·", trigger: "change"}], |
| | | testStandardId: [{required: false, message: "è¯·éæ©ææ ", trigger: "change"}], |
| | | unit: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | quantity: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | |
| | | {{ currentKnowledge.happenLocation }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="äºæ
ç级"> |
| | | {{ currentKnowledge.accidentGrade }} |
| | | <el-tag :type="accidentGradeType(currentKnowledge.accidentGrade)">{{ currentKnowledge.accidentGrade }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="äºæ
ç±»å"> |
| | | {{ currentKnowledge.accidentType }} |
| | | <el-tag type="info">{{ accidentTypeLabel(currentKnowledge.accidentType) }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="人å伤亡æ
åµ"> |
| | | {{ currentKnowledge.personLoss }} |
| | |
| | | label: "äºæ
ç±»å", |
| | | prop: "accidentType", |
| | | showOverflowTooltip: true, |
| | | formatData: params => { |
| | | return accidentTypeLabel(params); |
| | | }, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | |
| | | page.value.current = 1; |
| | | getList(); |
| | | }; |
| | | const accidentGradeType = val => { |
| | | switch (val) { |
| | | case "è½»å¾®äºæ
": |
| | | return "info"; |
| | | case "ä¸è¬äºæ
": |
| | | return "info"; |
| | | case "è¾å¤§äºæ
": |
| | | return "warning"; |
| | | case "éå¤§äºæ
": |
| | | return "danger"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | }; |
| | | const accidentGradeOptions = [ |
| | | { |
| | | label: "è½»å¾®äºæ
", |
| | |
| | | value: "éå¤§äºæ
", |
| | | }, |
| | | ]; |
| | | const accidentTypeOptions = [ |
| | | { |
| | | label: "è´£ä»»äºæ
", |
| | | value: "è´£ä»»äºæ
", |
| | | }, |
| | | { |
| | | label: "éè´£ä»»äºæ
", |
| | | value: "éè´£ä»»äºæ
", |
| | | }, |
| | | { |
| | | label: "ç ´åæ§äºæ
", |
| | | value: "ç ´åæ§äºæ
", |
| | | }, |
| | | { |
| | | label: "éå¤§äºæ
", |
| | | value: "éå¤§äºæ
", |
| | | }, |
| | | ]; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { accident_type } = proxy.useDict("accident_type"); |
| | | const accidentTypeOptions = computed(() => accident_type?.value || []); |
| | | const accidentTypeLabel = val => { |
| | | const item = accidentTypeOptions.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | safeAccidentListPage({ ...page.value, ...searchForm.value }) |
| | |
| | | // ç¨æ·åæ¶ |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const { proxy } = getCurrentInstance(); |
| | | const { knowledge_type } = proxy.useDict("knowledge_type"); |
| | | |
| | | // åå
¸å·¥å
· |
| | | const knowledgeTypeOptions = computed(() => knowledge_type?.value || []); |
| | |
| | | <span class="detail-title">{{ form.hiddenDesc }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="éæ£ç±»å"> |
| | | <span class="detail-title">{{ form.type }}</span> |
| | | <span class="detail-title">{{ TypeLabel(form.type) }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="鿣é£é©ç级"> |
| | | <el-tag :type="getTypeTagType(form.riskLevel)"> |
| | |
| | | <script setup> |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { onMounted, ref, getCurrentInstance } from "vue"; |
| | | import { onMounted, ref, getCurrentInstance, computed } from "vue"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | |
| | | verifyResult: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | acceptDesc: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | }; |
| | | const typeList = ref([ |
| | | { |
| | | value: "设å¤å®å
¨", |
| | | label: "设å¤å®å
¨", |
| | | }, |
| | | { |
| | | value: "人åæä½", |
| | | label: "人åæä½", |
| | | }, |
| | | { |
| | | value: "ç¯å¢é£é©", |
| | | label: "ç¯å¢é£é©", |
| | | }, |
| | | { |
| | | value: "ç©æç®¡æ§", |
| | | label: "ç©æç®¡æ§", |
| | | }, |
| | | { |
| | | value: "å
¶ä»", |
| | | label: "å
¶ä»", |
| | | }, |
| | | ]); |
| | | const { hidden_danger_type } = proxy.useDict("hidden_danger_type"); |
| | | const typeList = computed(() => hidden_danger_type?.value || []); |
| | | const TypeLabel = val => { |
| | | const item = typeList.value.find(i => String(i.value) === String(val)); |
| | | return item ? item.label : val; |
| | | }; |
| | | const form2 = ref({ |
| | | rectifyActualTime: "", // å®é
æ´æ¹å®ææ¶é´ |
| | | rectifyMeasures: "", // æ´æ¹å
·ä½æªæ½ |
| | |
| | | label: "ä½é£é©", |
| | | }, |
| | | ]); |
| | | // éæ£ç±»åé项 |
| | | const { type_qualification } = proxy.useDict("type_qualification"); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData(data.searchForm); |
| | | // 产å表åå¼¹æ¡æ°æ® |
| | |
| | | {{ currentKnowledge.coreResponsorUserName }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="颿¡ç±»å"> |
| | | <el-tag type="warning"> {{ currentKnowledge.planType }}</el-tag> |
| | | <el-tag type="warning"> {{ emergencyPlanTypeLabel(currentKnowledge.planType) }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨"> |
| | | {{ currentKnowledge.remark }} |
| | |
| | | label: "颿¡ç±»å", |
| | | prop: "planType", |
| | | showOverflowTooltip: true, |
| | | formatData: params => { |
| | | return emergencyPlanTypeLabel(params); |
| | | }, |
| | | }, |
| | | { |
| | | label: "夿³¨", |
| | |
| | | }; |
| | | return timeMap[efficiency] || "æªç¥"; |
| | | }; |
| | | const emergencyPlanTypeOptions = computed(() => [ |
| | | { |
| | | value: "é¢é²æ§åºæ¥é¢æ¡", |
| | | label: "é¢é²æ§åºæ¥é¢æ¡", |
| | | }, |
| | | { |
| | | value: "åºæ¥å¤ç颿¡", |
| | | label: "åºæ¥å¤ç颿¡", |
| | | }, |
| | | { |
| | | value: "æ¢å¤æ§åºæ¥é¢æ¡", |
| | | label: "æ¢å¤æ§åºæ¥é¢æ¡", |
| | | }, |
| | | ]); |
| | | const { proxy } = getCurrentInstance(); |
| | | const { emergency_plan_type } = proxy.useDict("emergency_plan_type"); |
| | | const emergencyPlanTypeOptions = computed( |
| | | () => emergency_plan_type?.value || [] |
| | | ); |
| | | const emergencyPlanTypeLabel = val => { |
| | | const item = emergencyPlanTypeOptions.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | // æäº¤åºæ¥é¢æ¡è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const { proxy } = getCurrentInstance(); |
| | | const { knowledge_type } = proxy.useDict("knowledge_type"); |
| | | |
| | | // åå
¸å·¥å
· |
| | | const knowledgeTypeOptions = computed(() => knowledge_type?.value || []); |
| | |
| | | {{ currentKnowledge.code }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å±é©æºç±»å"> |
| | | {{ getTypeLabel(currentKnowledge.type) }} |
| | | <el-tag type="info"> |
| | | {{ getTypeLabel(currentKnowledge.type) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æå¨ä½ç½®"> |
| | | {{ currentKnowledge.location }} |
| | |
| | | <span>{{ form.name }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å±é©æºç±»å"> |
| | | <span>{{ getTypeLabel(form.type) }}</span> |
| | | <el-tag type="info"> |
| | | {{ getTypeLabel(form.type) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æå¨ä½ç½®"> |
| | | <span>{{ form.location }}</span> |
| | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "é¢ç¨åå·", |
| | | prop: "materialRecordCode", |
| | | width: 130, |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å±é©æºç¼ç ", |
| | | prop: "code", |
| | | showOverflowTooltip: true, |
| | |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "åå·", |
| | | prop: "materialRecordCode", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 200, |
| | | width: 130, |
| | | operation: [ |
| | | { |
| | | name: "å½è¿", |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">å¹è®åç§°ï¼</span> |
| | | <el-input v-model="searchForm.name" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å¹è®åç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title ml10">å¹è®ç±»åï¼</span> |
| | | <el-select v-model="searchForm.type" |
| | | clearable |
| | | @change="handleQuery" |
| | | style="width: 240px"> |
| | | <el-option v-for="item in knowledgeTypeOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px"> |
| | | æç´¢ |
| | | </el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢å¹è®</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | :rowClassName="getRowClass"></PIMTable> |
| | | </div> |
| | | <!-- æ°å¢/ç¼è¾ç¥è¯å¼¹çª --> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="800px" |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹è®æ¥æ" |
| | | prop="trainingDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.trainingDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="课ç¨ç¼å·" |
| | | prop="courseCode"> |
| | | <el-input v-model="form.courseCode" |
| | | disabled |
| | | placeholder="èªå¨çæ" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¼å§æ¶é´" |
| | | prop="openingTime"> |
| | | <el-time-picker v-model="form.openingTime" |
| | | placeholder="è¯·éæ©" |
| | | value-format="HH:mm:ss" |
| | | format="HH:mm:ss" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»ææ¶é´" |
| | | prop="endTime"> |
| | | <el-time-picker v-model="form.endTime" |
| | | placeholder="è¯·éæ©" |
| | | value-format="HH:mm:ss" |
| | | format="HH:mm:ss" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹è®ç®æ " |
| | | prop="trainingObjectives"> |
| | | <el-input v-model="form.trainingObjectives" |
| | | placeholder="请è¾å
¥å¹è®ç®æ " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åå 对象" |
| | | prop="participants"> |
| | | <el-input v-model="form.participants" |
| | | placeholder="请è¾å
¥åå 对象" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹è®å
容" |
| | | prop="trainingContent"> |
| | | <el-input v-model="form.trainingContent" |
| | | placeholder="请è¾å
¥å¹è®å
容" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹è®è®²å¸" |
| | | prop="trainingLecturer"> |
| | | <el-input v-model="form.trainingLecturer" |
| | | placeholder="请è¾å
¥å¹è®è®²å¸" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="课ç¨å¦å" |
| | | prop="projectCredits"> |
| | | <el-input v-model="form.projectCredits" |
| | | placeholder="请è¾å
¥è¯¾ç¨å¦å" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹è®æ¹å¼" |
| | | prop="trainingMode"> |
| | | <el-select v-model="form.trainingMode" |
| | | placeholder="è¯·éæ©å¹è®æ¹å¼" |
| | | clearable> |
| | | <el-option v-for="item in trainingModeOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹è®å°ç¹" |
| | | prop="placeTraining"> |
| | | <el-input v-model="form.placeTraining" |
| | | placeholder="请è¾å
¥å¹è®å°ç¹" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="课æ¶" |
| | | prop="classHour"> |
| | | <el-input v-model="form.classHour" |
| | | type="number" |
| | | min="0" |
| | | placeholder="请è¾å
¥è¯¾æ¶" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- æ¥çç¥è¯è¯¦æ
å¼¹çª --> |
| | | <el-dialog v-model="viewDialogVisible" |
| | | title="å¹è®è¯¦æ
" |
| | | width="900px" |
| | | :close-on-click-modal="false"> |
| | | <div class="knowledge-detail"> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="å¹è®åç§°" |
| | | :span="2"> |
| | | <span class="detail-title">{{ currentKnowledge.name }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å¹è®ç¼ç "> |
| | | {{ currentKnowledge.code }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å¹è®ç±»å"> |
| | | <el-tag type="info"> |
| | | <!-- {{ getTypeLabel(currentKnowledge.type) }} --> |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æå¨ä½ç½®"> |
| | | {{ currentKnowledge.location }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç®¡æ§æªæ½"> |
| | | {{ currentKnowledge.controlMeasures }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="åºåæ°é"> |
| | | {{ currentKnowledge.stockQty }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="管æ§è´£ä»»äºº"> |
| | | {{ currentKnowledge.principalUserId }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="责任人èç³»çµè¯"> |
| | | {{ currentKnowledge.principalMobile }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="é£é©ç级"> |
| | | <el-tag :type="getTypeTagType(currentKnowledge.riskLevel)"> |
| | | {{ currentKnowledge.riskLevel }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="è§æ ¼ / é£é©æè¿°"> |
| | | {{ currentKnowledge.specInfo }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="viewDialogVisible = false">å
³é</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- éä»¶åè¡¨å¼¹çª --> |
| | | <FileListDialog ref="fileListRef" |
| | | v-model="fileListDialogVisible" |
| | | :show-upload-button="true" |
| | | :show-delete-button="true" |
| | | :upload-method="handleUpload" |
| | | :delete-method="handleFileDelete" |
| | | title="éä»¶å表" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import FileListDialog from "@/components/Dialog/FileListDialog.vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | computed, |
| | | } from "vue"; |
| | | import request from "@/utils/request"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | safeTrainingListPage, |
| | | safeTrainingAdd, |
| | | safeTrainingExport, |
| | | safeTrainingDel, |
| | | safeTrainingFileListPage, |
| | | safeTrainingFileAdd, |
| | | safeTrainingFileDel, |
| | | } from "@/api/safeProduction/safetyTrainingAssessment.js"; |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | trainingDate: [ |
| | | { required: true, message: "è¯·éæ©å¹è®æ¥æ", trigger: "change" }, |
| | | ], |
| | | openingTime: [ |
| | | { required: true, message: "è¯·éæ©å¼å§æ¶é´", trigger: "change" }, |
| | | ], |
| | | endTime: [{ required: true, message: "è¯·éæ©ç»ææ¶é´", trigger: "change" }], |
| | | trainingContent: [ |
| | | { required: true, message: "请è¾å
¥å¹è®å
容", trigger: "blur" }, |
| | | ], |
| | | trainingLecturer: [ |
| | | { required: true, message: "请è¾å
¥å¹è®è®²å¸", trigger: "blur" }, |
| | | ], |
| | | classHour: [{ required: true, message: "请è¾å
¥è¯¾æ¶", trigger: "blur" }], |
| | | }; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | name: "", |
| | | type: "", |
| | | }, |
| | | tableLoading: false, |
| | | page: { |
| | | current: 1, |
| | | size: 20, |
| | | total: 0, |
| | | }, |
| | | tableData: [], |
| | | selectedIds: [], |
| | | form: { |
| | | courseCode: "", // 课ç¨ç¼å· |
| | | trainingDate: "", // å¹è®æ¥æ |
| | | openingTime: "", // å¼å§æ¶é´ |
| | | endTime: "", // ç»ææ¶é´ |
| | | trainingObjectives: "", // å¹è®ç®æ |
| | | participants: "", // åå 对象 |
| | | trainingContent: "", // å¹è®å
容 |
| | | trainingLecturer: "", // å¹è®è®²å¸ |
| | | projectCredits: "", // 项ç®å¦å |
| | | trainingMode: "", // å¹è®æ¹å¼ |
| | | placeTraining: "", // å¹è®å°ç¹ |
| | | classHour: "", // è¯¾æ¶ |
| | | }, |
| | | dialogVisible: false, |
| | | dialogTitle: "", |
| | | dialogType: "add", |
| | | viewDialogVisible: false, |
| | | currentKnowledge: {}, |
| | | }); |
| | | |
| | | const { |
| | | searchForm, |
| | | tableLoading, |
| | | page, |
| | | tableData, |
| | | selectedIds, |
| | | form, |
| | | dialogVisible, |
| | | dialogTitle, |
| | | dialogType, |
| | | viewDialogVisible, |
| | | currentKnowledge, |
| | | } = toRefs(data); |
| | | const { proxy } = getCurrentInstance(); |
| | | const { safe_training_methods } = proxy.useDict("safe_training_methods"); |
| | | const trainingModeOptions = computed(() => safe_training_methods?.value || []); |
| | | const getTrainingModeLabel = val => { |
| | | const item = trainingModeOptions.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(); |
| | | const riskLevelOptions = ref([ |
| | | { value: "ä½é£é©", label: "ä½é£é©" }, |
| | | { value: "ä¸è¬é£é©", label: "ä¸è¬é£é©" }, |
| | | { value: "è¾å¤§é£é©", label: "è¾å¤§é£é©" }, |
| | | { value: "é大é£é©", label: "é大é£é©" }, |
| | | ]); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "课ç¨ç¼å·", |
| | | prop: "courseCode", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å¹è®æ¥æ", |
| | | prop: "trainingDate", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å¼å§æ¶é´", |
| | | prop: "openingTime", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "ç»ææ¶é´", |
| | | prop: "endTime", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å¹è®ç®æ ", |
| | | prop: "trainingObjectives", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "åå 对象", |
| | | prop: "participants", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å¹è®å
容", |
| | | prop: "trainingContent", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å¹è®è®²å¸", |
| | | prop: "trainingLecturer", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "项ç®å¦å", |
| | | prop: "projectCredits", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å¹è®æ¹å¼", |
| | | prop: "trainingMode", |
| | | showOverflowTooltip: true, |
| | | formatData: params => { |
| | | return getTrainingModeLabel(params); |
| | | }, |
| | | }, |
| | | { |
| | | label: "å¹è®å°ç¹", |
| | | prop: "placeTraining", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "课æ¶", |
| | | prop: "classHour", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 200, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "导åº", |
| | | type: "danger", |
| | | clickFun: row => { |
| | | exportKnowledge(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "éä»¶", |
| | | type: "danger", |
| | | clickFun: row => { |
| | | downLoadFile(row); |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "æ¥ç", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // viewKnowledge(row); |
| | | // }, |
| | | // }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const userList = ref([]); |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | getList(); |
| | | startAutoRefresh(); |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | }); |
| | | |
| | | // å¤çç¨æ·éæ©åå |
| | | const handleUserChange = userId => { |
| | | const selectedUser = userList.value.find(user => user.userId === userId); |
| | | if (selectedUser) { |
| | | form.value.principalUser = selectedUser.nickName; |
| | | form.value.principalMobile = selectedUser.phonenumber; |
| | | } |
| | | }; |
| | | /** |
| | | * ä¸è½½æä»¶ |
| | | * |
| | | * @param row ä¸è½½æä»¶çç¸å
³ä¿¡æ¯å¯¹è±¡ |
| | | */ |
| | | const fileListRef = ref(null); |
| | | const fileListDialogVisible = ref(false); |
| | | const currentFileRow = ref(null); |
| | | const downLoadFile = row => { |
| | | currentFileRow.value = row; |
| | | safeTrainingFileListPage({ safeTrainingId: row.id }).then(res => { |
| | | if (fileListRef.value) { |
| | | fileListRef.value.open(res.data.records); |
| | | } |
| | | }); |
| | | }; |
| | | // ä¸ä¼ éä»¶ |
| | | const handleUpload = async () => { |
| | | if (!currentFileRow.value) { |
| | | proxy.$modal.msgWarning("请å
éæ©æ°æ®"); |
| | | return null; |
| | | } |
| | | |
| | | return new Promise(resolve => { |
| | | // å建ä¸ä¸ªéèçæä»¶è¾å
¥å
ç´ |
| | | const input = document.createElement("input"); |
| | | input.type = "file"; |
| | | input.style.display = "none"; |
| | | input.onchange = async e => { |
| | | const file = e.target.files[0]; |
| | | if (!file) { |
| | | resolve(null); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // ä½¿ç¨ FormData ä¸ä¼ æä»¶ |
| | | const formData = new FormData(); |
| | | formData.append("file", file); |
| | | |
| | | const uploadRes = await request({ |
| | | url: "/file/upload", |
| | | method: "post", |
| | | data: formData, |
| | | headers: { |
| | | "Content-Type": "multipart/form-data", |
| | | Authorization: `Bearer ${getToken()}`, |
| | | }, |
| | | }); |
| | | |
| | | if (uploadRes.code === 200) { |
| | | // ä¿åéä»¶ä¿¡æ¯ |
| | | const fileData = { |
| | | safeTrainingId: currentFileRow.value.id, |
| | | name: uploadRes.data.originalName || file.name, |
| | | url: uploadRes.data.tempPath || uploadRes.data.url, |
| | | }; |
| | | |
| | | const saveRes = await safeTrainingFileAdd(fileData); |
| | | if (saveRes.code === 200) { |
| | | proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); |
| | | // éæ°å è½½æä»¶å表 |
| | | const listRes = await safeTrainingFileListPage({ |
| | | safeTrainingId: currentFileRow.value.id, |
| | | }); |
| | | if (listRes.code === 200 && fileListRef.value) { |
| | | const fileList = (listRes.data?.records || []).map(item => ({ |
| | | name: item.name, |
| | | url: item.url, |
| | | id: item.id, |
| | | ...item, |
| | | })); |
| | | fileListRef.value.setList(fileList); |
| | | } |
| | | // è¿åæ°æä»¶ä¿¡æ¯ |
| | | resolve({ |
| | | name: fileData.name, |
| | | url: fileData.url, |
| | | id: saveRes.data?.id, |
| | | }); |
| | | } else { |
| | | proxy.$modal.msgError(saveRes.msg || "æä»¶ä¿å失败"); |
| | | resolve(null); |
| | | } |
| | | } else { |
| | | proxy.$modal.msgError(uploadRes.msg || "æä»¶ä¸ä¼ 失败"); |
| | | resolve(null); |
| | | } |
| | | } catch (error) { |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); |
| | | resolve(null); |
| | | } finally { |
| | | document.body.removeChild(input); |
| | | } |
| | | }; |
| | | |
| | | document.body.appendChild(input); |
| | | input.click(); |
| | | }); |
| | | }; |
| | | // å é¤éä»¶ |
| | | const handleFileDelete = async row => { |
| | | try { |
| | | const res = await safeTrainingFileDel([row.id]); |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | // éæ°å è½½æä»¶å表 |
| | | if (currentFileRow.value && fileListRef.value) { |
| | | const listRes = await safeTrainingFileListPage({ |
| | | safeTrainingId: currentFileRow.value.id, |
| | | }); |
| | | if (listRes.code === 200) { |
| | | const fileList = (listRes.data?.records || []).map(item => ({ |
| | | name: item.name, |
| | | url: item.url, |
| | | id: item.id, |
| | | ...item, |
| | | })); |
| | | fileListRef.value.setList(fileList); |
| | | } |
| | | } |
| | | return true; // è¿å true 表示å 餿åï¼ç»ä»¶ä¼æ´æ°å表 |
| | | } else { |
| | | proxy.$modal.msgError(res.msg || "å é¤å¤±è´¥"); |
| | | return false; |
| | | } |
| | | } catch (error) { |
| | | proxy.$modal.msgError("å é¤å¤±è´¥"); |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | // å¼å§èªå¨å·æ° |
| | | const startAutoRefresh = () => { |
| | | setInterval(() => { |
| | | getList(); |
| | | }, 600000); // 10åéå·æ°ä¸æ¬¡ (10 * 60 * 1000 = 600000ms) |
| | | }; |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const handleQuery = () => { |
| | | page.value.current = 1; |
| | | getList(); |
| | | }; |
| | | const exportKnowledge = row => { |
| | | safeTrainingExport(row) |
| | | .then(res => { |
| | | // å建Blob对象 |
| | | const blob = new Blob([res], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | | }); |
| | | // å建ä¸è½½é¾æ¥ |
| | | const url = window.URL.createObjectURL(blob); |
| | | const link = document.createElement("a"); |
| | | link.href = url; |
| | | link.download = `å¹è®è®°å½_${row.courseCode}.xlsx`; |
| | | |
| | | // 模æç¹å»ä¸è½½ |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | |
| | | // æ¸
ç临æ¶å¯¹è±¡ |
| | | setTimeout(() => { |
| | | document.body.removeChild(link); |
| | | window.URL.revokeObjectURL(url); |
| | | }, 100); |
| | | |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }) |
| | | .catch(err => { |
| | | console.error("导åºå¤±è´¥:", err); |
| | | ElMessage.error("导åºå¤±è´¥ï¼è¯·éè¯"); |
| | | }); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | safeTrainingListPage({ ...page.value, ...searchForm.value }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.value.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å页å¤ç |
| | | const pagination = obj => { |
| | | page.value.current = obj.page; |
| | | page.value.size = obj.limit; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // éæ©ååå¤ç |
| | | const handleSelectionChange = selection => { |
| | | selectedIds.value = selection.map(item => item.id); |
| | | }; |
| | | |
| | | // æå¼è¡¨å |
| | | const openForm = (type, row = null) => { |
| | | dialogType.value = type; |
| | | if (type === "add") { |
| | | dialogTitle.value = "æ°å¢å¹è®"; |
| | | // é置表å |
| | | Object.assign(form.value, { |
| | | courseCode: "", // 课ç¨ç¼å· |
| | | trainingDate: "", // å¹è®æ¥æ |
| | | openingTime: "", // å¼å§æ¶é´ |
| | | endTime: "", // ç»ææ¶é´ |
| | | trainingObjectives: "", // å¹è®ç®æ |
| | | participants: "", // åå 对象 |
| | | trainingContent: "", // å¹è®å
容 |
| | | trainingLecturer: "", // å¹è®è®²å¸ |
| | | projectCredits: "", // 项ç®å¦å |
| | | trainingMode: "", // å¹è®æ¹å¼ |
| | | placeTraining: "", // å¹è®å°ç¹ |
| | | classHour: "", // è¯¾æ¶ |
| | | }); |
| | | } else if (type === "edit" && row) { |
| | | dialogTitle.value = "ç¼è¾å¹è®"; |
| | | Object.assign(form.value, { |
| | | id: row.id, |
| | | courseCode: row.courseCode, // 课ç¨ç¼å· |
| | | trainingDate: row.trainingDate, // å¹è®æ¥æ |
| | | openingTime: row.openingTime, // å¼å§æ¶é´ |
| | | endTime: row.endTime, // ç»ææ¶é´ |
| | | trainingObjectives: row.trainingObjectives, // å¹è®ç®æ |
| | | participants: row.participants, // åå 对象 |
| | | trainingContent: row.trainingContent, // å¹è®å
容 |
| | | trainingLecturer: row.trainingLecturer, // å¹è®è®²å¸ |
| | | projectCredits: row.projectCredits, // 项ç®å¦å |
| | | trainingMode: row.trainingMode, // å¹è®æ¹å¼ |
| | | placeTraining: row.placeTraining, // å¹è®å°ç¹ |
| | | classHour: row.classHour, // è¯¾æ¶ |
| | | }); |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // æ¥çå¹è®è¯¦æ
|
| | | const viewKnowledge = row => { |
| | | currentKnowledge.value = { ...row }; |
| | | viewDialogVisible.value = true; |
| | | }; |
| | | |
| | | // è·åç±»åæ ç¾ç±»å |
| | | const getTypeTagType = type => { |
| | | const typeMap = { |
| | | è¾å¤§é£é©: "warning", |
| | | ä½é£é©: "info", |
| | | ä¸è¬é£é©: "info", |
| | | é大é£é©: "danger", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | // è·åæçæ ç¾ç±»å |
| | | const getEfficiencyTagType = efficiency => { |
| | | const typeMap = { |
| | | high: "success", |
| | | medium: "warning", |
| | | low: "info", |
| | | }; |
| | | return typeMap[efficiency] || "info"; |
| | | }; |
| | | |
| | | // è·åæçæ ç¾ææ¬ |
| | | const getEfficiencyLabel = efficiency => { |
| | | const efficiencyMap = { |
| | | high: "æ¾èæå", |
| | | medium: "ä¸è¬æå", |
| | | low: "轻微æå", |
| | | }; |
| | | return efficiencyMap[efficiency] || efficiency; |
| | | }; |
| | | |
| | | // è·åæçæåç¾åæ¯ |
| | | const getEfficiencyScore = efficiency => { |
| | | const scoreMap = { |
| | | high: 40, |
| | | medium: 25, |
| | | low: 15, |
| | | }; |
| | | return scoreMap[efficiency] || 0; |
| | | }; |
| | | |
| | | // è·åå¹³åèçæ¶é´ |
| | | const getTimeSaved = efficiency => { |
| | | const timeMap = { |
| | | high: "2-3天", |
| | | medium: "1-2天", |
| | | low: "0.5-1天", |
| | | }; |
| | | return timeMap[efficiency] || "æªç¥"; |
| | | }; |
| | | |
| | | /** |
| | | * è·åè¡ç±»åï¼ç¨äºå¤æé£é©ç级æ¯å¦ä¸ºé大é£é© |
| | | * @param row è¡æ°æ® |
| | | * @returns ç±»å |
| | | */ |
| | | const getRowClass = ({ row }) => { |
| | | if (row.riskLevel === "é大é£é©") { |
| | | return "danger-row"; |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | // æäº¤å¹è®è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | | await formRef.value.validate(); |
| | | if (dialogType.value === "add") { |
| | | // æ°å¢å¹è®å°è´¦ |
| | | safeTrainingAdd({ ...form.value }) |
| | | .then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("æ·»å æå"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else { |
| | | // æ´æ°å¹è®å°è´¦ |
| | | safeTrainingAdd({ ...form.value }) |
| | | .then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("æ´æ°æå"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("表åéªè¯å¤±è´¥:", error); |
| | | } |
| | | }; |
| | | |
| | | // å é¤å¹è® |
| | | const handleDelete = () => { |
| | | if (selectedIds.value.length === 0) { |
| | | ElMessage.warning("è¯·éæ©è¦å é¤çå¹è®"); |
| | | return; |
| | | } |
| | | |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // console.log(selectedIds.value); |
| | | safeTrainingDel(selectedIds.value).then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("å 餿å"); |
| | | selectedIds.value = []; |
| | | getList(); |
| | | } |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | // ç¨æ·åæ¶ |
| | | }); |
| | | }; |
| | | |
| | | const getKnowledgeTypeTagType = val => { |
| | | const item = knowledgeTypeOptions.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item?.elTagType || "info"; |
| | | }; |
| | | const handleExport = () => { |
| | | proxy.download( |
| | | "/knowledgeBase/export", |
| | | { ...searchForm.value }, |
| | | "ç¥è¯åº.xlsx" |
| | | ); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .auto-refresh-info { |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .auto-refresh-info .el-alert { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | |
| | | .knowledge-detail { |
| | | padding: 20px 0; |
| | | } |
| | | |
| | | .detail-title { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .detail-section { |
| | | margin-top: 24px; |
| | | } |
| | | |
| | | .detail-section h4 { |
| | | margin: 0 0 12px 0; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | border-left: 4px solid #409eff; |
| | | padding-left: 12px; |
| | | } |
| | | |
| | | .detail-content { |
| | | background: #f8f9fa; |
| | | padding: 16px; |
| | | border-radius: 6px; |
| | | line-height: 1.6; |
| | | color: #606266; |
| | | white-space: pre-wrap; |
| | | } |
| | | |
| | | .key-points { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .usage-stats { |
| | | margin-top: 16px; |
| | | } |
| | | |
| | | .stat-item { |
| | | text-align: center; |
| | | padding: 20px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .stat-number { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | :deep(.danger-row td) { |
| | | color: #e95a66 !important; |
| | | } |
| | | </style> |
| | |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="æä½" width="150" align="center"> |
| | | <el-table-column fixed="right" label="æä½" width="200" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isApproving(scope.row.status)" |
| | | :disabled="!isApproved(scope.row.status)" |
| | | @click="openForm('edit', scope.row)">è¡¥å
åè´§ä¿¡æ¯</el-button> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="openDetail(scope.row)" |
| | | >详æ
</el-button> |
| | | <el-button |
| | | link |
| | | type="danger" |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 详æ
å¼¹æ¡ --> |
| | | <el-dialog v-model="detailDialogVisible" title="åè´§å°è´¦è¯¦æ
" width="55%" @close="closeDetail"> |
| | | <div v-if="detailRow" class="detail-wrapper"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="éå®è®¢å">{{ detailRow.salesContractNo || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å货订åå·">{{ detailRow.shippingNo || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ detailRow.customerName || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§ç±»å">{{ detailRow.type || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§æ¥æ">{{ detailRow.shippingDate || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®¡æ ¸ç¶æ">{{ getApprovalStatusText(detailRow.status) }}</el-descriptions-item> |
| | | <el-descriptions-item label="å货车çå·">{{ detailRow.shippingCarNumber || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éå
¬å¸">{{ detailRow.expressCompany || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éåå·" :span="2">{{ detailRow.expressNumber || '--' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="detail-images" v-if="detailImages.length"> |
| | | <div class="detail-images-title">åè´§å¾ç</div> |
| | | <el-image |
| | | v-for="img in detailImages" |
| | | :key="img.url" |
| | | :src="img.url" |
| | | :preview-src-list="detailImages.map(i => i.url)" |
| | | fit="cover" |
| | | class="detail-image" |
| | | /> |
| | | </div> |
| | | <div v-else class="detail-images-empty">ææ åè´§å¾ç</div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDetail">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | const total = ref(0); |
| | | const deliveryFileList = ref([]); |
| | | const javaApi = proxy.javaApi; |
| | | // 详æ
å¼¹æ¡ |
| | | const detailDialogVisible = ref(false); |
| | | const detailRow = ref(null); |
| | | const detailImages = ref([]); |
| | | |
| | | const normalizeFileUrl = (rawUrl = '') => { |
| | | let fileUrl = rawUrl || ''; |
| | | // Windows è·¯å¾è½¬ URL |
| | | if (fileUrl && fileUrl.indexOf('\\') > -1) { |
| | | const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads'); |
| | | if (uploadsIndex > -1) { |
| | | const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/'); |
| | | fileUrl = '/' + relativePath; |
| | | } else { |
| | | const parts = fileUrl.split('\\'); |
| | | const fileName = parts[parts.length - 1]; |
| | | fileUrl = '/uploads/' + fileName; |
| | | } |
| | | } |
| | | if (fileUrl && !fileUrl.startsWith('http')) { |
| | | if (!fileUrl.startsWith('/')) fileUrl = '/' + fileUrl; |
| | | fileUrl = javaApi + fileUrl; |
| | | } |
| | | return fileUrl; |
| | | }; |
| | | |
| | | // ä¸ä¼ é
ç½® |
| | | const upload = reactive({ |
| | |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = async (type, row) => { |
| | | // ç¼è¾æ¶æ£æ¥å®¡æ ¸ç¶æï¼åªæå®¡æ ¸ä¸ä¸è½ç¼è¾ |
| | | if (type === 'edit' && row && isApproving(row.status)) { |
| | | proxy.$modal.msgWarning("å®¡æ ¸ä¸çæ°æ®ä¸è½ç¼è¾"); |
| | | // è¡¥å
åè´§ä¿¡æ¯ï¼ä»
âå®¡æ ¸éè¿âå
许ç¼è¾ |
| | | if (type === 'edit' && row && !isApproved(row.status)) { |
| | | proxy.$modal.msgWarning("åªæå®¡æ ¸éè¿çæ°æ®æå¯ä»¥è¡¥å
åè´§ä¿¡æ¯"); |
| | | return; |
| | | } |
| | | |
| | |
| | | // 妿æå¾çï¼å° commonFileList 转æ¢ä¸ºæä»¶åè¡¨æ ¼å¼ |
| | | if (row.commonFileList && Array.isArray(row.commonFileList) && row.commonFileList.length > 0) { |
| | | deliveryFileList.value = row.commonFileList.map((file, index) => { |
| | | // å¤ç URLï¼å° Windows è·¯å¾è½¬æ¢ä¸ºå¯è®¿é®ç URL |
| | | let fileUrl = file.url || ''; |
| | | console.log('åå§ URL:', fileUrl); |
| | | |
| | | // 妿 URL æ¯ Windows è·¯å¾æ ¼å¼ï¼å
å«åææ ï¼ï¼éè¦è½¬æ¢ |
| | | if (fileUrl && fileUrl.indexOf('\\') > -1) { |
| | | // æ¥æ¾ uploads å
³é®åçä½ç½®ï¼ä»é£éå¼å§æåç¸å¯¹è·¯å¾ |
| | | const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads'); |
| | | if (uploadsIndex > -1) { |
| | | // ä» uploads å¼å§æåè·¯å¾ï¼å¹¶å°åææ æ¿æ¢ä¸ºæ£ææ |
| | | const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/'); |
| | | fileUrl = '/' + relativePath; |
| | | console.log('转æ¢åçç¸å¯¹è·¯å¾:', fileUrl); |
| | | } else { |
| | | // å¦ææ²¡ææ¾å° uploadsï¼æåæåä¸ä¸ªç®å½åæä»¶å |
| | | const parts = fileUrl.split('\\'); |
| | | const fileName = parts[parts.length - 1]; |
| | | fileUrl = '/uploads/' + fileName; |
| | | console.log('æªæ¾å° uploadsï¼ä½¿ç¨æä»¶å:', fileUrl); |
| | | } |
| | | } |
| | | |
| | | // ç¡®ä¿ææé http å¼å¤´ç URL 齿¼æ¥ baseUrl |
| | | if (fileUrl && !fileUrl.startsWith('http')) { |
| | | // ç¡®ä¿è·¯å¾ä»¥ / å¼å¤´ |
| | | if (!fileUrl.startsWith('/')) { |
| | | fileUrl = '/' + fileUrl; |
| | | } |
| | | // æ¼æ¥ baseUrl |
| | | fileUrl = javaApi + fileUrl; |
| | | console.log('æç»æ¼æ¥ç URL:', fileUrl); |
| | | } |
| | | const fileUrl = normalizeFileUrl(file.url || ''); |
| | | |
| | | return { |
| | | uid: file.id || Date.now() + index, |
| | |
| | | } |
| | | |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | |
| | | // æå¼è¯¦æ
å¼¹æ¡ |
| | | const openDetail = (row) => { |
| | | detailRow.value = row || null; |
| | | const list = Array.isArray(row?.commonFileList) ? row.commonFileList : []; |
| | | detailImages.value = list |
| | | .map((f) => ({ url: normalizeFileUrl(f?.url || '') })) |
| | | .filter((i) => !!i.url); |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | const closeDetail = () => { |
| | | detailDialogVisible.value = false; |
| | | detailRow.value = null; |
| | | detailImages.value = []; |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | |
| | | display: none; |
| | | } |
| | | } |
| | | .detail-wrapper { |
| | | padding: 8px 0; |
| | | } |
| | | .detail-images { |
| | | margin-top: 16px; |
| | | } |
| | | .detail-images-title { |
| | | font-weight: 600; |
| | | margin-bottom: 10px; |
| | | color: #303133; |
| | | } |
| | | .detail-image { |
| | | width: 120px; |
| | | height: 120px; |
| | | margin-right: 10px; |
| | | margin-bottom: 10px; |
| | | border-radius: 6px; |
| | | } |
| | | .detail-images-empty { |
| | | margin-top: 16px; |
| | | color: #909399; |
| | | } |
| | | </style> |
| | | |
| | |
| | | </div> |
| | | </div> |
| | | <div class="stat-content"> |
| | | <div class="stat-value">{{ indicatorKpis.shipRate }}%</div> |
| | | <div class="stat-value">{{ indicatorKpis.shipRate }}</div> |
| | | <div class="stat-label">åè´§ç</div> |
| | | </div> |
| | | <div class="stat-bg-decoration"></div> |
| | |
| | | <div ref="indicatorChartRef" class="chart-wrapper"></div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- ä¸ç»©ç»è®¡ï¼å¢éç»´åº¦ï¼æ 个人å§åï¼ --> |
| | | <el-card v-if="showTeamPerformance" class="table-card" shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span class="card-title">å¢éä¸ç»©ç»è®¡</span> |
| | | </div> |
| | | </template> |
| | | <el-table |
| | | :data="teamPerformanceList" |
| | | border |
| | | stripe |
| | | style="width: 100%" |
| | | :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: 'bold' }" |
| | | > |
| | | <el-table-column prop="team" label="éå®å¢é" min-width="120"/> |
| | | <el-table-column prop="orderCount" label="è®¢åæ°" align="right" min-width="100"/> |
| | | <el-table-column prop="salesAmount" label="éå®é¢" align="right" min-width="140"> |
| | | <template #default="scope">Â¥{{ scope.row.salesAmount.toLocaleString() }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="shipRate" label="åè´§ç" align="right" min-width="100"> |
| | | <template #default="scope">{{ scope.row.shipRate }}%</template> |
| | | </el-table-column> |
| | | <el-table-column prop="attainment" label="ç®æ è¾¾æç" align="center" min-width="120"> |
| | | <template #default="scope"> |
| | | <el-tag |
| | | :type="scope.row.attainment >= 100 ? 'success' : scope.row.attainment >= 80 ? 'warning' : 'danger'" |
| | | effect="dark" |
| | | > |
| | | {{ scope.row.attainment }}% |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | |
| | | const productOptions = ref([]) |
| | | const customerOption = ref([]) |
| | | |
| | | const teamPerformanceList = ref([ |
| | | { team: 'åä¸å¤§åº', orderCount: 320, salesAmount: 2850000, shipRate: 90, attainment: 105 }, |
| | | { team: 'åå大åº', orderCount: 280, salesAmount: 2150000, shipRate: 86, attainment: 92 }, |
| | | { team: 'åå大åº', orderCount: 210, salesAmount: 1850000, shipRate: 88, attainment: 78 }, |
| | | { team: '西å大åº', orderCount: 180, salesAmount: 1500000, shipRate: 83, attainment: 74 } |
| | | ]) |
| | | |
| | | // 转æ¢äº§åæ æ°æ®ï¼å° id æ¹ä¸º value |
| | | function convertIdToValue(data) { |
| | |
| | | indicatorKpis.orderCount = res.data.total || 0 |
| | | indicatorKpis.salesAmount = res.data.contractAmountTotal || 0 |
| | | // åè´§ç妿æ¥å£æ²¡æè¿åï¼ä¿æåå¼æè®¾ä¸º0 |
| | | // indicatorKpis.shipRate = res.data.shipRate || 0 |
| | | indicatorKpis.shipRate = res.data.shipRate || 0 |
| | | } |
| | | } catch (error) { |
| | | console.error('è·å头é¨ç»è®¡å¤±è´¥:', error) |
| | |
| | | |
| | | // æ·»å 表è¡ç±»åæ¹æ³ |
| | | const tableRowClassName = ({ row }) => { |
| | | switch (row.deliveryDaysDiff) { |
| | | case 15: |
| | | return 'yellow' |
| | | case 10: |
| | | return 'red' |
| | | case 2: |
| | | return 'purple' |
| | | const diff = row.deliveryDaysDiff; |
| | | |
| | | if (diff === 15) { |
| | | return 'yellow'; |
| | | } else if (diff === 10) { |
| | | return 'pink'; |
| | | } else if (diff === 2) { |
| | | return 'purple'; |
| | | } else if (diff < 2) { |
| | | return 'red'; |
| | | } |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | |
| | | background-color: #FAF0DE; |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | ::v-deep .pink { |
| | | background-color: #FAE1DE; |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | background-color: #f80202; |
| | | } |
| | | |
| | | ::v-deep .purple{ |
| | | background-color: #F4DEFA; |
| | | } |
| | | |
| | | |
| | | .table_list { |
| | | margin-top: unset; |