| | |
| | | import request from '@/utils/request' |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedListPage(query) { |
| | | return request({ |
| | | url: '/quality/qualityUnqualified/listPage', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ°å¢ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedAdd(query) { |
| | | return request({ |
| | | url: '/quality/qualityUnqualified/add', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/add", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¿®æ¹ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedUpdate(query) { |
| | | return request({ |
| | | url: '/quality/qualityUnqualified/update', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/update", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¸åæ ¼å¤ç |
| | | export function qualityUnqualifiedDeal(query) { |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/deal", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // å é¤ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedDel(query) { |
| | | return request({ |
| | | url: '/quality/qualityUnqualified/del', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/del", |
| | | method: "delete", |
| | | data: query, |
| | | }); |
| | | } |
| | | // æ¥è¯¢ä¸åæ ¼ç®¡çä¿¡æ¯ |
| | | export function getQualityUnqualifiedInfo(query) { |
| | | return request({ |
| | | url: '/quality/qualityUnqualified/' + query, |
| | | method: 'get', |
| | | data: query, |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/" + query, |
| | | method: "get", |
| | | data: query, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function safeAccidentListPage(query) { |
| | | return request({ |
| | | url: "/safeAccident/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | |
| | | export function safeAccidentAdd(query) { |
| | | return request({ |
| | | url: '/safeAccident', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeAccidentUpdate(query) { |
| | | return request({ |
| | | url: '/safeAccident', |
| | | method: 'put', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeAccidentDel(ids) { |
| | | return request({ |
| | | url: '/safeAccident/' + ids, |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // åè´§å°è´¦é¡µé¢æ¥å£ |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function dangerInvestigationListPage(query) { |
| | | return request({ |
| | | url: "/safeHidden/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢å®å
¨è§ç¨ä¸èµè´¨ç®¡ç |
| | | export function safeHiddenAdd(query) { |
| | | return request({ |
| | | url: '/safeHidden', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | // ä¿®æ¹å®å
¨è§ç¨ä¸èµè´¨ç®¡ç |
| | | export function safeHiddenUpdate(query) { |
| | | return request({ |
| | | url: '/safeHidden', |
| | | method: 'put', |
| | | data: query |
| | | }) |
| | | } |
| | | // å é¤å®å
¨è§ç¨ä¸èµè´¨ç®¡ç |
| | | export function safeHiddenDel(ids) { |
| | | return request({ |
| | | url: '/safeHidden/' + ids, |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢éä»¶å表 |
| | | export function fileListPage(query) { |
| | | return request({ |
| | | url: "/safeHiddenFile/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ·»å éä»¶ |
| | | export function safeHiddenFileAdd(query) { |
| | | return request({ |
| | | url: '/safeHiddenFile/add', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | // å é¤éä»¶ |
| | | export function safeHiddenFileDel(ids) { |
| | | return request({ |
| | | url: '/safeHiddenFile/del', |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // åè´§å°è´¦é¡µé¢æ¥å£ |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function safeContingencyPlanListPage(query) { |
| | | return request({ |
| | | url: "/safeContingencyPlan/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function safeContingencyPlanAdd(query) { |
| | | return request({ |
| | | url: '/safeContingencyPlan', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeContingencyPlanUpdate(query) { |
| | | return request({ |
| | | url: '/safeContingencyPlan', |
| | | method: 'put', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeContingencyPlanDel(ids) { |
| | | return request({ |
| | | url: '/safeContingencyPlan/' + ids, |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | export function safeHazardRecordListPage(query) { |
| | | return request({ |
| | | url: "/safeHazardRecord/page", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function safeHazardRecordDel(ids) { |
| | | return request({ |
| | | url: '/safeHazardRecord/' + ids, |
| | | method: 'delete', |
| | | data: ids |
| | | }) |
| | | } |
| | | // æ°å¢å±é©æºå°è´¦ |
| | | export function safeHazardRecordAdd(query) { |
| | | return request({ |
| | | url: '/safeHazardRecord/borrow', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | export function safeHazardRecordUpdate(query) { |
| | | return request({ |
| | | url: '/safeHazardRecord/return', |
| | | method: 'put', |
| | | data: query |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | |
| | | 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 |
| | | }) |
| | | } |
| | |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¿®æ¹åè´§å°è´¦ |
| | | export function deductStock(query) { |
| | | return request({ |
| | | url: "/shippingInfo/deductStock", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // å é¤åè´§å°è´¦ |
| | | export function delDeliveryLedger(query) { |
| | |
| | | // é¦é¡µæ¥å£ |
| | | import request from '@/utils/request' |
| | | import request from "@/utils/request"; |
| | | |
| | | // éå®-éè´-åºåæ°æ® |
| | | export const getBusiness = () => { |
| | | return request({ |
| | | url: '/home/business', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/business", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | // 客æ·ååéé¢åæ |
| | | export const analysisCustomerContractAmounts = () => { |
| | | return request({ |
| | | url: '/home/analysisCustomerContractAmounts', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/analysisCustomerContractAmounts", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | // è´¨æ£åæ |
| | | export const qualityStatistics = () => { |
| | | return request({ |
| | | url: '/home/qualityStatistics', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/qualityStatistics", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | // åºæ¶åºä»ç»è®¡ |
| | | export const statisticsReceivablePayable = (query) => { |
| | | return request({ |
| | | url: '/home/statisticsReceivablePayable', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/statisticsReceivablePayable", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | }; |
| | | // å¾
åäºé¡¹ |
| | | export const homeTodos = () => { |
| | | return request({ |
| | | url: '/home/todos', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/todos", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // çº¿å½¢å¾ |
| | | export const getAmountHalfYear = () => { |
| | | return request({ |
| | | url: '/sales/ledger/getAmountHalfYear', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/sales/ledger/getAmountHalfYear", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // åç产订åç宿è¿åº¦ç»è®¡ |
| | | // /home/progressStatistics |
| | | export const getProgressStatistics = ()=>{ |
| | | return request({ |
| | | url: '/home/progressStatistics', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | export const getProgressStatistics = () => { |
| | | return request({ |
| | | url: "/home/progressStatistics", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | //å¨å¶åå¨è½¬æ
åµ |
| | | //home/workInProcessTurnover |
| | | export const getWorkInProcessTurnover = () => { |
| | | return request({ |
| | | url: '/home/workInProcessTurnover', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/workInProcessTurnover", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // 客æ·è¥æ¶è´¡ç®æ°å¼åæ |
| | | export const customerRevenueAnalysis = (params) => { |
| | | return request({ |
| | | url: '/home/customerRevenueAnalysis', |
| | | method: 'get', |
| | | params |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/customerRevenueAnalysis", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // åå·¥-客æ·-ä¾åºåæ»æ° |
| | | export const summaryStatistics = () => { |
| | | return request({ |
| | | url: '/home/summaryStatistics', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/summaryStatistics", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // åé¨é¨äººååå¸ |
| | | export const deptStaffDistribution = () => { |
| | | return request({ |
| | | url: '/home/deptStaffDistribution', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/deptStaffDistribution", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // ä¾åºåéè´æå |
| | | export const supplierPurchaseRanking = (query) => { |
| | | return request({ |
| | | url: '/home/supplierPurchaseRanking', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/supplierPurchaseRanking", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | }; |
| | | |
| | | // 客æ·éé¢è´¡ç®æå |
| | | export const customerContributionRanking = (query) => { |
| | | return request({ |
| | | url: '/home/customerContributionRanking', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/customerContributionRanking", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | }; |
| | | |
| | | // å产å大类åå¸ |
| | | export const productCategoryDistribution = () => { |
| | | return request({ |
| | | url: '/home/productCategoryDistribution', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/productCategoryDistribution", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // 产åéå®éé¢åæ |
| | | export const productSalesAnalysis = () => { |
| | | return request({ |
| | | url: '/home/productSalesAnalysis', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/productSalesAnalysis", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // åææéè´éé¢å æ¯ |
| | | export const rawMaterialPurchaseAmountRatio = () => { |
| | | return request({ |
| | | url: '/home/rawMaterialPurchaseAmountRatio', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/rawMaterialPurchaseAmountRatio", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // éå®/éè´/å¨åäº§åæ° |
| | | export const salesPurchaseStorageProductCount = () => { |
| | | return request({ |
| | | url: '/home/salesPurchaseStorageProductCount', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/salesPurchaseStorageProductCount", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // 产ååºå
¥åºåæï¼å¯ä¼ productType: 1 åææ 2 åæå 3 æåï¼ |
| | | export const productInOutAnalysis = (params) => { |
| | | return request({ |
| | | url: '/home/productInOutAnalysis', |
| | | method: 'get', |
| | | params |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/productInOutAnalysis", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // 产åå¨è½¬å¤©æ° |
| | | export const productTurnoverDays = () => { |
| | | return request({ |
| | | url: '/home/productTurnoverDays', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/home/productTurnoverDays", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // æ¶æ¯å¯¹æ¯åæ |
| | | export const incomeExpenseAnalysis = () => { |
| | | return request({ |
| | | url: "/home/incomeExpenseAnalysis", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // 婿¶¦è¶å¿åæ |
| | | export const profitTrendAnalysis = () => { |
| | | return request({ |
| | | url: "/home/profitTrendAnalysis", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // æåº¦æ¶å
¥ |
| | | export const getMonthlyIncome = () => { |
| | | return request({ |
| | | url: "/home/monthlyIncome", |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // æåº¦æ¯åº |
| | | export const getMonthlyExpenditure = () => { |
| | | return request({ |
| | | url: "/home/monthlyExpenditure", |
| | | method: "get", |
| | | }); |
| | | }; |
| | |
| | | <div v-if="!activity.isShen" class="node-reason"> |
| | | <span>å®¡æ¹æè§ï¼</span>{{ activity.approveNodeReason }} |
| | | </div> |
| | | <div v-if="!activity.isShen" class="node-reason"> |
| | | <span>ç¾åï¼</span> |
| | | <img :src="activity.urlTem" class="signImg" alt="" v-if="activity.urlTem"/> |
| | | </div> |
| | | <div v-else-if="activity.isShen"> |
| | | <el-form-item |
| | | :prop="'activities.' + index + '.approveNodeReason'" |
| | |
| | | type="danger" |
| | | plain |
| | | @click="handleDelete" |
| | | v-if="currentApproveType !== 7" |
| | | v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7" |
| | | >å é¤</el-button> |
| | | </div> |
| | | </div> |
| | |
| | | </div> |
| | | |
| | | <!-- æ°å¢/ç¼è¾ç¥è¯å¼¹çª --> |
| | | <el-dialog |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="800px" |
| | | :close-on-click-modal="false" |
| | | :width="'800px'" |
| | | @close="closeKnowledgeDialog" |
| | | @confirm="submitForm" |
| | | @cancel="closeKnowledgeDialog" |
| | | > |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建人" prop="creator"> |
| | | <el-input v-model="form.creator" placeholder="请è¾å
¥å建人" /> |
| | | <el-select v-model="form.creator" placeholder="è¯·éæ©å建人" style="width: 100%" filterable> |
| | | <el-option |
| | | v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.nickName" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </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> |
| | | </FormDialog> |
| | | |
| | | <!-- æ¥çç¥è¯è¯¦æ
å¼¹çª --> |
| | | <el-dialog |
| | | <FormDialog |
| | | v-model="viewDialogVisible" |
| | | title="ç¥è¯è¯¦æ
" |
| | | width="900px" |
| | | :close-on-click-modal="false" |
| | | :width="'900px'" |
| | | @close="closeViewDialog" |
| | | @confirm="handleViewDialogConfirm" |
| | | @cancel="closeViewDialog" |
| | | > |
| | | <div class="knowledge-detail"> |
| | | <el-descriptions :column="2" border> |
| | |
| | | <h4>å
³é®è¦ç¹</h4> |
| | | <div class="key-points"> |
| | | <el-tag |
| | | v-for="(point, index) in currentKnowledge.keyPoints.split(',')" |
| | | v-for="(point, index) in currentKnowledge.keyPoints?.split(',') || []" |
| | | :key="index" |
| | | type="success" |
| | | style="margin-right: 8px; margin-bottom: 8px;" |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="viewDialogVisible = false">å
³é</el-button> |
| | | <el-button type="primary" @click="copyKnowledge">å¤å¶ç¥è¯</el-button> |
| | | <!-- <el-button type="success" @click="markAsFavorite">æ¶è@</el-button> --> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance, computed } from "vue"; |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance, computed, watch } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import { listKnowledgeBase, delKnowledgeBase,addKnowledgeBase,updateKnowledgeBase } from "@/api/collaborativeApproval/knowledgeBase.js"; |
| | | import useUserStore from '@/store/modules/user'; |
| | | import { userListNoPageByTenantId } from '@/api/system/user.js'; |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(); |
| | | // ç¨æ·ç¸å
³ |
| | | const userStore = useUserStore(); |
| | | const userList = ref([]); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const tableColumn = ref([ |
| | |
| | | } |
| | | ]); |
| | | |
| | | // çå¬å¯¹è¯æ¡æå¼ï¼è·åç¨æ·å表 |
| | | watch(dialogVisible, (newVal) => { |
| | | if (newVal) { |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data || []; |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | getList(); |
| | |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | | page.value.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | |
| | | dialogType.value = type; |
| | | if (type === "add") { |
| | | dialogTitle.value = "æ°å¢ç¥è¯"; |
| | | // é置表å |
| | | // é置表åï¼é»è®¤å建人为å½åç¨æ· |
| | | Object.assign(form.value, { |
| | | title: "", |
| | | type: "", |
| | |
| | | problem: "", |
| | | solution: "", |
| | | keyPoints: "", |
| | | creator: "", |
| | | creator: userStore.nickName || "", |
| | | usageCount: 0 |
| | | }); |
| | | } else if (type === "edit" && row) { |
| | |
| | | }); |
| | | }; |
| | | |
| | | // å
³éç¥è¯è¡¨åå¯¹è¯æ¡ |
| | | const closeKnowledgeDialog = () => { |
| | | // æ¸
ç©ºè¡¨åæ°æ®ï¼é»è®¤å建人为å½åç¨æ· |
| | | Object.assign(form.value, { |
| | | id: undefined, |
| | | title: "", |
| | | type: "", |
| | | scenario: "", |
| | | efficiency: "", |
| | | problem: "", |
| | | solution: "", |
| | | keyPoints: "", |
| | | creator: userStore.nickName || "", |
| | | usageCount: 0 |
| | | }); |
| | | // æ¸
é¤è¡¨åéªè¯ç¶æ |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | // å
³éæ¥ç详æ
å¯¹è¯æ¡ |
| | | const closeViewDialog = () => { |
| | | viewDialogVisible.value = false; |
| | | }; |
| | | |
| | | // å¤çæ¥ç详æ
å¯¹è¯æ¡ç¡®è®¤ï¼æ§è¡å¤å¶æä½ï¼ |
| | | const handleViewDialogConfirm = () => { |
| | | copyKnowledge(); |
| | | closeViewDialog(); |
| | | }; |
| | | |
| | | // æäº¤ç¥è¯è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | |
| | | addKnowledgeBase({...form.value}).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("æ·»å æå"); |
| | | dialogVisible.value = false; |
| | | closeKnowledgeDialog(); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | |
| | | updateKnowledgeBase({...form.value}).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("æ´æ°æå"); |
| | | dialogVisible.value = false; |
| | | closeKnowledgeDialog(); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | |
| | | <el-option |
| | | v-for="person in employees" |
| | | :key="person.id" |
| | | :label="`${person.staffName} (${person.postName})`" |
| | | :label="`${person.staffName}${person.postName ? ` (${person.postName})` : ''}`" |
| | | :value="person.id" |
| | | /> |
| | | </el-select> |
| | |
| | | size: -1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | employees.value = res.data.records.sort((a, b) => a.postName.localeCompare(b.postName)) |
| | | employees.value = res.data.records.sort((a, b) => (a.postName || '').localeCompare(b.postName || '')) |
| | | }) |
| | | }) |
| | | </script> |
| | |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postName})` |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | } |
| | | }) |
| | | |
| | |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postName})` |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | } |
| | | }) |
| | | |
| | |
| | | it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postName})` |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | } |
| | | }) |
| | | |
| | |
| | | </el-card> |
| | | |
| | | <!-- ç¨å°ç³è¯·å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="showSealApplyDialog" title="ç³è¯·ç¨å°" width="600px"> |
| | | <FormDialog |
| | | v-model="showSealApplyDialog" |
| | | title="ç³è¯·ç¨å°" |
| | | :width="'600px'" |
| | | @close="closeSealApplyDialog" |
| | | @confirm="submitSealApplication" |
| | | @cancel="closeSealApplyDialog" |
| | | > |
| | | <el-form :model="sealForm" :rules="sealRules" ref="sealFormRef" label-width="100px"> |
| | | <el-form-item label="ç³è¯·ç¼å·" prop="applicationNum"> |
| | | <el-input v-model="sealForm.applicationNum" placeholder="请è¾å
¥ç³è¯·ç¼å·" /> |
| | |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="showSealApplyDialog = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitSealApplication">æäº¤ç³è¯·</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </FormDialog> |
| | | |
| | | <!-- è§ç« å¶åº¦åå¸å¯¹è¯æ¡ --> |
| | | <!-- <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? 'åå¸å¶åº¦' : 'ç¼è¾å¶åº¦'" width="800px"> |
| | |
| | | </el-dialog> --> |
| | | |
| | | <!-- ç¨å°è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="showSealDetailDialog" title="ç¨å°ç³è¯·è¯¦æ
" width="700px"> |
| | | <FormDialog |
| | | v-model="showSealDetailDialog" |
| | | title="ç¨å°ç³è¯·è¯¦æ
" |
| | | :width="'700px'" |
| | | @close="closeSealDetailDialog" |
| | | @confirm="closeSealDetailDialog" |
| | | @cancel="closeSealDetailDialog" |
| | | > |
| | | <div v-if="currentSealDetail" class="mb10"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="ç³è¯·ç¼å·">{{ currentSealDetail.id }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·ç¼å·">{{ currentSealDetail.applicationNum }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·æ é¢">{{ currentSealDetail.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·äºº">{{ currentSealDetail.createUserName }}</el-descriptions-item> |
| | | <el-descriptions-item label="æå±é¨é¨">{{ currentSealDetail.department }}</el-descriptions-item> |
| | |
| | | <el-descriptions-item label="ç³è¯·åå " :span="2">{{ currentSealDetail.reason }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | </el-dialog> |
| | | </FormDialog> |
| | | |
| | | <!-- è§ç« å¶åº¦è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="showRegulationDetailDialog" title="è§ç« å¶åº¦è¯¦æ
" width="800px"> |
| | | <FormDialog |
| | | v-model="showRegulationDetailDialog" |
| | | title="è§ç« å¶åº¦è¯¦æ
" |
| | | :width="'800px'" |
| | | @close="closeRegulationDetailDialog" |
| | | @confirm="handleRegulationDetailConfirm" |
| | | @cancel="closeRegulationDetailDialog" |
| | | > |
| | | <div v-if="currentRegulationDetail"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="å¶åº¦ç¼å·">{{ currentRegulationDetail.id }}</el-descriptions-item> |
| | |
| | | <el-button type="success" @click="resetForm(currentRegulationDetail)">确认æ¥ç</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | </FormDialog> |
| | | |
| | | <!-- çæ¬åå²å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="showVersionHistoryDialog" title="çæ¬åå²" width="800px"> |
| | | <FormDialog |
| | | v-model="showVersionHistoryDialog" |
| | | title="çæ¬åå²" |
| | | :width="'800px'" |
| | | @close="closeVersionHistoryDialog" |
| | | @confirm="closeVersionHistoryDialog" |
| | | @cancel="closeVersionHistoryDialog" |
| | | > |
| | | <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="version" label="çæ¬å·" width="100" /> |
| | | <el-table-column prop="updateTime" label="æ´æ°æ¶é´" width="180" /> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | </FormDialog> |
| | | |
| | | <!-- é
è¯»ç¶æå¯¹è¯æ¡ --> |
| | | <el-dialog v-model="showReadStatusDialog" title="é
è¯»ç¶æ" width="800px"> |
| | | <FormDialog |
| | | v-model="showReadStatusDialog" |
| | | title="é
è¯»ç¶æ" |
| | | :width="'800px'" |
| | | @close="closeReadStatusDialog" |
| | | @confirm="closeReadStatusDialog" |
| | | @cancel="closeReadStatusDialog" |
| | | > |
| | | <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="employee" label="åå·¥å§å" width="120" /> |
| | | <el-table-column prop="department" label="æå±é¨é¨" width="150" /> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { getUserProfile, userListNoPageByTenantId } from '@/api/system/user.js' |
| | | import useUserStore from '@/store/modules/user' |
| | | import { userLoginFacotryList } from "@/api/system/user.js" |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js" |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue' |
| | | |
| | | // ååºå¼æ°æ® |
| | | const currentUser = ref(null) |
| | |
| | | addSealApplication(sealForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('ç³è¯·æäº¤æå') |
| | | showSealApplyDialog.value = false |
| | | closeSealApplyDialog() |
| | | getSealApplicationList() |
| | | Object.assign(sealForm, { |
| | | applicationNum: '', |
| | |
| | | }) |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg) |
| | | console.log(err.msg) |
| | | }) |
| | | |
| | | } catch (error) { |
| | | ElMessage.error('请å®åç³è¯·ä¿¡æ¯') |
| | | } |
| | | } |
| | | // å
³éç¨å°ç³è¯·å¯¹è¯æ¡ |
| | | const closeSealApplyDialog = () => { |
| | | // æ¸
ç©ºè¡¨åæ°æ® |
| | | Object.assign(sealForm, { |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | approveUserId: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | }) |
| | | // æ¸
é¤è¡¨åéªè¯ç¶æ |
| | | if (sealFormRef.value) { |
| | | sealFormRef.value.clearValidate() |
| | | } |
| | | showSealApplyDialog.value = false |
| | | } |
| | | // å
³éç¨å°è¯¦æ
å¯¹è¯æ¡ |
| | | const closeSealDetailDialog = () => { |
| | | showSealDetailDialog.value = false |
| | | } |
| | | // å
³éè§ç« å¶åº¦è¯¦æ
å¯¹è¯æ¡ |
| | | const closeRegulationDetailDialog = () => { |
| | | showRegulationDetailDialog.value = false |
| | | } |
| | | // å¤çè§ç« å¶åº¦è¯¦æ
确认 |
| | | const handleRegulationDetailConfirm = () => { |
| | | // 妿tableData>0ï¼æ§è¡ç¡®è®¤æ¥çæä½ |
| | | if (currentRegulationDetail.value && tableData.value && tableData.value.length > 0) { |
| | | resetForm(currentRegulationDetail.value) |
| | | } |
| | | closeRegulationDetailDialog() |
| | | } |
| | | // å
³éçæ¬åå²å¯¹è¯æ¡ |
| | | const closeVersionHistoryDialog = () => { |
| | | showVersionHistoryDialog.value = false |
| | | } |
| | | // å
³éé
è¯»ç¶æå¯¹è¯æ¡ |
| | | const closeReadStatusDialog = () => { |
| | | showReadStatusDialog.value = false |
| | | } |
| | | // æ°å¢ |
| | | const handleAdd = () => { |
| | |
| | | }) |
| | | } |
| | | |
| | | // å页ååå¤ç |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getSealApplicationList(); |
| | | }; |
| | | |
| | | // çå¬å¯¹è¯æ¡æå¼ï¼è·åç¨æ·å表 |
| | | watch(showSealApplyDialog, (newVal) => { |
| | | if (newVal) { |
| | |
| | | contractTerm: 0, |
| | | contractStartTime: "", |
| | | contractEndTime: "", |
| | | staffState: "", |
| | | sysPostId: undefined, |
| | | sysDeptId: undefined, |
| | | }, |
| | |
| | | form.value = {...res.data} |
| | | if (form.value.sysPostId === 0) { |
| | | form.value.sysPostId = undefined |
| | | } |
| | | if (form.value.sysDeptId === 0) { |
| | | form.value.sysDeptId = undefined |
| | | } |
| | | // ç¼è¾æ¶ä¹è®¡ç®ä¸æ¬¡ååå¹´é |
| | | calculateContractTerm(); |
| | |
| | | if (!form.value.sysPostId) { |
| | | form.value.sysPostId = 0; |
| | | } |
| | | if (!form.value.sysDeptId) { |
| | | form.value.sysDeptId = 0; |
| | | } |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | form.value.staffState = 1 |
| | | if (operationType.value === "add") { |
| | | createStaffOnJob(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | |
| | | item.futureTicketsAmount !== undefined ? item.futureTicketsAmount : (item.taxInclusiveTotalPrice || 0) |
| | | ); |
| | | |
| | | // æ°å¢æ¶ï¼æ¬æ¬¡å¼ç¥¨é»è®¤ä¸å¡«ï¼0ï¼ï¼é¿å
䏿å¼å°±æâæªæ¥ç¥¨æ°âæ£æ 0 |
| | | item.ticketsNum = 0; |
| | | item.ticketsAmount = 0; |
| | | |
| | | // 页é¢å±ç¤ºçâæªæ¥ç¥¨æ°/æªæ¥ç¥¨éé¢âé»è®¤å±ç¤ºåå§æªæ¥å¼ |
| | | item.futureTickets = item.tempFutureTickets; |
| | | item.futureTicketsAmount = item.tempFutureTicketsAmount; |
| | | // æ°å¢æ¶ï¼æ¬æ¬¡å¼ç¥¨æ°é»è®¤ = æªæ¥ç¥¨æ°ï¼ä¸ä¸è½å¤§äºæªæ¥ç¥¨æ°ï¼ |
| | | item.ticketsNum = Number(item.tempFutureTickets || 0); |
| | | // èå¨è®¡ç®æ¬æ¬¡å¼ç¥¨éé¢ãæªæ¥ç¥¨æ°ãæªæ¥ç¥¨éé¢ |
| | | const unitPrice = Number(item.taxInclusiveUnitPrice || 0); |
| | | item.ticketsAmount = Number((item.ticketsNum * unitPrice).toFixed(2)); |
| | | item.futureTickets = Number((item.tempFutureTickets - item.ticketsNum).toFixed(2)); |
| | | item.futureTicketsAmount = Number( |
| | | (item.tempFutureTicketsAmount - item.ticketsAmount).toFixed(2) |
| | | ); |
| | | }); |
| | | |
| | | form.productData = allProductData; |
| | |
| | | const totalAmount = allProductData.reduce((sum, item) => { |
| | | return sum + (Number(item.ticketsAmount) || 0); |
| | | }, 0); |
| | | form.invoiceAmount = totalAmount.toFixed(2); |
| | | form.invoiceAmount = Number(totalAmount.toFixed(2)); |
| | | |
| | | // åå¨éä¸çååæ°æ® |
| | | selectedContracts.value = selectedRows; |
| | |
| | | row.ticketsNum = Number(row.tempFutureTickets || 0); |
| | | } |
| | | // è®¡ç®æ¬æ¬¡æ¥ç¥¨éé¢ |
| | | row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2) |
| | | row.ticketsAmount = Number((Number(row.ticketsNum) * Number(row.taxInclusiveUnitPrice || 0)).toFixed(2)); |
| | | // è®¡ç®æªæ¥ç¥¨æ° |
| | | row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2) |
| | | row.futureTickets = Number((Number(row.tempFutureTickets || 0) - Number(row.ticketsNum || 0)).toFixed(2)); |
| | | // è®¡ç®æªæ¥ç¥¨éé¢ |
| | | row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2) |
| | | row.futureTicketsAmount = Number((Number(row.tempFutureTicketsAmount || 0) - Number(row.ticketsAmount || 0)).toFixed(2)); |
| | | calculateinvoiceAmount(); |
| | | }; |
| | | |
| | |
| | | proxy.$modal.msgWarning("æ¬æ¬¡å¼ç¥¨æ°ä¸è½å¤§äºæªæ¥ç¥¨æ°"); |
| | | row.ticketsNum = Number(row.tempFutureTickets || 0); |
| | | // éæ°è®¡ç®æ¬æ¬¡æ¥ç¥¨éé¢ |
| | | row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2); |
| | | row.ticketsAmount = Number((Number(row.ticketsNum) * Number(row.taxInclusiveUnitPrice || 0)).toFixed(2)); |
| | | } |
| | | // è®¡ç®æªæ¥ç¥¨æ° |
| | | row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2) |
| | | row.futureTickets = Number((Number(row.tempFutureTickets || 0) - Number(row.ticketsNum || 0)).toFixed(2)); |
| | | // è®¡ç®æªæ¥ç¥¨éé¢ |
| | | row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2) |
| | | row.futureTicketsAmount = Number((Number(row.tempFutureTicketsAmount || 0) - Number(row.ticketsAmount || 0)).toFixed(2)); |
| | | calculateinvoiceAmount(); |
| | | }; |
| | | |
| | |
| | | invoiceAmountTotal += Number(item.ticketsAmount); |
| | | } |
| | | }); |
| | | form.invoiceAmount = invoiceAmountTotal.toFixed(2); |
| | | form.invoiceAmount = Number(invoiceAmountTotal.toFixed(2)); |
| | | }; |
| | | |
| | | const open = async (type, selectedRows) => { |
| | |
| | | |
| | | // å¦ææ¯æ¹éæä½ï¼è®¾ç½®æ é¢ |
| | | if (Array.isArray(selectedRows) && selectedRows.length > 1) { |
| | | modalOptions.title = `æ¹éæ°å¢ (${selectedRows.length}æ¡)`; |
| | | modalOptions.value = { |
| | | ...(modalOptions.value || {}), |
| | | title: `æ¹éæ°å¢ (${selectedRows.length}æ¡)`, |
| | | }; |
| | | } else { |
| | | modalOptions.title = type === "add" ? "æ°å¢" : "ç¼è¾"; |
| | | modalOptions.value = { |
| | | ...(modalOptions.value || {}), |
| | | title: type === "add" ? "æ°å¢" : "ç¼è¾", |
| | | }; |
| | | } |
| | | |
| | | // 妿æ¯å个æä½ï¼è·åid |
| | |
| | | gePurchaseListPage, |
| | | { |
| | | purchaseContractNumber: undefined, |
| | | // åªæ¥è¯¢å·²å®¡æ¹éè¿ï¼ææå®ç¶æä¸º 3ï¼çè®°å½ |
| | | approvalStatus: 3, |
| | | }, |
| | | [ |
| | | { |
| | |
| | | { |
| | | label: "ä¾åºååç§°", |
| | | prop: "supplierName", |
| | | width:240 |
| | | }, |
| | | { |
| | | label: "仿¬¾ç¶æ", |
| | |
| | | label: "产å大类", |
| | | prop: "productCategory", |
| | | showOverflowTooltip: true, |
| | | width: 100 |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | |
| | | { |
| | | label: "已仿¬¾éé¢(å
)", |
| | | prop: "ticketsTotal", |
| | | width: 120, |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | |
| | | { |
| | | label: "å¾
仿¬¾éé¢(å
)", |
| | | prop: "pendingTicketsTotal", |
| | | width: 120, |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | |
| | | searchForm: { |
| | | supplierNameOrContractNo: "", |
| | | status: false, |
| | | // åªæ¥è¯¢å®¡æ¹ç¶æä¸º 3 çè®°å½ |
| | | approvalStatus: 3, |
| | | }, |
| | | form: { |
| | | purchaseContractNumber: "", |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">çäº§æ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | <span class="search_title ml10">ç产人ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.schedulingUserName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | <span class="search_title ml10">ååå·ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.salesContractNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | <div class="content-layout"> |
| | | <!-- 左侧å°è´¦ + é¡¶é¨çé --> |
| | | <div class="left-panel"> |
| | | <div class="left-header"> |
| | | <!-- <div class="left-title">ç产å°è´¦</div> --> |
| | | <el-radio-group v-model="dateType" size="small" @change="handleDateTypeChange"> |
| | | <el-radio-button label="day">æ¥</el-radio-button> |
| | | <el-radio-button label="month">æ</el-radio-button> |
| | | </el-radio-group> |
| | | |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="leftTableColumn" |
| | | :tableData="leftTableData" |
| | | :tableLoading="tableLoading" |
| | | @rowClick="handleLeftRowClick" |
| | | ></PIMTable> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | |
| | | <!-- å³ä¾§æç»ï¼åæå
å®¹ï¼ --> |
| | | <div class="right-panel"> |
| | | <div class="header-filters"> |
| | | <el-button @click="handleOut" class="ml10">导åº</el-button> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | style="margin-right: 20px;" |
| | | @pagination="pagination" |
| | | ></PIMTable> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | ></PIMTable> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | width: 100, |
| | | }, |
| | | ]); |
| | | |
| | | // å·¦ä¾§æ±æ»å°è´¦åï¼ç产人ã产éãå·¥èµãåæ ¼çï¼ |
| | | const leftTableColumn = ref([ |
| | | { |
| | | label: "ç产人", |
| | | prop: "schedulingUserName", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "产é", |
| | | prop: "finishedNum", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "å·¥èµ", |
| | | prop: "wages", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "åæ ¼ç", |
| | | prop: "qualifiedRate", |
| | | width: 100, |
| | | }, |
| | | ]); |
| | | |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const leftTableData = ref([]); |
| | | // æ¥ / æ 忢ï¼é»è®¤ææ¥ï¼ |
| | | const dateType = ref("day"); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.dateType = dateType.value; |
| | | params.entryDate = undefined |
| | | productionAccountingListPage(params).then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | const records = res.data.records || []; |
| | | tableData.value = records; |
| | | page.total = res.data.total || 0; |
| | | buildLeftTableData(records); |
| | | }); |
| | | }; |
| | | |
| | | // æå»ºå·¦ä¾§æ±æ»å°è´¦ï¼æçäº§äººæ±æ»äº§éãå·¥èµçï¼ |
| | | const buildLeftTableData = (records) => { |
| | | const map = {}; |
| | | records.forEach((item) => { |
| | | const key = item.schedulingUserName || "æªç¥"; |
| | | if (!map[key]) { |
| | | map[key] = { |
| | | id: key, |
| | | schedulingUserName: key, |
| | | finishedNum: 0, |
| | | wages: 0, |
| | | qualifiedRate: item.qualifiedRate ?? null, |
| | | }; |
| | | } |
| | | map[key].finishedNum += Number(item.finishedNum || 0); |
| | | map[key].wages += Number(item.wages || 0); |
| | | if (item.qualifiedRate != null) { |
| | | map[key].qualifiedRate = item.qualifiedRate; |
| | | } |
| | | }); |
| | | leftTableData.value = Object.values(map); |
| | | }; |
| | | |
| | | // 左侧æ¥/æåæ¢ |
| | | const handleDateTypeChange = () => { |
| | | // è¿éåªä½ä¸ºç鿡件çä¸é¨åï¼ç´æ¥éæ°æ¥è¯¢å表 |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // ç¹å»å·¦ä¾§è¡ï¼å·å³ä¾§æç»ï¼æçäº§äººè¿æ»¤ï¼ |
| | | const handleLeftRowClick = (row) => { |
| | | searchForm.value.schedulingUserName = row.schedulingUserName || ""; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| | | <style scoped lang="scss"> |
| | | .content-layout { |
| | | display: flex; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .left-panel { |
| | | flex: 0 0 50%; |
| | | max-width: 50%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .right-panel { |
| | | flex: 0 0 50%; |
| | | max-width: 49%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .left-header { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .left-title { |
| | | font-size: 16px; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .header-filters { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | justify-content: flex-end; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .search_title { |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .ml10 { |
| | | margin-left: 10px; |
| | | } |
| | | </style> |
| | |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js"; |
| | | // import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | import {productionDispatch} from "@/api/productionManagement/productionOrder.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | :row-class-name="tableRowClassName" |
| | | @pagination="pagination"> |
| | | <template #completionStatus="{ row }"> |
| | | <el-progress |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "äº¤ä»æ¥æ", |
| | | prop: "deliveryDate", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: 120, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | |
| | | if (p < 50) return "#e6a23c"; |
| | | if (p < 80) return "#409eff"; |
| | | return "#67c23a"; |
| | | }; |
| | | |
| | | // æ·»å 表è¡ç±»åæ¹æ³ |
| | | const tableRowClassName = ({ row }) => { |
| | | 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'; |
| | | } |
| | | }; |
| | | |
| | | // ç»å®å·¥èºè·¯çº¿å¼¹æ¡ |
| | |
| | | <style scoped lang="scss"> |
| | | .search_form{ |
| | | align-items: start; |
| | | }</style> |
| | | } |
| | | |
| | | ::v-deep .yellow { |
| | | background-color: #FAF0DE; |
| | | } |
| | | |
| | | ::v-deep .pink { |
| | | background-color: #FAE1DE; |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | background-color: #f80202; |
| | | } |
| | | |
| | | ::v-deep .purple{ |
| | | background-color: #F4DEFA; |
| | | } |
| | | </style> |
| | |
| | | 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 || "æªç¥é误")); |
| | | }; |
| | | |
| | |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="paginationSearch" |
| | | height="500" |
| | | > |
| | | </PIMTable> |
| | | <pagination |
| | | style="margin: 10px 0" |
| | | v-show="total > 0" |
| | | @pagination="paginationSearch" |
| | | :total="total" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | /> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | |
| | | qualityInspectFileDel, |
| | | qualityInspectFileListPage |
| | | } from "@/api/qualityManagement/qualityInspectFile.js"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const total = ref(0); |
| | | const tableData = ref([]); |
| | | const fileList = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | qualityInspectFileListPage({inspectId: currentId.value}).then(res => { |
| | | qualityInspectFileListPage({inspectId: currentId.value, ...page}).then(res => { |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | page.total = res.data.total; |
| | | }) |
| | | } |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | 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-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æï¼" prop="inspectState"> |
| | | <el-select v-model="form.inspectState"> |
| | | <el-option label="å¾
å¤ç" :value="0" /> |
| | | <el-option label="å·²å¤ç" :value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç±»å«ï¼" prop="inspectType"> |
| | | <el-select v-model="form.inspectType"> |
| | | <el-option label="åæææ£éª" :value="0" /> |
| | |
| | | </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, |
| | |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | inspectState: '', |
| | | inspectType: '', |
| | | defectivePhenomena: '', |
| | | dealResult: '', |
| | |
| | | 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) => { |
| | |
| | | getProductOptions(); |
| | | if (operationType.value === 'edit') { |
| | | getQualityUnqualifiedInfo(row.id).then(res => { |
| | | form.value = {...res.data} |
| | | const { inspectState, ...rest } = (res.data || {}) |
| | | form.value = { ...rest } |
| | | }) |
| | | } |
| | | } |
| | |
| | | }; |
| | | 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++) { |
| | |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | // ç¶æåæ®µä¸å¨è¡¨åå¡«åï¼ä¹ä¸ä¼ ç»å端 |
| | | const { inspectState, ...payload } = (form.value || {}) |
| | | if (operationType.value === "add") { |
| | | qualityUnqualifiedAdd(form.value).then(res => { |
| | | qualityUnqualifiedAdd(payload).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } else { |
| | | qualityUnqualifiedUpdate(form.value).then(res => { |
| | | qualityUnqualifiedUpdate(payload).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æï¼" prop="inspectState"> |
| | | <el-select v-model="form.inspectState"> |
| | | <el-option label="å¾
å¤ç" :value="0" /> |
| | | <el-option label="å·²å¤ç" :value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç±»å«ï¼" prop="inspectType"> |
| | | <el-select v-model="form.inspectType" disabled> |
| | | <el-option label="åæææ£éª" :value="0" /> |
| | |
| | | import {productTreeList} from "@/api/basicData/product.js"; |
| | | import { |
| | | getQualityUnqualifiedInfo, |
| | | qualityUnqualifiedAdd, |
| | | qualityUnqualifiedUpdate |
| | | qualityUnqualifiedDeal |
| | | } from "@/api/qualityManagement/nonconformingManagement.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | inspectState: '', |
| | | inspectType: '', |
| | | defectivePhenomena: '', |
| | | dealResult: '', |
| | |
| | | getProductOptions(); |
| | | if (operationType.value === 'edit') { |
| | | getQualityUnqualifiedInfo(row.id).then(res => { |
| | | form.value = {...res.data} |
| | | const { inspectState, ...rest } = (res.data || {}) |
| | | form.value = { ...rest } |
| | | }) |
| | | } |
| | | } |
| | |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add") { |
| | | qualityUnqualifiedAdd(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } else { |
| | | qualityUnqualifiedUpdate(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | // ç¶æåæ®µä¸å¨è¡¨åå¡«åï¼ä¹ä¸ä¼ ç»å端ï¼å¤çç»ä¸èµ° /deal æ¥å£ |
| | | const { inspectState, ...payload } = (form.value || {}) |
| | | qualityUnqualifiedDeal(payload).then(() => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | }, |
| | | disabled: (row) => row.inspectState === 1, |
| | | }, |
| | | { |
| | | name: "å¤ç", |
| | |
| | | clickFun: (row) => { |
| | | openInspectionForm("edit", row); |
| | | }, |
| | | disabled: (row) => row.inspectState === 1, |
| | | }, |
| | | ], |
| | | }, |
| | |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (type !== 'add' && row?.inspectState === 1) { |
| | | proxy.$modal.msgWarning("å·²å¤ççæ°æ®ä¸è½åç¼è¾"); |
| | | return; |
| | | } |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | // æå¼å¤çå¼¹æ¡ |
| | | const openInspectionForm = (type, row) => { |
| | | if (row?.inspectState === 1) { |
| | | proxy.$modal.msgWarning("å·²å¤ççæ°æ®ä¸è½åå¤ç"); |
| | | return; |
| | | } |
| | | nextTick(() => { |
| | | inspectionFormDia.value?.openDialog(type, row) |
| | | }) |
| | |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="paginationSearch" |
| | | height="500" |
| | | > |
| | | </PIMTable> |
| | | <pagination |
| | | style="margin: 10px 0" |
| | | v-show="total > 0" |
| | | @pagination="paginationSearch" |
| | | :total="total" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | /> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | |
| | | qualityInspectFileDel, |
| | | qualityInspectFileListPage |
| | | } from "@/api/qualityManagement/qualityInspectFile.js"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const total = ref(0); |
| | | const tableData = ref([]); |
| | | const fileList = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | qualityInspectFileListPage({inspectId: currentId.value}).then(res => { |
| | | qualityInspectFileListPage({inspectId: currentId.value, ...page}).then(res => { |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | page.total = res.data.total; |
| | | }) |
| | | } |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | 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" }], |
| | |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="paginationSearch" |
| | | height="500" |
| | | > |
| | | </PIMTable> |
| | | <pagination |
| | | style="margin: 10px 0" |
| | | v-show="total > 0" |
| | | @pagination="paginationSearch" |
| | | :total="total" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | /> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | |
| | | qualityInspectFileDel, |
| | | qualityInspectFileListPage |
| | | } from "@/api/qualityManagement/qualityInspectFile.js"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const total = ref(0); |
| | | const tableData = ref([]); |
| | | const fileList = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | const getList = () => { |
| | | qualityInspectFileListPage({inspectId: currentId.value, ...page}).then(res => { |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | page.total = res.data.total; |
| | | }) |
| | | } |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | 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"}], |
| | |
| | | itemHeight: 12, |
| | | textStyle: { color: '#B8C8E0', fontSize: 14 }, |
| | | data: [ |
| | | { name: 'åºåº', itemStyle: { color: '#4EE4FF' } }, |
| | | { name: 'å
¥åº', itemStyle: { color: '#00D68F' } }, |
| | | { name: 'åºåº', itemStyle: { color: 'rgba(11, 137, 254, 1)' } }, |
| | | { name: 'å
¥åº', itemStyle: { color: 'rgba(11, 249, 254, 1)' } }, |
| | | ], |
| | | } |
| | | |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="产åéè´éé¢åæ" /> |
| | | <PanelHeader title="éè´ååå¸" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <CarouselCards :items="cardItems" :visible-count="3" /> |
| | | <div class="pie-chart-wrapper"> |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="产åéå®éé¢åæ" /> |
| | | <PanelHeader title="éå®ååå¸" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <CarouselCards :items="cardItems" :visible-count="3" /> |
| | | <div class="pie-chart-wrapper"> |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="客æ·éé¢è´¡ç®æå" /> |
| | | <PanelHeader title="客æ·è´¡ç®æå" /> |
| | | <div class="panel-item-customers"> |
| | | <div class="switch-container"> |
| | | <DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" /> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-radio-group |
| | | v-model="currentValue" |
| | | class="date-type-switch" |
| | | @change="handleChange" |
| | | > |
| | | <el-radio-button :label="1">å¨</el-radio-button> |
| | | <el-radio-button :label="2">æ</el-radio-button> |
| | | <el-radio-button :label="3">å£åº¦</el-radio-button> |
| | | </el-radio-group> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, watch } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Number, |
| | | default: 1, // é»è®¤éä¸"å¨" |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:modelValue', 'change']) |
| | | |
| | | const currentValue = ref(props.modelValue) |
| | | |
| | | // çå¬å¤é¨å¼åå |
| | | watch( |
| | | () => props.modelValue, |
| | | (newVal) => { |
| | | currentValue.value = newVal |
| | | } |
| | | ) |
| | | |
| | | // å¤çå¼åå |
| | | const handleChange = (value) => { |
| | | emit('update:modelValue', value) |
| | | emit('change', value) |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .date-type-switch { |
| | | display: inline-flex; |
| | | } |
| | | |
| | | /* æªéä¸ç¶æçæ ·å¼ */ |
| | | .date-type-switch :deep(.el-radio-button__inner) { |
| | | background-color: rgba(26, 88, 176, 0.3); |
| | | color: rgba(184, 200, 224, 0.8); |
| | | border-color: rgba(255, 255, 255, 0.2); |
| | | border-radius: 0; |
| | | padding: 6px 20px; |
| | | font-size: 14px; |
| | | transition: all 0.3s; |
| | | } |
| | | |
| | | /* 第ä¸ä¸ªæé®å·¦ä¾§åè§ */ |
| | | .date-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) { |
| | | border-top-left-radius: 4px; |
| | | border-bottom-left-radius: 4px; |
| | | } |
| | | |
| | | /* æåä¸ä¸ªæé®å³ä¾§åè§ */ |
| | | .date-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) { |
| | | border-top-right-radius: 4px; |
| | | border-bottom-right-radius: 4px; |
| | | } |
| | | |
| | | /* æé®ä¹é´çåé线 */ |
| | | .date-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) { |
| | | border-right: 1px solid rgba(255, 255, 255, 0.2); |
| | | } |
| | | |
| | | /* éä¸ç¶æçæ ·å¼ */ |
| | | .date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) { |
| | | background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%); |
| | | color: #ffffff; |
| | | border-color: rgba(51, 120, 255, 0.8); |
| | | box-shadow: none; |
| | | } |
| | | |
| | | /* æ¬åææ */ |
| | | .date-type-switch :deep(.el-radio-button__inner:hover) { |
| | | color: rgba(184, 200, 224, 1); |
| | | border-color: rgba(255, 255, 255, 0.3); |
| | | } |
| | | |
| | | /* éä¸ç¶ææ¬å */ |
| | | .date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) { |
| | | background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%); |
| | | color: #ffffff; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="panel-header"> |
| | | <span class="panel-title">{{ title }}</span> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | defineProps({ |
| | | title: { |
| | | type: String, |
| | | required: true, |
| | | default: '' |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .panel-header { |
| | | background-image: url("@/assets/BI/kehuhetongback@2x.png"); |
| | | background-size: 100% 100%; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | } |
| | | |
| | | .panel-title { |
| | | width: 100%; |
| | | font-weight: 500; |
| | | font-size: 16px; |
| | | color: #D9ECFF; |
| | | padding-left: 46px; |
| | | line-height: 36px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-radio-group |
| | | v-model="currentValue" |
| | | class="product-type-switch" |
| | | @change="handleChange" |
| | | > |
| | | <el-radio-button |
| | | v-for="opt in options" |
| | | :key="opt.label" |
| | | :label="opt.label" |
| | | > |
| | | {{ opt.text }} |
| | | </el-radio-button> |
| | | </el-radio-group> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, watch } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: [Number, String], |
| | | default: 1, // é»è®¤éä¸ç¬¬ä¸ä¸ª |
| | | }, |
| | | // å¯é
ç½®é项ï¼é»è®¤æ¯åç»ä»¶çãåææ / åæå / æåã |
| | | options: { |
| | | type: Array, |
| | | default: () => [ |
| | | { label: 1, text: 'åææ' }, |
| | | { label: 3, text: 'åæå' }, |
| | | { label: 2, text: 'æå' }, |
| | | ], |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:modelValue', 'change']) |
| | | |
| | | const currentValue = ref(props.modelValue) |
| | | |
| | | watch( |
| | | () => props.modelValue, |
| | | (newVal) => { |
| | | currentValue.value = newVal |
| | | } |
| | | ) |
| | | |
| | | const handleChange = (value) => { |
| | | emit('update:modelValue', value) |
| | | emit('change', value) |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .product-type-switch { |
| | | display: inline-flex; |
| | | } |
| | | |
| | | .product-type-switch :deep(.el-radio-button__inner) { |
| | | background-color: rgba(26, 88, 176, 0.3); |
| | | color: rgba(184, 200, 224, 0.8); |
| | | border-color: rgba(255, 255, 255, 0.2); |
| | | border-radius: 0; |
| | | padding: 6px 20px; |
| | | font-size: 14px; |
| | | transition: all 0.3s; |
| | | } |
| | | |
| | | .product-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) { |
| | | border-top-left-radius: 4px; |
| | | border-bottom-left-radius: 4px; |
| | | } |
| | | |
| | | .product-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) { |
| | | border-top-right-radius: 4px; |
| | | border-bottom-right-radius: 4px; |
| | | } |
| | | |
| | | .product-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) { |
| | | border-right: 1px solid rgba(255, 255, 255, 0.2); |
| | | } |
| | | |
| | | .product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) { |
| | | background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%); |
| | | color: #ffffff; |
| | | border-color: rgba(51, 120, 255, 0.8); |
| | | box-shadow: none; |
| | | } |
| | | |
| | | .product-type-switch :deep(.el-radio-button__inner:hover) { |
| | | color: rgba(184, 200, 224, 1); |
| | | border-color: rgba(255, 255, 255, 0.3); |
| | | } |
| | | |
| | | .product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) { |
| | | background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%); |
| | | color: #ffffff; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="婿¶¦è¶å¿" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="barLegend" |
| | | :series="barSeries1" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }" |
| | | style="height: 260px" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import Echarts from '@/components/Echarts/echarts.vue' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | import { profitTrendAnalysis } from '@/api/viewIndex.js' |
| | | |
| | | const chartStyle = { width: '100%', height: '150%' } |
| | | const grid = { left: '3%', right: '4%', bottom: '3%', top: '4%', containLabel: true } |
| | | const barLegend = { show: false, textStyle: { color: '#B8C8E0' }, data: ['婿¶¦'] } |
| | | const barSeries1 = ref([ |
| | | { |
| | | name: '婿¶¦', |
| | | type: 'bar', |
| | | barGap: 0, |
| | | barWidth: 30, |
| | | emphasis: { focus: 'series' }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, y: 1, x2: 0, y2: 0, |
| | | colorStops: [ |
| | | { offset: 0, color: 'rgba(0,164,237,0)' }, |
| | | { offset: 1, color: '#4EE4FF' }, |
| | | ], |
| | | }, |
| | | }, |
| | | data: [], |
| | | }, |
| | | ]) |
| | | |
| | | const xAxis1 = ref([ |
| | | { |
| | | type: 'category', |
| | | axisTick: { show: false }, |
| | | axisLabel: { color: '#B8C8E0' }, |
| | | data: [], |
| | | }, |
| | | ]) |
| | | |
| | | const tooltip = { |
| | | trigger: 'axis', |
| | | axisPointer: { type: 'shadow' }, |
| | | formatter(params) { |
| | | let result = params[0].axisValueLabel + '<br/>' |
| | | params.forEach((item) => { |
| | | result += `<div>${item.marker} ${item.seriesName}: ${item.value} å
</div>` |
| | | }) |
| | | return result |
| | | }, |
| | | } |
| | | |
| | | const yAxis1 = [{ type: 'value', axisLabel: { color: '#B8C8E0' } }] |
| | | |
| | | const fetchData = () => { |
| | | profitTrendAnalysis() |
| | | .then((res) => { |
| | | if (res.code === 200 && Array.isArray(res.data)) { |
| | | const list = res.data |
| | | xAxis1.value[0].data = list.map((d) => d.name) |
| | | barSeries1.value[0].data = list.map((d) => parseFloat(d.value) || 0) |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error('è·å婿¶¦è¶å¿åæå¤±è´¥:', err) |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fetchData() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .main-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .panel-item-customers { |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 428px; |
| | | } |
| | | </style> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <!-- 设å¤ç»è®¡ --> |
| | | <div class="equipment-stats"> |
| | | <div class="equipment-header"> |
| | | <img |
| | | src="@/assets/BI/shujutongjiicon@2x.png" |
| | | alt="徿 " |
| | | class="equipment-icon" |
| | | /> |
| | | <span class="equipment-title">æ¶æ¯å¯¹æ¯åæ</span> |
| | | </div> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }" |
| | | style="height: 260px" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | import Echarts from '@/components/Echarts/echarts.vue' |
| | | import { incomeExpenseAnalysis } from '@/api/viewIndex.js' |
| | | |
| | | const chartStyle = { width: '100%', height: '100%' } |
| | | const grid = { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | top: '16%', |
| | | containLabel: true, |
| | | } |
| | | |
| | | const lineLegend = { |
| | | show: true, |
| | | top: '2%', |
| | | left: 'center', |
| | | itemGap: 24, |
| | | itemWidth: 12, |
| | | itemHeight: 12, |
| | | textStyle: { color: '#B8C8E0', fontSize: 14 }, |
| | | data: [ |
| | | { name: 'æ¶å
¥', itemStyle: { color: 'rgba(11, 137, 254, 1)' } }, |
| | | { name: 'æ¯åº', itemStyle: { color: 'rgba(11, 249, 254, 1)' } }, |
| | | ], |
| | | } |
| | | |
| | | const xAxis1 = ref([ |
| | | { |
| | | type: 'category', |
| | | data: [], |
| | | axisTick: { show: false }, |
| | | axisLine: { show: false, lineStyle: { color: 'rgba(184, 200, 224, 0.3)' } }, |
| | | axisLabel: { color: '#B8C8E0', fontSize: 12 }, |
| | | splitLine: { show: false, lineStyle: { type: 'dashed', color: 'rgba(184, 200, 224, 0.2)' } }, |
| | | }, |
| | | ]) |
| | | |
| | | const yAxis1 = [ |
| | | { |
| | | type: 'value', |
| | | name: 'åä½: å
', |
| | | nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 0] }, |
| | | axisLine: { show: false }, |
| | | axisTick: { show: false }, |
| | | axisLabel: { color: '#B8C8E0', fontSize: 12 }, |
| | | splitLine: { lineStyle: { color: '#B8C8E0' } }, |
| | | }, |
| | | ] |
| | | |
| | | const lineSeries = ref([ |
| | | { |
| | | name: 'æ¶å
¥', |
| | | type: 'line', |
| | | smooth: false, |
| | | showSymbol: true, |
| | | symbol: 'circle', |
| | | symbolSize: 8, |
| | | lineStyle: { color: 'rgba(11, 137, 254, 1)', width: 2 }, |
| | | itemStyle: { color: 'rgba(11, 137, 254, 1)', borderWidth: 0 }, |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: 'rgba(11, 137, 254, 0.40)' }, |
| | | { offset: 1, color: 'rgba(11, 137, 254, 0.05)' }, |
| | | ]), |
| | | }, |
| | | data: [], |
| | | emphasis: { focus: 'series' }, |
| | | }, |
| | | { |
| | | name: 'æ¯åº', |
| | | type: 'line', |
| | | smooth: false, |
| | | showSymbol: true, |
| | | symbol: 'circle', |
| | | symbolSize: 8, |
| | | lineStyle: { color: 'rgba(11, 249, 254, 1)', width: 2 }, |
| | | itemStyle: { color: 'rgba(11, 249, 254, 1)', borderWidth: 0 }, |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: 'rgba(11, 249, 254, 0.5)' }, |
| | | { offset: 1, color: 'rgba(11, 249, 254, 0.05)' }, |
| | | ]), |
| | | }, |
| | | data: [], |
| | | emphasis: { focus: 'series' }, |
| | | }, |
| | | ]) |
| | | |
| | | const tooltip = { |
| | | trigger: 'axis', |
| | | axisPointer: { type: 'line' }, |
| | | formatter(params) { |
| | | let result = params[0].axisValue + '<br/>' |
| | | params.forEach((item) => { |
| | | result += `${item.marker} ${item.seriesName}: ${item.value} å
<br/>` |
| | | }) |
| | | return result |
| | | }, |
| | | } |
| | | |
| | | const fetchData = () => { |
| | | incomeExpenseAnalysis() |
| | | .then((res) => { |
| | | if (res.code === 200 && Array.isArray(res.data)) { |
| | | const list = res.data |
| | | xAxis1.value[0].data = list.map((d) => d.date) |
| | | lineSeries.value[0].data = list.map((d) => Number(d.income) || 0) |
| | | lineSeries.value[1].data = list.map((d) => Number(d.expense) || 0) |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error('è·åæ¶æ¯å¯¹æ¯åæå¤±è´¥:', err) |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fetchData() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .equipment-stats { |
| | | border: 1px solid #1a58b0; |
| | | padding: 0 18px 18px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .equipment-header { |
| | | font-weight: 500; |
| | | font-size: 21px; |
| | | display: flex; |
| | | border-bottom: 1px solid; |
| | | border-image: linear-gradient( |
| | | 270deg, |
| | | rgba(0, 126, 255, 0) 0%, |
| | | rgba(0, 126, 255, 0.4549) 35%, |
| | | #007eff 78%, |
| | | #007eff 100% |
| | | ) |
| | | 1; |
| | | padding-bottom: 2px; |
| | | } |
| | | |
| | | .equipment-title { |
| | | font-weight: 500; |
| | | font-size: 18px; |
| | | background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%); |
| | | -webkit-background-clip: text; |
| | | -webkit-text-fill-color: transparent; |
| | | background-clip: text; |
| | | line-height: 50px; |
| | | } |
| | | |
| | | .equipment-icon { |
| | | width: 50px; |
| | | height: 50px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <!-- 顶鍿¶æ¯å¡ç --> |
| | | <div class="finance-cards"> |
| | | <!-- æåº¦æ¶å
¥ --> |
| | | <div class="finance-card income-card"> |
| | | <div class="icon-box"> |
| | | <img src="@/assets/BI/icon@2x.png" alt="徿 " class="card-icon" /> |
| | | </div> |
| | | <div class="card-body"> |
| | | <div class="card-left"> |
| | | <div class="card-title">æåº¦æ¶å
¥</div> |
| | | <div class="card-amount"> |
| | | <span>{{ formatAmountWanNumber(income.amount) }}</span> |
| | | <span v-if="isWanAmount(income.amount)" class="card-amount-unit">ä¸</span> |
| | | </div> |
| | | </div> |
| | | <div class="card-right"> |
| | | <div class="metric-row"> |
| | | <span class="metric-label">忬¾ç</span> |
| | | <span class="metric-value" :class="metricClass(income.repayRate)"> |
| | | {{ formatPercent(income.repayRate.value) }} |
| | | <span class="arrow">{{ metricArrow(income.repayRate) }}</span> |
| | | </span> |
| | | </div> |
| | | <div class="metric-row"> |
| | | <span class="metric-label">龿æ°</span> |
| | | <span class="metric-value metric-up"> |
| | | {{ formatAmountWanNumber(income.overdueCount) }} |
| | | <span |
| | | v-if="isWanAmount(income.overdueCount)" |
| | | class="metric-unit" |
| | | > |
| | | ä¸ |
| | | </span> |
| | | </span> |
| | | </div> |
| | | <div class="metric-row"> |
| | | <span class="metric-label">龿ç</span> |
| | | <span class="metric-value" :class="metricClass(income.overdueRate)"> |
| | | {{ formatPercent(income.overdueRate.value) }} |
| | | <span class="arrow">{{ metricArrow(income.overdueRate) }}</span> |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æåº¦æ¯åº --> |
| | | <div class="finance-card expense-card"> |
| | | <div class="icon-box"> |
| | | <img src="@/assets/BI/icon@2x.png" alt="徿 " class="card-icon" /> |
| | | </div> |
| | | <div class="card-body"> |
| | | <div class="card-left"> |
| | | <div class="card-title">æåº¦æ¯åº</div> |
| | | <div class="card-amount"> |
| | | <span>{{ formatAmountWanNumber(expense.amount) }}</span> |
| | | <span v-if="isWanAmount(expense.amount)" class="card-amount-unit">ä¸</span> |
| | | </div> |
| | | </div> |
| | | <div class="card-right"> |
| | | <div class="metric-row"> |
| | | <span class="metric-label">仿¬¾ç</span> |
| | | <span class="metric-value" :class="metricClass(expense.netProfit)"> |
| | | {{ formatPercent(expense.netProfit.value) }} |
| | | <span class="arrow">{{ metricArrow(expense.netProfit) }}</span> |
| | | </span> |
| | | </div> |
| | | <div class="metric-row"> |
| | | <span class="metric-label">æ¯å©æ¶¦</span> |
| | | <span class="metric-value metric-down"> |
| | | {{ formatAmountWanNumber(expense.grossProfit) }} |
| | | <span |
| | | v-if="isWanAmount(expense.grossProfit)" |
| | | class="metric-unit" |
| | | > |
| | | ä¸ |
| | | </span> |
| | | </span> |
| | | </div> |
| | | <div class="metric-row"> |
| | | <span class="metric-label">婿¶¦ç</span> |
| | | <span class="metric-value" :class="metricClass(expense.profitRate)"> |
| | | {{ formatPercent(expense.profitRate.value) }} |
| | | <span class="arrow">{{ metricArrow(expense.profitRate) }}</span> |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref } from 'vue' |
| | | import { getMonthlyIncome, getMonthlyExpenditure } from '@/api/viewIndex' |
| | | |
| | | const income = ref({ |
| | | amount: 0, |
| | | repayRate: { value: 0, trend: 0 }, |
| | | overdueCount: 0, |
| | | overdueRate: { value: 0, trend: 0 }, |
| | | }) |
| | | |
| | | const expense = ref({ |
| | | amount: 0, |
| | | netProfit: { value: 0, trend: 0 }, |
| | | grossProfit: 0, |
| | | profitRate: { value: 0, trend: 0 }, |
| | | }) |
| | | |
| | | const fetchMonthlyIncome = async () => { |
| | | const res = await getMonthlyIncome() |
| | | const data = res?.data || {} |
| | | |
| | | income.value.amount = data.monthlyIncome ?? 0 |
| | | const collectionRate = Number(data.collectionRate ?? 0) |
| | | const overdueRate = Number(data.overdueRate ?? 0) |
| | | income.value.repayRate = { |
| | | value: collectionRate, |
| | | trend: collectionRate >= 0 ? 1 : -1, |
| | | } |
| | | income.value.overdueCount = data.overdueNum ?? 0 |
| | | income.value.overdueRate = { |
| | | value: overdueRate, |
| | | trend: overdueRate >= 0 ? 1 : -1, |
| | | } |
| | | } |
| | | |
| | | const fetchMonthlyExpenditure = async () => { |
| | | const res = await getMonthlyExpenditure() |
| | | const data = res?.data || {} |
| | | |
| | | expense.value.amount = data.monthlyExpenditure ?? 0 |
| | | const paymentRate = Number(data.paymentRate ?? 0) |
| | | expense.value.netProfit = { |
| | | value: paymentRate, |
| | | trend: paymentRate >= 0 ? 1 : -1, |
| | | } |
| | | expense.value.grossProfit = data.grossProfit ?? 0 |
| | | |
| | | const profitMarginRate = Number(data.profitMarginRate ?? 0) |
| | | expense.value.profitRate = { |
| | | value: profitMarginRate, |
| | | trend: profitMarginRate >= 0 ? 1 : -1, |
| | | } |
| | | } |
| | | |
| | | const isWanAmount = (val) => { |
| | | const num = Number(val) || 0 |
| | | return Math.abs(num) >= 10000 |
| | | } |
| | | |
| | | const formatAmountWanNumber = (val) => { |
| | | const num = Number(val) || 0 |
| | | if (Math.abs(num) >= 10000) { |
| | | return (num / 10000).toFixed(2) |
| | | } |
| | | return num.toFixed(2) |
| | | } |
| | | |
| | | const formatPercent = (val) => { |
| | | const num = Number(val) || 0 |
| | | // ç¾åæ¯å±ç¤ºå§ç»ç¨ç»å¯¹å¼ï¼å°æ°ä¿çä¸¤ä½ |
| | | return `${Math.abs(num).toFixed(2)}%` |
| | | } |
| | | |
| | | const metricClass = (metric) => { |
| | | if (metric?.trend === undefined || metric?.trend === null) return 'metric-up' |
| | | return Number(metric.trend) >= 0 ? 'metric-up' : 'metric-down' |
| | | } |
| | | |
| | | const metricArrow = (metric) => { |
| | | if (metric?.trend === undefined || metric?.trend === null) return '' |
| | | return Number(metric.trend) >= 0 ? 'â' : 'â' |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fetchMonthlyIncome() |
| | | fetchMonthlyExpenditure() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .finance-cards { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .finance-card { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 18px 10px; |
| | | background-image: url('@/assets/BI/border@2x.png'); |
| | | background-size: 100% 100%; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | min-height: 138px; |
| | | } |
| | | |
| | | .icon-box { |
| | | width: 92px; |
| | | height: 92px; |
| | | /* border: 1px dashed rgba(208, 231, 255, 0.55); */ |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 18px; |
| | | } |
| | | |
| | | .card-icon { |
| | | width: 78px; |
| | | height: 78px; |
| | | } |
| | | |
| | | .card-body { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 18px; |
| | | } |
| | | |
| | | .card-left { |
| | | min-width: 90px; |
| | | } |
| | | |
| | | .card-title { |
| | | font-weight: 400; |
| | | font-size: 18px; |
| | | color: rgba(208, 231, 255, 0.7); |
| | | } |
| | | |
| | | .card-amount { |
| | | font-weight: 500; |
| | | font-size: 36px; |
| | | line-height: 1.1; |
| | | margin-top: 8px; |
| | | display: inline-flex; |
| | | align-items: baseline; |
| | | white-space: nowrap; |
| | | background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%); |
| | | -webkit-background-clip: text; |
| | | -webkit-text-fill-color: transparent; |
| | | background-clip: text; |
| | | } |
| | | |
| | | .card-amount-unit { |
| | | font-size: 20px; |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | .card-right { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | padding-right: 6px; |
| | | } |
| | | |
| | | .metric-row { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | font-size: 14px; |
| | | color: #d0e7ff; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .metric-label { |
| | | margin-right: 12px; |
| | | } |
| | | |
| | | .metric-label { |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .metric-value { |
| | | font-weight: 600; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .metric-unit { |
| | | font-size: 12px; |
| | | margin-left: 2px; |
| | | } |
| | | |
| | | .metric-value .arrow { |
| | | font-size: 13px; |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | .metric-up { |
| | | color: #00c853; |
| | | } |
| | | |
| | | .metric-down { |
| | | color: #ff5252; |
| | | } |
| | | |
| | | |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="ææåæ" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <div class="filters-row"> |
| | | <ProductTypeSwitch |
| | | v-model="amountType" |
| | | :options="amountTypeOptions" |
| | | @change="handleTypeChange" |
| | | /> |
| | | </div> |
| | | <!-- <CarouselCards :items="cardItems" :visible-count="3" /> --> |
| | | <div class="pie-chart-wrapper"> |
| | | <div class="pie-background"></div> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :legend="landLegend" |
| | | :series="landSeries" |
| | | :tooltip="landTooltip" |
| | | :color="landColors" |
| | | :options="pieOptions" |
| | | style="height: 320px" |
| | | class="land-chart" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, computed } from 'vue' |
| | | import Echarts from '@/components/Echarts/echarts.vue' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | import ProductTypeSwitch from './ProductTypeSwitch.vue' |
| | | import { rawMaterialPurchaseAmountRatio } from '@/api/viewIndex.js' |
| | | |
| | | /** |
| | | * @introduction ææ°ç»ä¸keyå¼ç¸åçé£ä¸é¡¹æååºæ¥ï¼ç»æä¸ä¸ªå¯¹è±¡ |
| | | * @param {åæ°ç±»å} array ä¼ å
¥çæ°ç» [{a:"1",b:"2"},{a:"2",b:"3"}] |
| | | * @param {åæ°ç±»å} key 屿§å a |
| | | * @return {è¿åç±»å说æ} |
| | | */ |
| | | function array2obj(array, key) { |
| | | const resObj = {} |
| | | for (let i = 0; i < array.length; i++) { |
| | | resObj[array[i][key]] = array[i] |
| | | } |
| | | return resObj |
| | | } |
| | | |
| | | // å½åç±»åï¼1=æ¯åº 2=æ¶å
¥ |
| | | const amountType = ref(1) |
| | | |
| | | const amountTypeOptions = [ |
| | | { label: 1, text: 'æ¯åº' }, |
| | | { label: 2, text: 'æ¶å
¥' }, |
| | | ] |
| | | |
| | | // æ°æ®åè¡¨ï¼æ¥èªæ¥å£ï¼ |
| | | const dataList = ref([]) |
| | | |
| | | // å¡çæ°æ® |
| | | const cardItems = ref([]) |
| | | |
| | | // é¢è²å表 |
| | | const landColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF'] |
| | | |
| | | const landObjData = computed(() => array2obj(dataList.value, 'name')) |
| | | |
| | | // å¾ä¾é
ç½®ï¼å³ä¾§ç«æï¼ |
| | | const landLegend = computed(() => { |
| | | const data = dataList.value.map((d, idx) => ({ |
| | | name: d.name, |
| | | icon: 'circle', |
| | | textStyle: { |
| | | fontSize: 18, |
| | | color: landColors[idx % landColors.length], |
| | | }, |
| | | })) |
| | | |
| | | return { |
| | | orient: 'vertical', |
| | | top: 'center', |
| | | left: '52%', |
| | | itemGap: 30, |
| | | show: true, |
| | | data: data, |
| | | formatter: function (name) { |
| | | const item = landObjData.value[name] |
| | | if (!item) return name |
| | | return `{title|${name}}{value|${item.value}}{unit|å
}{percent|${item.rate}}{unit|%}` |
| | | }, |
| | | textStyle: { |
| | | rich: { |
| | | value: { |
| | | color: '#43e8fc', |
| | | fontSize: 14, |
| | | fontWeight: 600, |
| | | padding: [0, 0, 0, 10], |
| | | }, |
| | | unit: { |
| | | color: '#82baff', |
| | | fontSize: 12, |
| | | fontWeight: 600, |
| | | padding: [0, 10, 0, 0], |
| | | }, |
| | | percent: { |
| | | color: '#43e8fc', |
| | | fontSize: 14, |
| | | fontWeight: 600, |
| | | padding: [0, 0, 0, 0], |
| | | }, |
| | | title: { |
| | | fontSize: 12, |
| | | padding: [0, 0, 0, 0], |
| | | }, |
| | | }, |
| | | }, |
| | | } |
| | | }) |
| | | |
| | | // æç¤ºæ¡ |
| | | const landTooltip = { |
| | | trigger: 'item', |
| | | formatter: '{a} <br/>{b} : {c}å
({d}%)', |
| | | } |
| | | |
| | | // åå±ç¯å½¢é¥¼å¾ |
| | | // åå±ç¯å½¢é¥¼å¾ |
| | | const landSeries = ref([ |
| | | { |
| | | name: 'ææåæ', |
| | | type: 'pie', |
| | | radius: ['40%', '60%'], |
| | | center: ['25%', '50%'], |
| | | itemStyle: { |
| | | borderColor: '#0a1c3a', |
| | | borderWidth: 2, |
| | | color: function (params) { |
| | | return landColors[params.dataIndex % landColors.length] |
| | | }, |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | minAngle: 15, |
| | | data: dataList.value, |
| | | animationType: 'scale', |
| | | animationEasing: 'elasticOut', |
| | | animationDelay: function () { |
| | | return Math.random() * 200 |
| | | }, |
| | | }, |
| | | { |
| | | // å
å |
| | | type: 'pie', |
| | | radius: ['40%', '45%'], |
| | | center: ['25%', '50%'], |
| | | silent: true, |
| | | label: { |
| | | show: false, |
| | | }, |
| | | labelLine: { |
| | | show: false, |
| | | }, |
| | | itemStyle: { |
| | | color: 'rgba(0, 127, 255, 0.25)', |
| | | }, |
| | | data: [1], |
| | | }, |
| | | ]) |
| | | |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '100%', |
| | | } |
| | | |
| | | const pieOptions = { |
| | | backgroundColor: 'transparent', |
| | | textStyle: { color: '#B8C8E0' }, |
| | | } |
| | | |
| | | const fetchData = () => { |
| | | // ç®åæ¥å£åªææ¯åºææå æ¯ï¼å
忽ç¥ç±»ååæ° |
| | | // é¢çæ©å±ï¼åç»å¯æ ¹æ® amountType åä¸åæ¥å£ |
| | | rawMaterialPurchaseAmountRatio() |
| | | .then((res) => { |
| | | if (res.code === 200 && Array.isArray(res.data)) { |
| | | const items = res.data |
| | | cardItems.value = items.map((item) => ({ |
| | | label: item.name, |
| | | value: item.value, |
| | | unit: 'å
', |
| | | rate: item.rate, |
| | | })) |
| | | dataList.value = items.map((it) => ({ |
| | | name: it.name, |
| | | value: parseFloat(it.value) || 0, |
| | | rate: it.rate, |
| | | children: [], |
| | | })) |
| | | landSeries.value[0].data = dataList.value |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error('è·ååææéè´éé¢å æ¯å¤±è´¥:', err) |
| | | }) |
| | | } |
| | | |
| | | const handleTypeChange = () => { |
| | | fetchData() |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fetchData() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .main-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .filters-row { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | gap: 12px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .panel-item-customers { |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 449px; |
| | | } |
| | | |
| | | .pie-chart-wrapper { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 320px; |
| | | background: transparent; |
| | | } |
| | | |
| | | |
| | | .pie-background { |
| | | position: absolute; |
| | | left: 25%; |
| | | top: 50%; |
| | | transform: translate(-51.5%, -50%); |
| | | width: 310px; |
| | | height: 310px; |
| | | background-image: url('@/assets/BI/ç«ç°å¾è¾¹æ¡.png'); |
| | | background-size: contain; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | z-index: 1; |
| | | pointer-events: none; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="忬¾ä¸å¼ç¥¨åæ" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="barLegend" |
| | | :series="barSeries1" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }" |
| | | style="height: 260px" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { getAmountHalfYear } from '@/api/viewIndex.js' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | import Echarts from '@/components/Echarts/echarts.vue' |
| | | |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '160%', |
| | | } |
| | | |
| | | const grid = { left: '3%', right: '4%', bottom: '3%', top: '10%', containLabel: true } |
| | | const barLegend = { |
| | | show: true, |
| | | textStyle: { color: '#B8C8E0' }, |
| | | data: ['å¼ç¥¨éé¢', '忬¾éé¢'], |
| | | } |
| | | const barSeries1 = ref([ |
| | | { |
| | | name: 'å¼ç¥¨éé¢', |
| | | type: 'bar', |
| | | barWidth: 20, |
| | | emphasis: { focus: 'series' }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: 'rgba(0, 164, 237, 0)' }, |
| | | { offset: 0, color: 'rgba(78, 228, 255, 1)' }, |
| | | ], |
| | | }, |
| | | }, |
| | | data: [], |
| | | }, |
| | | { |
| | | name: '忬¾éé¢', |
| | | type: 'bar', |
| | | barGap: '40%', |
| | | barWidth: 20, |
| | | emphasis: { focus: 'series' }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: 'rgba(83, 126, 245, 0.19)' }, |
| | | { offset: 0, color: 'rgba(144, 97, 248, 1)' }, |
| | | ], |
| | | }, |
| | | }, |
| | | data: [], |
| | | }, |
| | | ]) |
| | | |
| | | const tooltip = { |
| | | trigger: 'axis', |
| | | axisPointer: { type: 'shadow' }, |
| | | formatter(params) { |
| | | let result = params[0].axisValueLabel + '<br/>' |
| | | params.forEach((item) => { |
| | | result += `<div>${item.marker} ${item.seriesName}: ${item.value} å
</div>` |
| | | }) |
| | | return result |
| | | }, |
| | | } |
| | | |
| | | const xAxis1 = ref([{ type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] }]) |
| | | const yAxis1 = [{ type: 'value', axisLabel: { color: '#B8C8E0' } }] |
| | | |
| | | const fetchData = () => { |
| | | getAmountHalfYear() |
| | | .then((res) => { |
| | | if (res.code === 200 && Array.isArray(res.data)) { |
| | | const items = res.data |
| | | xAxis1.value[0].data = items.map((item) => item.month) |
| | | barSeries1.value[0].data = items.map( |
| | | (item) => parseFloat(item.invoiceAmount) || 0 |
| | | ) |
| | | barSeries1.value[1].data = items.map( |
| | | (item) => parseFloat(item.receiptAmount) || 0 |
| | | ) |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error('è·åè¿å年忬¾ä¸å¼ç¥¨æ°æ®å¤±è´¥:', err) |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fetchData() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .main-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .panel-item-customers { |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 449px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="scale-container"> |
| | | <div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }"> |
| | | <!-- å
¨å±æé® - ç§»å¨å°å·¦ä¸è§ --> |
| | | <button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? 'éåºå
¨å±' : 'å
¨å±æ¾ç¤º'"> |
| | | <svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| | | <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/> |
| | | </svg> |
| | | <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| | | <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/> |
| | | </svg> |
| | | </button> |
| | | |
| | | <!-- 顶鍿 颿 --> |
| | | <div class="dashboard-header"> |
| | | <div class="factory-name">è´¢å¡æ°æ®åæ</div> |
| | | </div> |
| | | |
| | | <!-- 主è¦å
容åºå --> |
| | | <div class="dashboard-content"> |
| | | <!-- 左侧åºå --> |
| | | <div class="left-panel"> |
| | | <LeftTop /> |
| | | |
| | | <LeftBottom /> |
| | | </div> |
| | | |
| | | <!-- ä¸é´åºå --> |
| | | <div class="center-panel"> |
| | | <CenterTop /> |
| | | <CenterCenter/> |
| | | <CenterBottom /> |
| | | </div> |
| | | |
| | | <!-- å³ä¾§åºå --> |
| | | <div class="right-panel"> |
| | | <RightBottom /> |
| | | <RightTop /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue' |
| | | import autofit from 'autofit.js' |
| | | import LeftBottom from './components/left-bottom.vue' |
| | | import CenterCenter from './components/center-center.vue' |
| | | import RightTop from '../dataDashboard/components/basic/right-top.vue' |
| | | import RightBottom from '../dataDashboard/components/basic/right-bottom.vue' |
| | | import useUserStore from '@/store/modules/user' |
| | | import LeftTop from './components/left-top.vue' |
| | | import CenterTop from './components/center-top.vue' |
| | | import CenterBottom from './components/center-bottom.vue' |
| | | |
| | | // å
¨å±ç¸å
³ç¶æ |
| | | const isFullscreen = ref(false); |
| | | |
| | | // ç¼©æ¾æ¯ä¾ |
| | | const scaleRatio = ref(1) |
| | | // 设计尺寸ï¼åºå尺寸ï¼- æ ¹æ®å®é
è®¾è®¡ç¨¿è°æ´ |
| | | const designWidth = 1920 |
| | | const designHeight = 1080 |
| | | |
| | | // ç¨æ·store |
| | | const userStore = useUserStore() |
| | | |
| | | // 计ç®ç¼©æ¾æ¯ä¾ |
| | | const calculateScale = () => { |
| | | const container = document.querySelector('.scale-container') |
| | | if (!container) return |
| | | |
| | | // è·å容å¨çå®é
尺寸 |
| | | const rect = container.getBoundingClientRect?.() |
| | | const containerWidth = container.clientWidth || rect?.width || window.innerWidth |
| | | const containerHeight = container.clientHeight || rect?.height || window.innerHeight |
| | | |
| | | // 计ç®å®½é«ç¼©æ¾æ¯ä¾ï¼åè¾å°å¼ä»¥ä¿è¯å
容宿´æ¾ç¤ºï¼çæ¯ç¼©æ¾ï¼ |
| | | const scaleX = containerWidth / designWidth |
| | | const scaleY = containerHeight / designHeight |
| | | scaleRatio.value = Math.min(scaleX, scaleY) |
| | | } |
| | | |
| | | // çªå£å¤§å°ååå¤ç |
| | | const handleResize = () => { |
| | | // å»¶è¿æ§è¡ï¼ç¡®ä¿DOMæ´æ°å®æ |
| | | setTimeout(() => { |
| | | calculateScale() |
| | | }, 100) |
| | | } |
| | | |
| | | // å
¨å±åè½å®ç° - é对scale-containerå
ç´ |
| | | const toggleFullscreen = () => { |
| | | const element = document.querySelector('.scale-container') |
| | | |
| | | if (!element) return |
| | | |
| | | if (!isFullscreen.value) { |
| | | if (element.requestFullscreen) { |
| | | element.requestFullscreen() |
| | | } else if (element.webkitRequestFullscreen) { |
| | | element.webkitRequestFullscreen() |
| | | } else if (element.msRequestFullscreen) { |
| | | element.msRequestFullscreen() |
| | | } |
| | | } else { |
| | | if (document.exitFullscreen) { |
| | | document.exitFullscreen() |
| | | } else if (document.webkitExitFullscreen) { |
| | | document.webkitExitFullscreen() |
| | | } else if (document.msExitFullscreen) { |
| | | document.msExitFullscreen() |
| | | } |
| | | } |
| | | } |
| | | |
| | | // çå¬å
¨å±ååäºä»¶ |
| | | const handleFullscreenChange = () => { |
| | | const fullscreenElement = document.fullscreenElement || |
| | | document.webkitFullscreenElement || |
| | | document.msFullscreenElement |
| | | isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container') |
| | | |
| | | // å
¨å±ç¶æååæ¶ï¼å»¶è¿éæ°è®¡ç®ç¼©æ¾æ¯ä¾ï¼ç¡®ä¿DOMæ´æ°å®æï¼ |
| | | setTimeout(() => { |
| | | calculateScale() |
| | | }, 200) |
| | | } |
| | | |
| | | // çå½å¨æé©å |
| | | onMounted(() => { |
| | | // 使ç¨nextTickç¡®ä¿DOMå®å
¨æ¸²æåååå§å |
| | | nextTick(() => { |
| | | // 计ç®åå§ç¼©æ¾æ¯ä¾ |
| | | calculateScale() |
| | | }) |
| | | |
| | | window.addEventListener('resize', handleResize) |
| | | window.addEventListener('fullscreenchange', handleFullscreenChange) |
| | | window.addEventListener('webkitfullscreenchange', handleFullscreenChange) |
| | | window.addEventListener('MSFullscreenChange', handleFullscreenChange) |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener('resize', handleResize) |
| | | window.removeEventListener('fullscreenchange', handleFullscreenChange) |
| | | window.removeEventListener('webkitfullscreenchange', handleFullscreenChange) |
| | | window.removeEventListener('MSFullscreenChange', handleFullscreenChange) |
| | | // ç§»é¤æä»¬æ·»å çautofitå¨æè°æ´çå¬å¨ |
| | | if (window._autofitUpdateHandler) { |
| | | window.removeEventListener('resize', window._autofitUpdateHandler) |
| | | delete window._autofitUpdateHandler |
| | | } |
| | | // å
³éautofit |
| | | autofit.off() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* å¤é¨ç¼©æ¾å®¹å¨ - å æ®æ´ä¸ªè§å£ */ |
| | | .scale-container { |
| | | position: relative; |
| | | width: 100%; |
| | | /* 页é¢å¨å¸¸è§å¸å±ä¸ï¼æé¡¶æ ï¼é»è®¤åå» 84pxï¼é¿å
å
容被è£å */ |
| | | height: calc(100vh - 84px); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background-color: #000; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* å
é¨å
容åºå - åºå®è®¾è®¡å°ºå¯¸ */ |
| | | .data-dashboard { |
| | | position: relative; |
| | | width: 1920px; |
| | | height: 1080px; |
| | | background-image: url("@/assets/BI/backImage@2x.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | transform-origin: center center; |
| | | } |
| | | |
| | | /* å
¨å±ç¶æçæ ·å¼ - ä½ç¨äºscale-container */ |
| | | .scale-container:fullscreen { |
| | | width: 100vw; |
| | | height: 100vh; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: #000; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | /* Webkitæµè§å¨åç¼ */ |
| | | .scale-container:-webkit-full-screen { |
| | | width: 100vw; |
| | | height: 100vh; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: #000; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | /* MSæµè§å¨åç¼ */ |
| | | .scale-container:-ms-fullscreen { |
| | | width: 100vw; |
| | | height: 100vh; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: #000; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | |
| | | .dashboard-header { |
| | | position: relative; |
| | | z-index: 1; |
| | | height: 86px; |
| | | background-image: url("@/assets/BI/biaoti.png"); |
| | | background-size: cover; |
| | | background-repeat: no-repeat; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .factory-name { |
| | | font-weight: 600; |
| | | font-size: 52px; |
| | | color: #FFFFFF; |
| | | top: 16px; |
| | | position: absolute; |
| | | } |
| | | |
| | | .fullscreen-btn { |
| | | position: absolute; |
| | | top: 10px; |
| | | left: 20px; |
| | | width: 40px; |
| | | height: 40px; |
| | | background: rgba(0, 20, 60, 0.8); |
| | | border: 1px solid rgba(0, 212, 255, 0.3); |
| | | border-radius: 6px; |
| | | color: #00d4ff; |
| | | cursor: pointer; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s; |
| | | z-index: 10000; |
| | | } |
| | | |
| | | .fullscreen-btn:hover { |
| | | background: rgba(0, 30, 90, 0.9); |
| | | border-color: rgba(0, 212, 255, 0.5); |
| | | } |
| | | |
| | | .dashboard-content { |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | gap: 30px; |
| | | padding: 0 30px; |
| | | height: calc(100% - 86px); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* ç¡®ä¿å颿¿è½å¤æ£ç¡®æ¾ç¤º */ |
| | | .left-panel, .center-panel, .right-panel { |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .left-panel, |
| | | .right-panel { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24px; |
| | | width: 520px; |
| | | } |
| | | |
| | | .center-panel { |
| | | flex: 1.5; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">äºæ
åç§°ï¼</span> |
| | | <el-input v-model="searchForm.accidentName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥äºæ
åç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title ml10">äºæ
ç±»åï¼</span> |
| | | <el-select v-model="searchForm.accidentType" |
| | | clearable |
| | | @change="handleQuery" |
| | | style="width: 240px"> |
| | | <el-option v-for="item in accidentTypeOptions" |
| | | :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"></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="140px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äºæ
ç¼å·" |
| | | prop="accidentCode"> |
| | | <el-input v-model="form.accidentCode" |
| | | placeholder="请è¾å
¥äºæ
ç¼å·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äºæ
åç§°" |
| | | prop="accidentName"> |
| | | <el-input v-model="form.accidentName" |
| | | placeholder="请è¾å
¥äºæ
åç§°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äºæ
åçæ¶é´ï¼" |
| | | prop="happenTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.happenTime" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äºæ
åçä½ç½®" |
| | | prop="happenLocation"> |
| | | <el-input v-model="form.happenLocation" |
| | | placeholder="请è¾å
¥äºæ
åçä½ç½®" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äºæ
ç级" |
| | | prop="accidentGrade"> |
| | | <el-select v-model="form.accidentGrade" |
| | | placeholder="è¯·éæ©äºæ
ç级" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in accidentGradeOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äºæ
ç±»å" |
| | | prop="accidentType"> |
| | | <el-select v-model="form.accidentType" |
| | | placeholder="è¯·éæ©äºæ
ç±»å" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in accidentTypeOptions" |
| | | :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="personLoss"> |
| | | <el-input v-model="form.personLoss" |
| | | placeholder="请è¾å
¥äººåæå¤±æ
åµ" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç´æ¥è´¢äº§æå¤±ï¼å
ï¼" |
| | | prop="assetLoss"> |
| | | <el-input v-model="form.assetLoss" |
| | | type="number" |
| | | placeholder="请è¾å
¥ç´æ¥è´¢äº§æå¤±" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="䏿¥äºº" |
| | | prop="createUser"> |
| | | <el-select v-model="form.createUser" |
| | | placeholder="è¯·éæ©" |
| | | disabled |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="䏿¥æ¶é´" |
| | | prop="createTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.createTime" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | type="datetime" |
| | | disabled |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="äºæ
ç´æ¥åå " |
| | | prop="accidentCause"> |
| | | <el-input v-model="form.accidentCause" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥äºæ
ç´æ¥åå " /> |
| | | </el-form-item> |
| | | <el-form-item label="äºæ
æ ¹æ¬åå " |
| | | prop="rootCause"> |
| | | <el-input v-model="form.rootCause" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥äºæ
æ ¹æ¬åå " /> |
| | | </el-form-item> |
| | | <el-form-item label="ç产影åæ
åµ" |
| | | prop="productionLoss"> |
| | | <el-input v-model="form.productionLoss" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥ç产影åæ
åµ" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç°åºåºæ¥å¤ç½®æªæ½" |
| | | prop="handleMeasures"> |
| | | <el-input v-model="form.handleMeasures" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="ç°åºåºæ¥å¤ç½®æªæ½" /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </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>{{ currentKnowledge.accidentCode }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="äºæ
åç§°"> |
| | | <span>{{ currentKnowledge.accidentName }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="äºæ
åçæ¶é´"> |
| | | {{ currentKnowledge.happenTime }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="äºæ
åçå°ç¹"> |
| | | {{ currentKnowledge.happenLocation }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="äºæ
ç级"> |
| | | <el-tag :type="accidentGradeType(currentKnowledge.accidentGrade)">{{ currentKnowledge.accidentGrade }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="äºæ
ç±»å"> |
| | | <el-tag type="info">{{ accidentTypeLabel(currentKnowledge.accidentType) }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="人å伤亡æ
åµ"> |
| | | {{ currentKnowledge.personLoss }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç´æ¥è´¢äº§æå¤±ï¼å
ï¼"> |
| | | {{ currentKnowledge.assetLoss }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="䏿¥äºº"> |
| | | {{ currentKnowledge.createUserName }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="䏿¥æ¶é´"> |
| | | {{ currentKnowledge.createTime }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <div class="detail-section"> |
| | | <h4>äºæ
ç´æ¥åå </h4> |
| | | <div class="detail-content">{{ currentKnowledge.accidentCause }}</div> |
| | | </div> |
| | | <div class="detail-section"> |
| | | <h4>äºæ
æ ¹æ¬åå </h4> |
| | | <div class="detail-content">{{ currentKnowledge.rootCause }}</div> |
| | | </div> |
| | | <div class="detail-section"> |
| | | <h4>ç产影åæ
åµ</h4> |
| | | <div class="detail-content">{{ currentKnowledge.productionLoss }}</div> |
| | | </div> |
| | | <div class="detail-section"> |
| | | <h4>ç°åºåºæ¥å¤ç½®æªæ½</h4> |
| | | <div class="detail-content">{{ currentKnowledge.handleMeasures }}</div> |
| | | </div> |
| | | <div class="detail-section"> |
| | | <h4>夿³¨</h4> |
| | | <div class="detail-content">{{ currentKnowledge.remark }}</div> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="viewDialogVisible = false">å
³é</el-button> |
| | | <!-- <el-button type="success" @click="markAsFavorite">æ¶è@</el-button> --> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | computed, |
| | | } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { |
| | | safeAccidentListPage, |
| | | safeAccidentAdd, |
| | | safeAccidentUpdate, |
| | | safeAccidentDel, |
| | | } from "@/api/safeProduction/accidentReportingRecord.js"; |
| | | import dayjs from "dayjs"; |
| | | const userStore = useUserStore(); |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | accidentCode: [ |
| | | { required: true, message: "请è¾å
¥äºæ
ç¼å·", trigger: "blur" }, |
| | | ], |
| | | accidentName: [ |
| | | { required: true, message: "请è¾å
¥äºæ
åç§°", trigger: "blur" }, |
| | | ], |
| | | happenTime: [ |
| | | { required: true, message: "è¯·éæ©äºæ
åçæ¶é´", trigger: "change" }, |
| | | ], |
| | | happenLocation: [ |
| | | { required: true, message: "请è¾å
¥äºæ
åçä½ç½®", trigger: "blur" }, |
| | | ], |
| | | accidentGrade: [ |
| | | { required: true, message: "è¯·éæ©äºæ
ç级", trigger: "change" }, |
| | | ], |
| | | accidentType: [ |
| | | { required: true, message: "è¯·éæ©äºæ
ç±»å", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | accidentName: "", |
| | | accidentType: "", |
| | | }, |
| | | tableLoading: false, |
| | | page: { |
| | | current: 1, |
| | | size: 20, |
| | | total: 0, |
| | | }, |
| | | tableData: [], |
| | | selectedIds: [], |
| | | form: { |
| | | accidentCode: "", // äºæ
ç¼ç |
| | | accidentName: "", // äºæ
åç§° |
| | | happenTime: "", // äºæ
åçæ¶é´ |
| | | happenLocation: "", // äºæ
åçå°ç¹ |
| | | accidentGrade: "", // äºæ
ç级 |
| | | accidentType: "", // äºæ
ç±»å |
| | | personLoss: "", // 人å伤亡 |
| | | assetLoss: "", // èµäº§æå¤± |
| | | createUser: "", // åå»ºç¨æ· |
| | | createTime: "", // å建æ¶é´ |
| | | createUserName: "", // åå»ºç¨æ·å |
| | | accidentCause: "", // äºæ
ç´æ¥åå |
| | | rootCause: "", // äºæ
æ ¹æ¬åå |
| | | productionLoss: "", // ç产æå¤± |
| | | handleMeasures: "", // åºæ¥å¤ç½®æªæ½ |
| | | remark: "", // 夿³¨ |
| | | }, |
| | | dialogVisible: false, |
| | | dialogTitle: "", |
| | | dialogType: "add", |
| | | viewDialogVisible: false, |
| | | currentKnowledge: {}, |
| | | }); |
| | | |
| | | const { |
| | | searchForm, |
| | | tableLoading, |
| | | page, |
| | | tableData, |
| | | selectedIds, |
| | | form, |
| | | dialogVisible, |
| | | dialogTitle, |
| | | dialogType, |
| | | viewDialogVisible, |
| | | currentKnowledge, |
| | | } = toRefs(data); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "äºæ
ç¼ç ", |
| | | prop: "accidentCode", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "äºæ
åç§°", |
| | | prop: "accidentName", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "äºæ
åçæ¶é´", |
| | | prop: "happenTime", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "äºæ
åçä½ç½®", |
| | | prop: "happenLocation", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "äºæ
ç级", |
| | | prop: "accidentGrade", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "äºæ
ç±»å", |
| | | prop: "accidentType", |
| | | showOverflowTooltip: true, |
| | | formatData: params => { |
| | | return accidentTypeLabel(params); |
| | | }, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 200, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | type: "text", |
| | | clickFun: row => { |
| | | viewKnowledge(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const userList = ref([]); |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | getCurrentFactoryName(); |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | getList(); |
| | | startAutoRefresh(); |
| | | }); |
| | | const handleChange = userId => { |
| | | const selectedUser = userList.value.find(user => user.userId === userId); |
| | | if (selectedUser) { |
| | | form.value.coreResponsorUserName = selectedUser.nickName; |
| | | } |
| | | }; |
| | | // å¼å§èªå¨å·æ° |
| | | const startAutoRefresh = () => { |
| | | setInterval(() => { |
| | | getList(); |
| | | }, 600000); // 10åéå·æ°ä¸æ¬¡ (10 * 60 * 1000 = 600000ms) |
| | | }; |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const handleQuery = () => { |
| | | 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: "è½»å¾®äºæ
", |
| | | }, |
| | | { |
| | | 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 }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å页å¤ç |
| | | const pagination = obj => { |
| | | page.value.current = obj.page; |
| | | page.value.size = obj.limit; |
| | | handleQuery(); |
| | | }; |
| | | const currentUserId = ref(""); |
| | | const currentUserName = ref(""); |
| | | const getCurrentFactoryName = async () => { |
| | | let res = await userStore.getInfo(); |
| | | currentUserId.value = res.user.userId; |
| | | currentUserName.value = res.user.nickName; |
| | | }; |
| | | // éæ©ååå¤ç |
| | | 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, { |
| | | accidentCode: "", // äºæ
ç¼ç |
| | | accidentName: "", // äºæ
åç§° |
| | | happenTime: "", // äºæ
åçæ¶é´ |
| | | happenLocation: "", // äºæ
åçå°ç¹ |
| | | accidentGrade: "", // äºæ
ç级 |
| | | accidentType: "", // äºæ
ç±»å |
| | | personLoss: "", // 人å伤亡 |
| | | assetLoss: "", // èµäº§æå¤± |
| | | createUser: currentUserId.value, // åå»ºç¨æ· |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // å建æ¶é´ |
| | | createUserName: currentUserName.value, // åå»ºç¨æ·å |
| | | accidentCause: "", // äºæ
ç´æ¥åå |
| | | rootCause: "", // äºæ
æ ¹æ¬åå |
| | | productionLoss: "", // ç产æå¤± |
| | | handleMeasures: "", // åºæ¥å¤ç½®æªæ½ |
| | | remark: "", // 夿³¨ |
| | | }); |
| | | } else if (type === "edit" && row) { |
| | | dialogTitle.value = "ç¼è¾äºæ
"; |
| | | Object.assign(form.value, { |
| | | id: row.id, |
| | | accidentCode: row.accidentCode, // äºæ
ç¼ç |
| | | accidentName: row.accidentName, // äºæ
åç§° |
| | | happenTime: row.happenTime, // äºæ
åçæ¶é´ |
| | | happenLocation: row.happenLocation, // äºæ
åçå°ç¹ |
| | | accidentGrade: row.accidentGrade, // äºæ
ç级 |
| | | accidentType: row.accidentType, // äºæ
ç±»å |
| | | personLoss: row.personLoss, // 人å伤亡 |
| | | assetLoss: row.assetLoss, // èµäº§æå¤± |
| | | createUser: row.createUser, // åå»ºç¨æ· |
| | | createTime: row.createTime, // å建æ¶é´ |
| | | createUserName: row.createUserName, // åå»ºç¨æ·å |
| | | accidentCause: row.accidentCause, // äºæ
ç´æ¥åå |
| | | rootCause: row.rootCause, // äºæ
æ ¹æ¬åå |
| | | productionLoss: row.productionLoss, // ç产æå¤± |
| | | handleMeasures: row.handleMeasures, // åºæ¥å¤ç½®æªæ½ |
| | | remark: row.remark, // 夿³¨ |
| | | }); |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // æ¥çäºæ
详æ
|
| | | const viewKnowledge = row => { |
| | | currentKnowledge.value = { ...row }; |
| | | viewDialogVisible.value = true; |
| | | }; |
| | | const getApplyScopeLabel = scope => { |
| | | const scopeMap = { |
| | | all: "å
¨ä½åå·¥", |
| | | manager: "管çå±", |
| | | hr: "人äºé¨é¨", |
| | | finance: "è´¢å¡é¨é¨", |
| | | tech: "ææ¯é¨é¨", |
| | | }; |
| | | return scopeMap[scope] || scope; |
| | | }; |
| | | |
| | | // è·åç±»åæ ç¾ç±»å |
| | | const getTypeTagType = type => { |
| | | const typeMap = { |
| | | contract: "success", |
| | | approval: "warning", |
| | | solution: "primary", |
| | | experience: "info", |
| | | guide: "danger", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | // è·åç±»åæ ç¾ææ¬ |
| | | const getTypeLabel = type => { |
| | | return getKnowledgeTypeLabel(type); |
| | | }; |
| | | |
| | | // è·åæçæ ç¾ç±»å |
| | | 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] || "æªç¥"; |
| | | }; |
| | | // æäº¤åºæ¥é¢æ¡è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | | await formRef.value.validate(); |
| | | if (dialogType.value === "add") { |
| | | safeAccidentAdd({ ...form.value }) |
| | | .then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("æ·»å æå"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else { |
| | | safeAccidentUpdate({ ...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); |
| | | safeAccidentDel(selectedIds.value).then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("å 餿å"); |
| | | selectedIds.value = []; |
| | | getList(); |
| | | } |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | // ç¨æ·åæ¶ |
| | | }); |
| | | }; |
| | | |
| | | // åå
¸å·¥å
· |
| | | const knowledgeTypeOptions = computed(() => knowledge_type?.value || []); |
| | | const getKnowledgeTypeLabel = val => { |
| | | const item = knowledgeTypeOptions.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | 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; |
| | | } |
| | | |
| | | .exec-steps-container { |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 4px; |
| | | padding: 15px; |
| | | background-color: #f9fafc; |
| | | } |
| | | |
| | | .exec-step-item { |
| | | margin-bottom: 10px; |
| | | padding: 10px; |
| | | background-color: #ffffff; |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .step-header { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .exec-step-view { |
| | | margin-bottom: 8px; |
| | | padding-left: 20px; |
| | | position: relative; |
| | | } |
| | | |
| | | .step-number { |
| | | position: absolute; |
| | | left: 0; |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .step-title { |
| | | font-weight: bold; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .no-data { |
| | | color: #909399; |
| | | font-style: italic; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- <div class="search_form"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form-item label="鿣ç¼å·ï¼"> |
| | | <el-input v-model="searchForm.hiddenCode" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> --> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="openForm('add')"> |
| | | æ°å¢éæ£ |
| | | </el-button> |
| | | <!-- <el-button type="primary" |
| | | plain |
| | | @click="handleImport">导å
¥</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> --> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | <!-- <el-button type="primary" |
| | | plain |
| | | @click="handlePrint">æå°</el-button> --> |
| | | </div> |
| | | </div> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | :row-class-name="getRowClass" |
| | | style="width: 100%" |
| | | height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" |
| | | fixed="left" /> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="鿣ç¼å·" |
| | | prop="hiddenCode" |
| | | width="180" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="鿣æè¿°" |
| | | prop="hiddenDesc" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="鿣å
·ä½ä½ç½®" |
| | | prop="location" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="䏿¥äºº" |
| | | prop="createUserName" |
| | | width="180" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="䏿¥æ¶é´" |
| | | prop="createTime" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ´æ¹å®ææé" |
| | | prop="rectifyTime" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ´æ¹è´£ä»»äºº" |
| | | prop="rectifyUserName" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ´æ¹è´£ä»»äººèç³»æ¹å¼" |
| | | prop="rectifyUserMobile" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ´æ¹å
·ä½æªæ½" |
| | | prop="rectifyMeasures" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="å®é
æ´æ¹å®ææ¶é´" |
| | | prop="rectifyActualTime" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éªæ¶æè§" |
| | | prop="verifyRemark" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éªæ¶æ¶é´" |
| | | prop="verifyTime" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éªæ¶ç»æ" |
| | | prop="verifyResult" |
| | | width="120" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.verifyResult" |
| | | :type="scope.row.verifyResult === 'éè¿' ? 'success' : 'danger'"> |
| | | {{ scope.row.verifyResult }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | min-width="250" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="openForm('edit', scope.row)">ç¼è¾</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="downLoadFile(scope.row)">éä»¶</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="openForm('edit2', scope.row)">æ´æ¹</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="!scope.row.rectifyActualTime" |
| | | @click="openForm('edit3', scope.row)">éªæ¶</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | </div> |
| | | <FormDialog v-model="dialogFormVisible" |
| | | :title="getTitle(operationType)" |
| | | :width="'70%'" |
| | | :operation-type="operationType" |
| | | @close="closeDia" |
| | | @confirm="submitForm" |
| | | @cancel="closeDia"> |
| | | <el-form :model="form" |
| | | v-if="operationType === 'add' || operationType === 'edit'" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="鿣ç¼å·ï¼" |
| | | prop="hiddenCode"> |
| | | <el-input v-model="form.hiddenCode" |
| | | placeholder="èªå¨çæ" |
| | | disabled |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="鿣å
·ä½ä½ç½®ï¼" |
| | | prop="location"> |
| | | <el-input v-model="form.location" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="鿣æè¿°ï¼" |
| | | prop="hiddenDesc"> |
| | | <el-input v-model="form.hiddenDesc" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éæ£ç±»åï¼" |
| | | prop="type"> |
| | | <el-select v-model="form.type" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in typeList" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="鿣é£é©ç级ï¼" |
| | | prop="riskLevel"> |
| | | <el-select v-model="form.riskLevel" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in riskLevelList" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="䏿¥äººï¼" |
| | | prop="createUser"> |
| | | <el-select v-model="form.createUser" |
| | | placeholder="è¯·éæ©" |
| | | disabled |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="䏿¥æ¶é´ï¼" |
| | | prop="createTime"> |
| | | <el-date-picker style="width: 100%" |
| | | disabled |
| | | v-model="form.createTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ´æ¹è´£ä»»äººï¼" |
| | | prop="rectifyUserId"> |
| | | <el-select v-model="form.rectifyUserId" |
| | | placeholder="è¯·éæ©" |
| | | @change="handleChange2" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ´æ¹å®ææé" |
| | | prop="rectifyTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.rectifyTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <el-descriptions :column="2" |
| | | style="margin-bottom: 20px;" |
| | | v-if="operationType === 'edit2' || operationType === 'edit3'" |
| | | title="éæ£è¯¦æ
" |
| | | border> |
| | | <el-descriptions-item label="鿣ç¼å·"> |
| | | <span class="detail-title">{{ form.hiddenCode }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="鿣å
·ä½ä½ç½®"> |
| | | <span class="detail-title">{{ form.location }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item :span="2" |
| | | label="鿣æè¿°"> |
| | | <span class="detail-title">{{ form.hiddenDesc }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="éæ£ç±»å"> |
| | | <span class="detail-title">{{ TypeLabel(form.type) }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="鿣é£é©ç级"> |
| | | <el-tag :type="getTypeTagType(form.riskLevel)"> |
| | | {{ form.riskLevel }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="䏿¥äºº"> |
| | | <span class="detail-title">{{ form.createUserName }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="䏿¥æ¶é´"> |
| | | <span class="detail-title">{{ form.createTime }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ´æ¹è´£ä»»äºº"> |
| | | <span class="detail-title">{{ form.rectifyUserName }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ´æ¹è´£ä»»äººèç³»æ¹å¼"> |
| | | <span class="detail-title">{{ form.rectifyUserMobile }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ´æ¹å®ææé"> |
| | | <span class="detail-title">{{ form.rectifyTime }}</span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-descriptions :column="2" |
| | | style="margin-bottom: 20px;" |
| | | v-if="operationType === 'edit3'" |
| | | title="æ´æ¹è¯¦æ
" |
| | | border> |
| | | <el-descriptions-item label="æ´æ¹å
·ä½æªæ½" |
| | | :span="2"> |
| | | <span class="detail-title">{{ form2.rectifyMeasures }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å®é
æ´æ¹å®ææ¶é´"> |
| | | <span class="detail-title">{{ form2.rectifyActualTime }}</span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-form :model="form2" |
| | | v-if="operationType === 'edit2'" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules2" |
| | | ref="formRef2"> |
| | | <el-form-item label="æ´æ¹å
·ä½æªæ½ï¼" |
| | | prop="rectifyMeasures"> |
| | | <el-input v-model="form2.rectifyMeasures" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥æ´æ¹å
·ä½æªæ½" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å®é
æ´æ¹å®ææ¶é´ï¼" |
| | | prop="rectifyActualTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form2.rectifyActualTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <el-form :model="form3" |
| | | v-if="operationType === 'edit3'" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules3" |
| | | ref="formRef3"> |
| | | <el-form-item label="éªæ¶æè§ï¼" |
| | | prop="verifyRemark"> |
| | | <el-input v-model="form3.verifyRemark" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥éªæ¶æè§" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éªæ¶æ¶é´ï¼" |
| | | prop="verifyTime"> |
| | | <el-date-picker style="width: 100%" |
| | | disabled |
| | | v-model="form3.verifyTime" |
| | | 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="verifyResult"> |
| | | <el-select v-model="form3.verifyResult" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option label="éè¿" |
| | | value="éè¿" /> |
| | | <el-option label="ä¸éè¿" |
| | | value="ä¸éè¿" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éªæ¶äººï¼" |
| | | prop="verifyUserId"> |
| | | <el-select v-model="form3.verifyUserId" |
| | | disabled |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </FormDialog> |
| | | <!-- éä»¶åè¡¨å¼¹çª --> |
| | | <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 { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.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"; |
| | | import FileListDialog from "@/components/Dialog/FileListDialog.vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { |
| | | dangerInvestigationListPage, |
| | | safeHiddenAdd, |
| | | safeHiddenUpdate, |
| | | safeHiddenDel, |
| | | fileListPage, |
| | | safeHiddenFileAdd, |
| | | safeHiddenFileDel, |
| | | } from "@/api/safeProduction/dangerInvestigation.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import request from "@/utils/request"; |
| | | import dayjs from "dayjs"; |
| | | import { get } from "@vueuse/core"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const userList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const getTitle = type => { |
| | | if (type === "add") { |
| | | return "æ°å¢éæ£"; |
| | | } else if (type === "edit") { |
| | | return "ä¿®æ¹éæ£"; |
| | | } else if (type === "edit2") { |
| | | return "æ´æ¹é¡µé¢"; |
| | | } else if (type === "edit3") { |
| | | return "éªæ¶é¡µé¢"; |
| | | } |
| | | }; |
| | | // è·åç±»åæ ç¾ç±»å |
| | | const getTypeTagType = type => { |
| | | const typeMap = { |
| | | è¾å¤§é£é©: "warning", |
| | | ä½é£é©: "info", |
| | | ä¸è¬é£é©: "info", |
| | | é大é£é©: "danger", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | hiddenCode: "", // 鿣ç¼å· |
| | | }, |
| | | form: { |
| | | hiddenCode: "", // 鿣ç¼å· |
| | | location: "", // 鿣ä½ç½® |
| | | hiddenDesc: "", // 鿣æè¿° |
| | | createUser: "", // 䏿¥äºº |
| | | createUserName: "", |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 䏿¥æ¶é´ |
| | | rectifyUserId: "", // æ´æ¹è´£ä»»äºº |
| | | rectifyUserName: "", |
| | | rectifyTime: "", // æ´æ¹å®ææé |
| | | rectifyUserMobile: "", // æ´æ¹è´£ä»»äººææºå· |
| | | riskLevel: "", // 鿣é£é©ç级 |
| | | type: "", // éæ£ç±»å |
| | | }, |
| | | |
| | | rules: { |
| | | location: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | hiddenDesc: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | riskLevel: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | type: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | createUser: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | rectifyUserId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | rectifyTime: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const rules2 = { |
| | | rectifyActualTime: [{ required: true, message: "è¯·éæ©", trigger: "blur" }], |
| | | rectifyMeasures: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | }; |
| | | const rules3 = { |
| | | verifyTime: [{ required: true, message: "è¯·éæ©", trigger: "blur" }], |
| | | verifyRemark: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | verifyResult: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | acceptDesc: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | }; |
| | | 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: "", // æ´æ¹å
·ä½æªæ½ |
| | | }); |
| | | const form3 = ref({ |
| | | verifyTime: "", // éªæ¶æ¶é´ |
| | | verifyRemark: "", // éªæ¶å¤æ³¨ |
| | | acceptDesc: "", // éªæ¶æè¿° |
| | | verifyUserId: "", // éªæ¶äºº |
| | | verifyUserName: "", |
| | | verifyResult: "", // éªæ¶ç»æ |
| | | }); |
| | | const riskLevelList = ref([ |
| | | { |
| | | value: "é大é£é©", |
| | | label: "é大é£é©", |
| | | }, |
| | | { |
| | | value: "è¾å¤§é£é©", |
| | | label: "è¾å¤§é£é©", |
| | | }, |
| | | { |
| | | value: "ä¸è¬é£é©", |
| | | label: "ä¸è¬é£é©", |
| | | }, |
| | | { |
| | | value: "ä½é£é©", |
| | | label: "ä½é£é©", |
| | | }, |
| | | ]); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData(data.searchForm); |
| | | // 产å表åå¼¹æ¡æ°æ® |
| | | const productFormVisible = ref(false); |
| | | |
| | | const quotationLoading = ref(false); |
| | | const quotationList = ref([]); |
| | | const quotationSearchForm = reactive({ |
| | | quotationNo: "", |
| | | customer: "", |
| | | }); |
| | | |
| | | const handleChange2 = userId => { |
| | | const selectedUser = userList.value.find(user => user.userId === userId); |
| | | if (selectedUser) { |
| | | form.value.rectifyUserName = selectedUser.nickName; |
| | | form.value.rectifyUserMobile = selectedUser.phonenumber; |
| | | } |
| | | }; |
| | | |
| | | // 导å
¥ç¸å
³ |
| | | const importUploadRef = ref(null); |
| | | const importUpload = reactive({ |
| | | title: "导å
¥éå®å°è´¦", |
| | | open: false, |
| | | url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import", |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | isUploading: false, |
| | | beforeUpload: file => { |
| | | const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls"); |
| | | const isLt10M = file.size / 1024 / 1024 < 10; |
| | | if (!isExcel) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶åªè½æ¯ xlsx/xls æ ¼å¼!"); |
| | | return false; |
| | | } |
| | | if (!isLt10M) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿ 10MB!"); |
| | | return false; |
| | | } |
| | | return true; |
| | | }, |
| | | onChange: (file, fileList) => { |
| | | console.log("æä»¶ç¶ææ¹å", file, fileList); |
| | | }, |
| | | onProgress: (event, file, fileList) => { |
| | | console.log("ä¸ä¼ ä¸...", event.percent); |
| | | }, |
| | | onSuccess: (response, file, fileList) => { |
| | | console.log("ä¸ä¼ æå", response, file, fileList); |
| | | importUpload.isUploading = false; |
| | | if (response.code === 200) { |
| | | proxy.$modal.msgSuccess("导å
¥æå"); |
| | | importUpload.open = false; |
| | | if (importUploadRef.value) { |
| | | importUploadRef.value.clearFiles(); |
| | | } |
| | | getList(); |
| | | } else { |
| | | proxy.$modal.msgError(response.msg || "导å
¥å¤±è´¥"); |
| | | } |
| | | }, |
| | | onError: (error, file, fileList) => { |
| | | console.error("ä¸ä¼ 失败", error, file, fileList); |
| | | importUpload.isUploading = false; |
| | | proxy.$modal.msgError("导å
¥å¤±è´¥ï¼è¯·éè¯"); |
| | | }, |
| | | }); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | // åªæå¨ç¹å»æç´¢æé®æ¶æé置页ç å°ç¬¬ä¸é¡µ |
| | | // é¿å
表ååæ®µchangeäºä»¶å¹²æ°å页 |
| | | if (arguments.length === 0) { |
| | | page.current = 1; |
| | | } |
| | | expandedRowKeys.value = []; |
| | | getList(); |
| | | }; |
| | | const paginationChange = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const { entryDate, ...rest } = searchForm; |
| | | // å°èå´æ¥æåæ®µä¼ éç»å端 |
| | | const params = { ...rest, ...page }; |
| | | // ç§»é¤å½å
¥æ¥æçé»è®¤å¼è®¾ç½®ï¼åªä¿çèå´æ¥æå段 |
| | | delete params.entryDate; |
| | | return dangerInvestigationListPage(params) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | return res; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | console.log("selection", selectedRows.value); |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = async (type, row) => { |
| | | console.log("row", row); |
| | | operationType.value = type; |
| | | form3.value = { |
| | | verifyTime: "", // éªæ¶æ¶é´ |
| | | verifyRemark: "", // éªæ¶å¤æ³¨ |
| | | verifyResult: "", // éªæ¶æè¿° |
| | | verifyUserId: "", // éªæ¶äºº |
| | | }; |
| | | form2.value = { |
| | | rectifyActualTime: "", // å®é
æ´æ¹å®ææ¶é´ |
| | | rectifyMeasures: "", // æ´æ¹å
·ä½æªæ½ |
| | | }; |
| | | if (type === "add") { |
| | | form.value = { |
| | | hiddenCode: "", // 鿣ç¼å· |
| | | location: "", // 鿣ä½ç½® |
| | | hiddenDesc: "", // 鿣æè¿° |
| | | createUser: Number(currentUserId.value), // 䏿¥äºº |
| | | createUserName: currentUserName.value, |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 䏿¥æ¶é´ |
| | | rectifyUserId: "", // æ´æ¹è´£ä»»äºº |
| | | rectifyUserName: "", |
| | | rectifyTime: "", // æ´æ¹å®ææé |
| | | rectifyUserMobile: "", // æ´æ¹è´£ä»»äººææºå· |
| | | riskLevel: "", // 鿣é£é©ç级 |
| | | type: "", // éæ£ç±»å |
| | | }; |
| | | } else if (type === "edit") { |
| | | form.value = { |
| | | id: row.id, |
| | | hiddenCode: row.hiddenCode, // 鿣ç¼å· |
| | | location: row.location, // 鿣ä½ç½® |
| | | hiddenDesc: row.hiddenDesc, // 鿣æè¿° |
| | | createUser: row.createUser, // 䏿¥äºº |
| | | createUserName: row.createUserName, |
| | | createTime: row.createTime, // 䏿¥æ¶é´ |
| | | rectifyUserId: row.rectifyUserId, // æ´æ¹è´£ä»»äºº |
| | | rectifyUserName: row.rectifyUserName, |
| | | rectifyTime: row.rectifyTime, // æ´æ¹å®ææé |
| | | rectifyUserMobile: row.rectifyUserMobile, // æ´æ¹è´£ä»»äººææºå· |
| | | riskLevel: row.riskLevel, // 鿣é£é©ç级 |
| | | type: row.type, // éæ£ç±»å |
| | | }; |
| | | } else if (type === "edit2") { |
| | | form.value = { |
| | | id: row.id, |
| | | hiddenCode: row.hiddenCode, // 鿣ç¼å· |
| | | location: row.location, // 鿣ä½ç½® |
| | | hiddenDesc: row.hiddenDesc, // 鿣æè¿° |
| | | createUser: row.createUser, // 䏿¥äºº |
| | | createUserName: row.createUserName, |
| | | createTime: row.createTime, // 䏿¥æ¶é´ |
| | | rectifyUserId: row.rectifyUserId, // æ´æ¹è´£ä»»äºº |
| | | rectifyUserName: row.rectifyUserName, |
| | | rectifyTime: row.rectifyTime, // æ´æ¹å®ææé |
| | | rectifyUserMobile: row.rectifyUserMobile, // æ´æ¹è´£ä»»äººææºå· |
| | | riskLevel: row.riskLevel, // 鿣é£é©ç级 |
| | | type: row.type, // éæ£ç±»å |
| | | }; |
| | | form2.value = { |
| | | rectifyActualTime: row.rectifyActualTime, // å®é
æ´æ¹å®ææ¶é´ |
| | | rectifyMeasures: row.rectifyMeasures, // æ´æ¹å
·ä½æªæ½ |
| | | }; |
| | | } else if (type === "edit3") { |
| | | form.value = { |
| | | id: row.id, |
| | | hiddenCode: row.hiddenCode, // 鿣ç¼å· |
| | | location: row.location, // 鿣ä½ç½® |
| | | hiddenDesc: row.hiddenDesc, // 鿣æè¿° |
| | | createUser: row.createUser, // 䏿¥äºº |
| | | createUserName: row.createUserName, |
| | | createTime: row.createTime, // 䏿¥æ¶é´ |
| | | rectifyUserId: row.rectifyUserId, // æ´æ¹è´£ä»»äºº |
| | | rectifyUserName: row.rectifyUserName, |
| | | rectifyTime: row.rectifyTime, // æ´æ¹å®ææé |
| | | rectifyUserMobile: row.rectifyUserMobile, // æ´æ¹è´£ä»»äººææºå· |
| | | riskLevel: row.riskLevel, // 鿣é£é©ç级 |
| | | type: row.type, // éæ£ç±»å |
| | | }; |
| | | form2.value = { |
| | | rectifyActualTime: row.rectifyActualTime, // å®é
æ´æ¹å®ææ¶é´ |
| | | rectifyMeasures: row.rectifyMeasures, // æ´æ¹å
·ä½æªæ½ |
| | | }; |
| | | form3.value = { |
| | | verifyTime: row.verifyTime, // éªæ¶æ¶é´ |
| | | verifyRemark: row.verifyRemark, // éªæ¶å¤æ³¨ |
| | | verifyResult: row.verifyResult, // éªæ¶æè¿° |
| | | verifyUserId: row.verifyUserId, // éªæ¶äºº |
| | | }; |
| | | console.log("form3.value", form3.value); |
| | | if (!form3.value.verifyUserId || form3.value.verifyUserId === "null") { |
| | | form3.value.verifyUserId = Number(currentUserId.value); // éªæ¶äºº |
| | | } |
| | | if (!form3.value.verifyTime || form3.value.verifyTime === "null") { |
| | | form3.value.verifyTime = dayjs().format("YYYY-MM-DD"); // éªæ¶æè¿° |
| | | } |
| | | } |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | const getCurrentUserInfo = () => { |
| | | getInfo; |
| | | }; |
| | | const fetchQuotationList = async () => { |
| | | quotationLoading.value = true; |
| | | try { |
| | | const params = { |
| | | // å
¼å®¹å端å页忮µï¼è¿éæ²¿ç¨æ¥ä»·é¡µé¢å·²æå¯ç¨çåæ®µå½å |
| | | currentPage: 1, |
| | | pageSize: 100, |
| | | ...quotationSearchForm, |
| | | status: "éè¿", |
| | | }; |
| | | const res = await getQuotationList(params); |
| | | quotationList.value = res?.data?.records || []; |
| | | } finally { |
| | | quotationLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | console.log("operationType.value", operationType.value); |
| | | |
| | | if (operationType.value == "add") { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | safeHiddenAdd(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | } else if (operationType.value == "edit") { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | safeHiddenUpdate(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | } else if (operationType.value == "edit2") { |
| | | console.log("form2.value", form2.value); |
| | | proxy.$refs["formRef2"].validate(valid => { |
| | | if (valid) { |
| | | safeHiddenUpdate({ ...form2.value, ...form.value }).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | } else if (operationType.value == "edit3") { |
| | | proxy.$refs["formRef3"].validate(valid => { |
| | | if (valid) { |
| | | safeHiddenUpdate({ |
| | | ...form3.value, |
| | | ...form2.value, |
| | | ...form.value, |
| | | }).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | proxy.resetForm("formRef2"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProductDia = () => { |
| | | proxy.resetForm("productFormRef"); |
| | | productFormVisible.value = false; |
| | | }; |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | safeHiddenDel(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 夿æ¯å¦å¯ä»¥åè´§ |
| | | * åªæå¨äº§åç¶ææ¯å
è¶³ï¼åè´§ç¶ææ¯å¾
åè´§åå®¡æ ¸æç»çæ¶åæå¯ä»¥åè´§ |
| | | * @param row è¡æ°æ® |
| | | */ |
| | | const canShip = row => { |
| | | // 产åç¶æå¿
é¡»æ¯å
è¶³ï¼approveStatus === 1ï¼ |
| | | if (row.approveStatus !== 1) { |
| | | return false; |
| | | } |
| | | |
| | | // è·ååè´§ç¶æ |
| | | const shippingStatus = row.shippingStatus; |
| | | |
| | | // 妿已åè´§ï¼æåè´§æ¥ææè½¦çå·ï¼ï¼ä¸è½å次åè´§ |
| | | if (row.shippingDate || row.shippingCarNumber) { |
| | | return false; |
| | | } |
| | | |
| | | // åè´§ç¶æå¿
é¡»æ¯"å¾
åè´§"æ"å®¡æ ¸æç»" |
| | | const statusStr = shippingStatus ? String(shippingStatus).trim() : ""; |
| | | return statusStr === "å¾
åè´§" || statusStr === "å®¡æ ¸æç»"; |
| | | }; |
| | | |
| | | /** |
| | | * ä¸è½½æä»¶ |
| | | * |
| | | * @param row ä¸è½½æä»¶çç¸å
³ä¿¡æ¯å¯¹è±¡ |
| | | */ |
| | | const fileListRef = ref(null); |
| | | const fileListDialogVisible = ref(false); |
| | | const currentFileRow = ref(null); |
| | | const downLoadFile = row => { |
| | | currentFileRow.value = row; |
| | | fileListPage({ safeHiddenId: row.id }).then(res => { |
| | | if (fileListRef.value) { |
| | | fileListRef.value.open(res.data.records); |
| | | } |
| | | }); |
| | | }; |
| | | const currentUserId = ref(""); |
| | | const currentUserName = ref(""); |
| | | const getCurrentFactoryName = async () => { |
| | | let res = await userStore.getInfo(); |
| | | currentUserId.value = res.user.userId; |
| | | currentUserName.value = res.user.nickName; |
| | | }; |
| | | |
| | | /** |
| | | * è·åè¡ç±»åï¼ç¨äºå¤ææ¯å¦è¿ææªæ´æ¹ |
| | | * @param row è¡æ°æ® |
| | | * @returns ç±»å |
| | | */ |
| | | const getRowClass = ({ row }) => { |
| | | const now = new Date(); |
| | | |
| | | // æ£æ¥æ¯å¦è¶
è¿æ´æ¹æé䏿ªå®é
æ´æ¹ |
| | | if (row.rectifyTime && !row.rectifyActualTime) { |
| | | const rectifyTime = new Date(row.rectifyTime); |
| | | if (now > rectifyTime) { |
| | | return "overdue-row"; |
| | | } |
| | | } |
| | | |
| | | return ""; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | getCurrentFactoryName(); |
| | | }); |
| | | // ä¸ä¼ éä»¶ |
| | | 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 = { |
| | | safeHiddenId: currentFileRow.value.id, |
| | | name: uploadRes.data.originalName || file.name, |
| | | url: uploadRes.data.tempPath || uploadRes.data.url, |
| | | }; |
| | | |
| | | const saveRes = await safeHiddenFileAdd(fileData); |
| | | if (saveRes.code === 200) { |
| | | proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); |
| | | // éæ°å è½½æä»¶å表 |
| | | const listRes = await fileListPage({ |
| | | safeHiddenId: 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 safeHiddenFileDel([row.id]); |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | // éæ°å è½½æä»¶å表 |
| | | if (currentFileRow.value && fileListRef.value) { |
| | | const listRes = await fileListPage({ |
| | | safeHiddenId: 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; |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .ml-10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | |
| | | :deep(.warning-row) { |
| | | background-color: #fef0f0 !important; |
| | | } |
| | | |
| | | :deep(.warning-row td) { |
| | | // color: #cf1322 !important; |
| | | } |
| | | |
| | | :deep(.overdue-row) { |
| | | background-color: #ffffff !important; |
| | | } |
| | | |
| | | :deep(.overdue-row td) { |
| | | color: #e1707a !important; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | .print-preview-dialog { |
| | | .el-dialog__body { |
| | | padding: 0; |
| | | max-height: 80vh; |
| | | overflow-y: auto; |
| | | } |
| | | } |
| | | |
| | | .print-preview-container { |
| | | .print-preview-header { |
| | | padding: 15px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | text-align: center; |
| | | |
| | | .el-button { |
| | | margin: 0 10px; |
| | | } |
| | | } |
| | | |
| | | .print-preview-content { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | | min-height: 400px; |
| | | } |
| | | } |
| | | |
| | | .print-page { |
| | | width: 220mm; |
| | | height: 90mm; |
| | | padding: 10mm; |
| | | margin: 0 auto; |
| | | background: white; |
| | | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| | | margin-bottom: 10px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .delivery-note { |
| | | width: 100%; |
| | | height: 100%; |
| | | font-family: "SimSun", serif; |
| | | font-size: 10px; |
| | | line-height: 1.2; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .header { |
| | | text-align: center; |
| | | margin-bottom: 8px; |
| | | |
| | | .company-name { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .document-title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .info-section { |
| | | margin-bottom: 8px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .info-row { |
| | | line-height: 20px; |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 60px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | margin-right: 20px; |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table-section { |
| | | margin-bottom: 4px; |
| | | flex: 1; |
| | | |
| | | .product-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | border: 1px solid #000; |
| | | |
| | | th, |
| | | td { |
| | | border: 1px solid #000; |
| | | padding: 6px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | th { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-label { |
| | | text-align: right; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-value { |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .footer-section { |
| | | .footer-row { |
| | | display: flex; |
| | | margin-bottom: 3px; |
| | | line-height: 20px; |
| | | justify-content: space-between; |
| | | |
| | | .footer-item { |
| | | display: flex; |
| | | margin-right: 20px; |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | &.address-item { |
| | | .address-value { |
| | | min-width: 200px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media print { |
| | | .app-container { |
| | | display: none; |
| | | } |
| | | |
| | | .print-page { |
| | | box-shadow: none; |
| | | margin: 0; |
| | | padding: 10mm; |
| | | padding-left: 20mm; |
| | | page-break-inside: avoid; |
| | | page-break-after: always; |
| | | } |
| | | .print-page:last-child { |
| | | page-break-after: avoid; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">åºæ¥é¢æ¡åç§°ï¼</span> |
| | | <el-input v-model="searchForm.planName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥åºæ¥é¢æ¡åç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title ml10">åºæ¥é¢æ¡ç±»åï¼</span> |
| | | <el-select v-model="searchForm.planType" |
| | | clearable |
| | | @change="handleQuery" |
| | | style="width: 240px"> |
| | | <el-option v-for="item in emergencyPlanTypeOptions" |
| | | :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"></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="planCode"> |
| | | <el-input v-model="form.planCode" |
| | | placeholder="请è¾å
¥åºæ¥é¢æ¡ç¼ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºæ¥é¢æ¡åç§°" |
| | | prop="planName"> |
| | | <el-input v-model="form.planName" |
| | | placeholder="请è¾å
¥åºæ¥é¢æ¡åç§°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åå¸çææ¶é´ï¼" |
| | | prop="publishTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.publishTime" |
| | | 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="coreResponsorUserId"> |
| | | <el-select v-model="form.coreResponsorUserId" |
| | | placeholder="è¯·éæ©" |
| | | @change="handleChange" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="颿¡ç±»å" |
| | | prop="planType"> |
| | | <el-select v-model="form.planType" |
| | | placeholder="è¯·éæ©é¢æ¡ç±»å" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in emergencyPlanTypeOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="éç¨èå´" |
| | | prop="applyScope"> |
| | | <el-checkbox-group v-model="form.applyScope"> |
| | | <el-checkbox label="all">å
¨ä½åå·¥</el-checkbox> |
| | | <el-checkbox label="manager">管çå±</el-checkbox> |
| | | <el-checkbox label="hr">人äºé¨é¨</el-checkbox> |
| | | <el-checkbox label="finance">è´¢å¡é¨é¨</el-checkbox> |
| | | <el-checkbox label="tech">ææ¯é¨é¨</el-checkbox> |
| | | </el-checkbox-group> |
| | | </el-form-item> |
| | | <el-form-item label="åºæ¥å¤ç½®æ¥éª¤" |
| | | prop="execSteps"> |
| | | <div class="exec-steps-container" |
| | | style="width:100%"> |
| | | <div v-for="(step, index) in execStepsList" |
| | | :key="index" |
| | | class="exec-step-item"> |
| | | <div class="step-header"> |
| | | <div> |
| | | <el-input v-model="step.step" |
| | | placeholder="æ¥éª¤" |
| | | style="width: 200px; margin-right: 10px" /> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="removeExecStep(index)" |
| | | style="margin-left: 10px">å é¤</el-button> |
| | | </div> |
| | | <div style="margin-top: 5px;width: 100%;"> |
| | | <el-input v-model="step.description" |
| | | placeholder="æªæ½" |
| | | type="textarea" |
| | | :rows="2" |
| | | style="flex: 1" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="addExecStep" |
| | | style="margin-top: 10px">æ·»å æ¥éª¤</el-button> |
| | | </div> |
| | | </el-form-item> |
| | | </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.planName }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="åºæ¥é¢æ¡ç¼ç "> |
| | | <span>{{ currentKnowledge.planCode }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="åå¸çææ¶é´"> |
| | | {{ currentKnowledge.publishTime }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ ¸å¿è´£ä»»äºº"> |
| | | {{ currentKnowledge.coreResponsorUserName }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="颿¡ç±»å"> |
| | | <el-tag type="warning"> {{ emergencyPlanTypeLabel(currentKnowledge.planType) }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨"> |
| | | {{ currentKnowledge.remark }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <div class="detail-section"> |
| | | <h4>éç¨èå´</h4> |
| | | <div class="key-points"> |
| | | <el-tag v-for="(point, index) in currentKnowledge.applyScope.split(',')" |
| | | :key="index" |
| | | type="primary" |
| | | style="margin-right: 8px; margin-bottom: 8px;"> |
| | | {{ getApplyScopeLabel(point.trim()) }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | <div class="detail-section"> |
| | | <h4>åºæ¥å¤ç½®æ¥éª¤</h4> |
| | | <div class="detail-content"> |
| | | <div v-if="currentKnowledge.execSteps"> |
| | | <div v-for="(step, index) in JSON.parse(currentKnowledge.execSteps)" |
| | | :key="index" |
| | | class="exec-step-view"> |
| | | <span class="step-number">{{ index + 1 }}.</span> |
| | | <span class="step-title">{{ step.step }}ï¼</span> |
| | | <span>{{ step.description }}</span> |
| | | </div> |
| | | </div> |
| | | <div v-else |
| | | class="no-data">æ åºæ¥å¤ç½®æ¥éª¤</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="viewDialogVisible = false">å
³é</el-button> |
| | | <!-- <el-button type="success" @click="markAsFavorite">æ¶è@</el-button> --> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | computed, |
| | | } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | safeContingencyPlanListPage, |
| | | safeContingencyPlanAdd, |
| | | safeContingencyPlanUpdate, |
| | | safeContingencyPlanDel, |
| | | } from "@/api/safeProduction/emergencyPlanReview.js"; |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | planCode: [ |
| | | { required: true, message: "请è¾å
¥åºæ¥é¢æ¡ç¼ç ", trigger: "blur" }, |
| | | ], |
| | | applyScope: [ |
| | | { required: true, message: "è¯·éæ©éç¨èå´", trigger: "change" }, |
| | | ], |
| | | planType: [{ required: true, message: "è¯·éæ©é¢æ¡ç±»å", trigger: "change" }], |
| | | planName: [ |
| | | { required: true, message: "请è¾å
¥åºæ¥é¢æ¡åç§°", trigger: "blur" }, |
| | | ], |
| | | publishTime: [ |
| | | { required: true, message: "è¯·éæ©åå¸çææ¶é´", trigger: "change" }, |
| | | ], |
| | | coreResponsorUserId: [ |
| | | { required: true, message: "è¯·éæ©æ ¸å¿è´£ä»»äºº", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | planName: "", |
| | | planType: "", |
| | | }, |
| | | tableLoading: false, |
| | | page: { |
| | | current: 1, |
| | | size: 20, |
| | | total: 0, |
| | | }, |
| | | tableData: [], |
| | | selectedIds: [], |
| | | form: { |
| | | planCode: "", // 颿¡ç¼å· |
| | | applyScope: [], // éç¨èå´ |
| | | planType: "", // 颿¡ç±»å |
| | | planName: "", // 颿¡åç§° |
| | | publishTime: "", // å叿¶é´ |
| | | coreResponsorUserId: "", // æ ¸å¿è´è´£äººç¨æ·ID |
| | | coreResponsorUserName: "", // æ ¸å¿è´è´£äººç¨æ·å |
| | | remark: "", // 夿³¨ |
| | | execSteps: "", // æ§è¡æ¥éª¤ |
| | | }, |
| | | dialogVisible: false, |
| | | dialogTitle: "", |
| | | dialogType: "add", |
| | | viewDialogVisible: false, |
| | | currentKnowledge: {}, |
| | | }); |
| | | |
| | | const { |
| | | searchForm, |
| | | tableLoading, |
| | | page, |
| | | tableData, |
| | | selectedIds, |
| | | form, |
| | | dialogVisible, |
| | | dialogTitle, |
| | | dialogType, |
| | | viewDialogVisible, |
| | | currentKnowledge, |
| | | } = toRefs(data); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(); |
| | | const execStepsList = ref([]); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "åºæ¥é¢æ¡ç¼ç ", |
| | | prop: "planCode", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "åºæ¥é¢æ¡åç§°", |
| | | prop: "planName", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "åå¸çææ¶é´", |
| | | prop: "publishTime", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "æ ¸å¿è´£ä»»äºº", |
| | | prop: "coreResponsorUserName", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "颿¡ç±»å", |
| | | prop: "planType", |
| | | showOverflowTooltip: true, |
| | | formatData: params => { |
| | | return emergencyPlanTypeLabel(params); |
| | | }, |
| | | }, |
| | | { |
| | | label: "夿³¨", |
| | | prop: "remark", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 200, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | type: "text", |
| | | clickFun: row => { |
| | | viewKnowledge(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const userList = ref([]); |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | getList(); |
| | | startAutoRefresh(); |
| | | }); |
| | | const handleChange = userId => { |
| | | const selectedUser = userList.value.find(user => user.userId === userId); |
| | | if (selectedUser) { |
| | | form.value.coreResponsorUserName = selectedUser.nickName; |
| | | } |
| | | }; |
| | | |
| | | // åºæ¥å¤ç½®æ¥éª¤ç®¡ç |
| | | const addExecStep = () => { |
| | | const stepNumber = execStepsList.value.length + 1; |
| | | execStepsList.value.push({ |
| | | step: `æ¥éª¤${stepNumber}`, |
| | | description: "", |
| | | }); |
| | | }; |
| | | |
| | | const removeExecStep = index => { |
| | | execStepsList.value.splice(index, 1); |
| | | }; |
| | | |
| | | const initExecSteps = execSteps => { |
| | | if (execSteps) { |
| | | try { |
| | | execStepsList.value = JSON.parse(execSteps); |
| | | } catch (e) { |
| | | execStepsList.value = []; |
| | | } |
| | | } else { |
| | | execStepsList.value = []; |
| | | } |
| | | }; |
| | | // å¼å§èªå¨å·æ° |
| | | const startAutoRefresh = () => { |
| | | setInterval(() => { |
| | | getList(); |
| | | }, 600000); // 10åéå·æ°ä¸æ¬¡ (10 * 60 * 1000 = 600000ms) |
| | | }; |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const handleQuery = () => { |
| | | page.value.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | safeContingencyPlanListPage({ ...page.value, ...searchForm.value }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.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, { |
| | | planCode: "", // 颿¡ç¼å· |
| | | applyScope: [], // éç¨èå´ |
| | | planType: "", // 颿¡ç±»å |
| | | planName: "", // 颿¡åç§° |
| | | publishTime: "", // å叿¶é´ |
| | | coreResponsorUserId: "", // æ ¸å¿è´è´£äººç¨æ·ID |
| | | coreResponsorUserName: "", // æ ¸å¿è´è´£äººç¨æ·å |
| | | remark: "", // 夿³¨ |
| | | execSteps: "", // æ§è¡æ¥éª¤ |
| | | }); |
| | | initExecSteps(""); |
| | | } else if (type === "edit" && row) { |
| | | dialogTitle.value = "ç¼è¾åºæ¥é¢æ¡"; |
| | | Object.assign(form.value, { |
| | | id: row.id, |
| | | planCode: row.planCode, // 颿¡ç¼å· |
| | | applyScope: row.applyScope ? row.applyScope.split(",") : [], // éç¨èå´ |
| | | planType: row.planType, // 颿¡ç±»å |
| | | planName: row.planName, // 颿¡åç§° |
| | | publishTime: row.publishTime, // å叿¶é´ |
| | | coreResponsorUserId: row.coreResponsorUserId, // æ ¸å¿è´è´£äººç¨æ·ID |
| | | coreResponsorUserName: row.coreResponsorUserName, // æ ¸å¿è´è´£äººç¨æ·å |
| | | remark: row.remark, // 夿³¨ |
| | | execSteps: row.execSteps, // æ§è¡æ¥éª¤ |
| | | }); |
| | | initExecSteps(row.execSteps); |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // æ¥çåºæ¥é¢æ¡è¯¦æ
|
| | | const viewKnowledge = row => { |
| | | currentKnowledge.value = { ...row }; |
| | | viewDialogVisible.value = true; |
| | | }; |
| | | const getApplyScopeLabel = scope => { |
| | | const scopeMap = { |
| | | all: "å
¨ä½åå·¥", |
| | | manager: "管çå±", |
| | | hr: "人äºé¨é¨", |
| | | finance: "è´¢å¡é¨é¨", |
| | | tech: "ææ¯é¨é¨", |
| | | }; |
| | | return scopeMap[scope] || scope; |
| | | }; |
| | | |
| | | // è·åç±»åæ ç¾ç±»å |
| | | const getTypeTagType = type => { |
| | | const typeMap = { |
| | | contract: "success", |
| | | approval: "warning", |
| | | solution: "primary", |
| | | experience: "info", |
| | | guide: "danger", |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }; |
| | | |
| | | // è·åç±»åæ ç¾ææ¬ |
| | | const getTypeLabel = type => { |
| | | return getKnowledgeTypeLabel(type); |
| | | }; |
| | | |
| | | // è·åæçæ ç¾ç±»å |
| | | 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] || "æªç¥"; |
| | | }; |
| | | 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 { |
| | | await formRef.value.validate(); |
| | | |
| | | // éªè¯åºæ¥å¤ç½®æ¥éª¤ |
| | | for (let i = 0; i < execStepsList.value.length; i++) { |
| | | const step = execStepsList.value[i]; |
| | | if (!step.step || !step.step.trim()) { |
| | | ElMessage.error(`第${i + 1}æ¡æ¥éª¤ç"æ¥éª¤"ä¸è½ä¸ºç©º`); |
| | | return; |
| | | } |
| | | if (!step.description || !step.description.trim()) { |
| | | ElMessage.error(`第${i + 1}æ¡æ¥éª¤ç"æªæ½"ä¸è½ä¸ºç©º`); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // å°åºæ¥å¤ç½®æ¥éª¤è½¬æ¢ä¸ºJSONå符串 |
| | | form.value.execSteps = JSON.stringify(execStepsList.value); |
| | | if (dialogType.value === "add") { |
| | | // æ°å¢åºæ¥é¢æ¡ |
| | | form.value.applyScope = form.value.applyScope.join(","); |
| | | safeContingencyPlanAdd({ ...form.value }) |
| | | .then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("æ·»å æå"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else { |
| | | form.value.applyScope = form.value.applyScope.join(","); |
| | | safeContingencyPlanUpdate({ ...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); |
| | | safeContingencyPlanDel(selectedIds.value).then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("å 餿å"); |
| | | selectedIds.value = []; |
| | | getList(); |
| | | } |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | // ç¨æ·åæ¶ |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | |
| | | // åå
¸å·¥å
· |
| | | const knowledgeTypeOptions = computed(() => knowledge_type?.value || []); |
| | | const getKnowledgeTypeLabel = val => { |
| | | const item = knowledgeTypeOptions.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | 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; |
| | | } |
| | | |
| | | .exec-steps-container { |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 4px; |
| | | padding: 15px; |
| | | background-color: #f9fafc; |
| | | } |
| | | |
| | | .exec-step-item { |
| | | margin-bottom: 10px; |
| | | padding: 10px; |
| | | background-color: #ffffff; |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .step-header { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .exec-step-view { |
| | | margin-bottom: 8px; |
| | | padding-left: 20px; |
| | | position: relative; |
| | | } |
| | | |
| | | .step-number { |
| | | position: absolute; |
| | | left: 0; |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .step-title { |
| | | font-weight: bold; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .no-data { |
| | | color: #909399; |
| | | font-style: italic; |
| | | } |
| | | </style> |
| | |
| | | {{ 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 }} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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="pagination1" |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <!-- æ°å¢/ç¼è¾ç¥è¯å¼¹çª --> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="800px" |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | v-if="dialogType === 'add'" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="140px" |
| | | label-position="top"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å±é©æº" |
| | | prop="safeHazardId"> |
| | | <el-input v-model="valueItem.name" |
| | | readonly |
| | | @click="openSafeHazardSelect" |
| | | placeholder="è¯·éæ©å±é©æº" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æå¨ä½ç½®" |
| | | prop="location"> |
| | | <el-input v-model="valueItem.location" |
| | | disabled |
| | | placeholder="èªå¨å¸¦åº" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å±é©æºç±»å" |
| | | prop="type"> |
| | | <el-input v-model="valueItem.type" |
| | | disabled |
| | | placeholder="èªå¨å¸¦åº" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é£é©ç级" |
| | | prop="riskLevel"> |
| | | <el-input v-model="valueItem.riskLevel" |
| | | disabled |
| | | placeholder="èªå¨å¸¦åº" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåæ°é" |
| | | prop="stockQty"> |
| | | <el-input v-model="valueItem.stockQty" |
| | | disabled |
| | | placeholder="èªå¨å¸¦åº" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢ç¨ç¨é" |
| | | prop="applyPurpose"> |
| | | <el-input v-model="form.applyPurpose" |
| | | placeholder="请è¾å
¥é¢ç¨ç¨é" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢ç¨æ°é" |
| | | prop="applyQty"> |
| | | <el-input v-model="form.applyQty" |
| | | type="number" |
| | | min="0" |
| | | @input="handleApplyQtyChange" |
| | | placeholder="请è¾å
¥é¢ç¨æ°é" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢ç¨äºº" |
| | | prop="applyUserId"> |
| | | <el-select v-model="form.applyUserId" |
| | | disabled |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢ç¨æ¶é´" |
| | | prop="applyTime"> |
| | | <el-date-picker style="width: 100%" |
| | | disabled |
| | | v-model="form.applyTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <el-descriptions :column="2" |
| | | style="margin-bottom: 20px" |
| | | v-if="dialogType === 'edit' || dialogType === 'view'" |
| | | border> |
| | | <el-descriptions-item label="å±é©æºç¼ç "> |
| | | <span>{{ form.code }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å±é©æºåç§°"> |
| | | <span>{{ form.name }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å±é©æºç±»å"> |
| | | <el-tag type="info"> |
| | | {{ getTypeLabel(form.type) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æå¨ä½ç½®"> |
| | | <span>{{ form.location }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="é£é©ç级"> |
| | | <el-tag :type="getTypeTagType(form.riskLevel)"> |
| | | {{ form.riskLevel }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="é¢ç¨ç¨é"> |
| | | <span>{{ form.applyPurpose }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="é¢ç¨æ¶é´"> |
| | | <span>{{ form.applyTime }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="é¢ç¨æ°é"> |
| | | <span>{{ form.applyQty }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="é¢ç¨äºº"> |
| | | <span>{{ form.applyUserName }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item v-if="dialogType === 'view' && form.returnUserId" |
| | | label="å½è¿äºº"> |
| | | <span>{{ form.returnUserName }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item v-if="dialogType === 'view' && form.returnTime" |
| | | label="å½è¿æ¶é´"> |
| | | <span>{{ form.returnTime }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item v-if="dialogType === 'view' && form.returnUserId" |
| | | label="å½è¿æ
åµè¯´æ"> |
| | | <span>{{ form.returnRemark }}</span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-form ref="formRef1" |
| | | v-if="dialogType === 'edit'" |
| | | :model="form" |
| | | :rules="rules1" |
| | | label-width="140px" |
| | | label-position="top"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½è¿äºº" |
| | | prop="returnUserId"> |
| | | <el-select v-model="form.returnUserId" |
| | | disabled |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½è¿æ¶é´" |
| | | prop="returnTime"> |
| | | <el-date-picker style="width: 100%" |
| | | disabled |
| | | v-model="form.returnTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å½è¿æ
åµè¯´æ" |
| | | prop="applyPurpose"> |
| | | <el-input v-model="form.returnRemark" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥å½è¿æ
åµè¯´æ" /> |
| | | </el-form-item> |
| | | </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="safeHazardSelectVisible" |
| | | title="éæ©å±é©æº" |
| | | width="800px" |
| | | :close-on-click-modal="false"> |
| | | <div> |
| | | <el-table :data="safeHazardList" |
| | | border |
| | | v-loading="safeHazardLoading" |
| | | style="width: 100%" |
| | | @row-click="handleSafeHazardSelect"> |
| | | <el-table-column prop="code" |
| | | label="å±é©æºç¼ç " |
| | | width="180" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="name" |
| | | label="å±é©æºåç§°" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="type" |
| | | label="å±é©æºç±»å" |
| | | width="120" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ getTypeLabel(scope.row.type) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" |
| | | label="æå¨ä½ç½®" |
| | | width="180" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="riskLevel" |
| | | label="é£é©ç级" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getTypeTagType(scope.row.riskLevel)"> |
| | | {{ scope.row.riskLevel }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="stockQty" |
| | | label="åºåæ°é" |
| | | width="100" /> |
| | | </el-table> |
| | | <pagination :total="safeHazardPage.total" |
| | | style="margin-top: 20px" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="safeHazardPage.current" |
| | | :limit="safeHazardPage.size" |
| | | @pagination="safeHazardPagination" /> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="safeHazardSelectVisible = false">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | computed, |
| | | } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { safeHazardListPage } from "@/api/safeProduction/hazardSourceLedger.js"; |
| | | import { |
| | | safeHazardRecordListPage, |
| | | safeHazardRecordDel, |
| | | safeHazardRecordAdd, |
| | | safeHazardRecordUpdate, |
| | | } from "@/api/safeProduction/hazardousMaterialsControl.js"; |
| | | import dayjs from "dayjs"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | const userStore = useUserStore(); |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | applyPurpose: [ |
| | | { required: true, message: "请è¾å
¥é¢ç¨ç¨é", trigger: "blur" }, |
| | | ], |
| | | applyQty: [{ required: true, message: "请è¾å
¥é¢ç¨æ°é", trigger: "blur" }], |
| | | safeHazardId: [ |
| | | { required: true, message: "è¯·éæ©å±é©æº", trigger: "change" }, |
| | | ], |
| | | }; |
| | | const rules1 = { |
| | | returnRemark: [ |
| | | { required: true, message: "请è¾å
¥å½è¿æ
åµè¯´æ", trigger: "blur" }, |
| | | ], |
| | | }; |
| | | // ååºå¼æ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | name: "", |
| | | type: "", |
| | | }, |
| | | tableLoading: false, |
| | | page: { |
| | | current: 1, |
| | | size: 20, |
| | | total: 0, |
| | | }, |
| | | tableData: [], |
| | | selectedIds: [], |
| | | form: { |
| | | applyPurpose: "", // é¢ç¨ç¨é |
| | | applyTime: "", // é¢ç¨æ¶é´ |
| | | applyQty: "", // é¢ç¨æ°é |
| | | applyUserId: "", // é¢ç¨äººID |
| | | safeHazardId: "", // å±é©æºID |
| | | }, |
| | | dialogVisible: false, |
| | | dialogTitle: "", |
| | | dialogType: "add", |
| | | viewDialogVisible: false, |
| | | currentKnowledge: {}, |
| | | safeHazardSelectVisible: false, |
| | | safeHazardList: [], |
| | | safeHazardLoading: false, |
| | | safeHazardPage: { |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }, |
| | | }); |
| | | |
| | | const { |
| | | searchForm, |
| | | tableLoading, |
| | | page, |
| | | tableData, |
| | | selectedIds, |
| | | form, |
| | | dialogVisible, |
| | | dialogTitle, |
| | | dialogType, |
| | | viewDialogVisible, |
| | | currentKnowledge, |
| | | safeHazardSelectVisible, |
| | | safeHazardList, |
| | | safeHazardLoading, |
| | | safeHazardPage, |
| | | } = toRefs(data); |
| | | const valueItem = ref({}); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(); |
| | | const formRef1 = ref(); |
| | | const riskLevelOptions = ref([ |
| | | { value: "ä½é£é©", label: "ä½é£é©" }, |
| | | { value: "ä¸è¬é£é©", label: "ä¸è¬é£é©" }, |
| | | { value: "è¾å¤§é£é©", label: "è¾å¤§é£é©" }, |
| | | { value: "é大é£é©", label: "é大é£é©" }, |
| | | ]); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "é¢ç¨åå·", |
| | | prop: "materialRecordCode", |
| | | width: 130, |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å±é©æºç¼ç ", |
| | | prop: "code", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å±é©æºåç§°", |
| | | prop: "name", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å±é©æºç±»å", |
| | | prop: "type", |
| | | showOverflowTooltip: true, |
| | | formatData: params => { |
| | | return getTypeLabel(params); |
| | | }, |
| | | }, |
| | | { |
| | | label: "é£é©ç级", |
| | | prop: "riskLevel", |
| | | showOverflowTooltip: true, |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | ä½é£é©: "info", |
| | | ä¸è¬é£é©: "info", |
| | | è¾å¤§é£é©: "warning", |
| | | é大é£é©: "danger", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "é¢ç¨ç¨é", |
| | | prop: "applyPurpose", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "é¢ç¨æ¶é´", |
| | | prop: "applyTime", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "é¢ç¨æ°é", |
| | | prop: "applyQty", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å½è¿æ¶é´", |
| | | prop: "returnTime", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å½è¿äºº", |
| | | prop: "returnUserName", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "å½è¿æ
åµè¯´æ", |
| | | prop: "returnRemark", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "æå¨ä½ç½®", |
| | | prop: "location", |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 130, |
| | | operation: [ |
| | | { |
| | | name: "å½è¿", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | disabled: row => row.returnUserId, |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("view", row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const userList = ref([]); |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | getCurrentFactoryName(); |
| | | getList(); |
| | | startAutoRefresh(); |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | }); |
| | | const currentUserId = ref(""); |
| | | const currentUserName = ref(""); |
| | | const getCurrentFactoryName = async () => { |
| | | let res = await userStore.getInfo(); |
| | | currentUserId.value = res.user.userId; |
| | | currentUserName.value = res.user.nickName; |
| | | }; |
| | | |
| | | // å¤çç¨æ·éæ©åå |
| | | const handleUserChange = userId => { |
| | | const selectedUser = userList.value.find(user => user.userId === userId); |
| | | if (selectedUser) { |
| | | form.value.principalUser = selectedUser.nickName; |
| | | form.value.principalMobile = selectedUser.phonenumber; |
| | | } |
| | | }; |
| | | const handleApplyQtyChange = () => { |
| | | if (form.value.applyQty > valueItem.value.stockQty) { |
| | | ElMessage.error("é¢ç¨æ°éä¸è½å¤§äºåºåæ°é"); |
| | | form.value.applyQty = ""; |
| | | } |
| | | }; |
| | | |
| | | // å¼å§èªå¨å·æ° |
| | | const startAutoRefresh = () => { |
| | | setInterval(() => { |
| | | getList(); |
| | | }, 600000); // 10åéå·æ°ä¸æ¬¡ (10 * 60 * 1000 = 600000ms) |
| | | }; |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const handleQuery = () => { |
| | | page.value.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | safeHazardRecordListPage({ ...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 openSafeHazardSelect = async () => { |
| | | await fetchSafeHazardList(); |
| | | safeHazardSelectVisible.value = true; |
| | | }; |
| | | |
| | | const fetchSafeHazardList = () => { |
| | | safeHazardLoading.value = true; |
| | | return safeHazardListPage({ |
| | | page: safeHazardPage.value.current, |
| | | size: safeHazardPage.value.size, |
| | | }) |
| | | .then(res => { |
| | | safeHazardList.value = res.data.records; |
| | | safeHazardPage.value.total = res.data.total; |
| | | }) |
| | | .finally(() => { |
| | | safeHazardLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleSafeHazardSelect = item => { |
| | | valueItem.value = { |
| | | ...item, |
| | | }; |
| | | valueItem.value.type = getTypeLabel(valueItem.value.type); |
| | | form.value.safeHazardId = item.id; |
| | | safeHazardSelectVisible.value = false; |
| | | }; |
| | | |
| | | const safeHazardPagination = obj => { |
| | | safeHazardPage.value.current = obj.page; |
| | | safeHazardPage.value.size = obj.limit; |
| | | fetchSafeHazardList(); |
| | | }; |
| | | |
| | | // å页å¤ç |
| | | const pagination1 = 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 = "é¢ç¨å±é©æº"; |
| | | // é置表å |
| | | form.value = {}; |
| | | Object.assign(form.value, { |
| | | applyPurpose: "", // é¢ç¨ç¨é |
| | | applyTime: dayjs().format("YYYY-MM-DD"), // é¢ç¨æ¶é´ |
| | | applyQty: "", // é¢ç¨æ°é |
| | | applyUserId: currentUserId.value, // é¢ç¨äººID |
| | | safeHazardId: "", // å±é©æºID |
| | | }); |
| | | valueItem.value = {}; |
| | | } else if (type === "edit" && row) { |
| | | dialogTitle.value = "å½è¿"; |
| | | Object.assign(form.value, { |
| | | id: row.id, // 主é®ID |
| | | materialRecordCode: row.materialRecordCode, // åå· |
| | | applyPurpose: row.applyPurpose, // é¢ç¨ç¨é |
| | | applyTime: row.applyTime, // é¢ç¨æ¶é´ |
| | | applyQty: row.applyQty, // é¢ç¨æ°é |
| | | applyUserId: row.applyUserId, // é¢ç¨äººID |
| | | safeHazardId: row.safeHazardId, // å±é©æºID |
| | | applyUserName: row.applyUserName, // é¢ç¨äººå§å |
| | | code: row.code, // å±é©æºç¼ç |
| | | name: row.name, // å±é©æºåç§° |
| | | type: row.type, // å±é©æºç±»å |
| | | location: row.location, // æå¨ä½ç½® |
| | | riskLevel: row.riskLevel, // é£é©ç级 |
| | | type: row.type, // å±é©æºç±»å |
| | | returnTime: dayjs().format("YYYY-MM-DD"), // å½è¿æ¶é´ |
| | | returnUserName: "", // å½è¿äººå§å |
| | | returnUserId: currentUserId.value, // å½è¿äººID |
| | | returnRemark: "", // å½è¿æ
åµè¯´æ |
| | | }); |
| | | } else if (type === "view") { |
| | | dialogTitle.value = "æ¥ç"; |
| | | form.value = { ...row }; |
| | | } |
| | | 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 getTypeLabel = type => { |
| | | return getKnowledgeTypeLabel(type); |
| | | }; |
| | | |
| | | // è·åæçæ ç¾ç±»å |
| | | 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] || "æªç¥"; |
| | | }; |
| | | |
| | | // æäº¤å±é©æºè¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | | if (dialogType.value === "add") { |
| | | await formRef.value.validate(); |
| | | safeHazardRecordAdd({ ...form.value }) |
| | | .then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("æ·»å æå"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }); |
| | | } else { |
| | | await formRef1.value.validate(); |
| | | safeHazardRecordUpdate({ ...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); |
| | | safeHazardRecordDel(selectedIds.value).then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success("å 餿å"); |
| | | selectedIds.value = []; |
| | | getList(); |
| | | } |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | // ç¨æ·åæ¶ |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const { proxy } = getCurrentInstance(); |
| | | const { hazard_source_type } = proxy.useDict("hazard_source_type"); |
| | | |
| | | // åå
¸å·¥å
· |
| | | const knowledgeTypeOptions = computed(() => hazard_source_type?.value || []); |
| | | const getKnowledgeTypeLabel = val => { |
| | | const item = knowledgeTypeOptions.value.find( |
| | | i => String(i.value) === String(val) |
| | | ); |
| | | return item ? item.label : val; |
| | | }; |
| | | 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> |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="è§ç¨èµè´¨ç±»å" |
| | | prop="type" |
| | | show-overflow-tooltip /> |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ type_qualification.find(item => item.value === scope.row.type)?.label || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çæ¬å·" |
| | | prop="version" |
| | | width="180" |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'approval' ? '审æ¹' : '详æ
'" |
| | | width="700px" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | ref="formRef"> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="æµç¨ç¼å·ï¼" |
| | | prop="approveId"> |
| | | <el-input v-model="form.approveId" |
| | | placeholder="èªå¨ç¼å·" |
| | | clearable |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="ç³è¯·é¨é¨ï¼"> |
| | | <el-select disabled |
| | | v-model="form.approveDeptId" |
| | | placeholder="éæ©é¨é¨"> |
| | | <el-option v-for="user in productOptions" |
| | | :key="user.deptId" |
| | | :label="user.deptName" |
| | | :value="user.deptId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-if="!isQuotationApproval && !isPurchaseApproval"> |
| | | <el-col :span="24"> |
| | | <el-form-item :label="props.approveType == 5 ? 'éè´ååå·ï¼' : '审æ¹äºç±ï¼'" |
| | | prop="approveReason"> |
| | | <el-input v-model="form.approveReason" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | type="textarea" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 审æ¹äººéæ©ï¼å¨æèç¹ï¼ --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·äººï¼" |
| | | prop="approveUser"> |
| | | <el-select v-model="form.approveUser" |
| | | placeholder="éæ©äººå" |
| | | disabled> |
| | | <el-option v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·æ¥æï¼" |
| | | prop="approveTime"> |
| | | <el-date-picker v-model="form.approveTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <!-- æ¥ä»·å®¡æ¹ï¼å±ç¤ºæ¥ä»·è¯¦æ
ï¼å¤ç¨é宿¥ä»·"æ¥ç详æ
å¯¹è¯æ¡"å
å®¹ç»æï¼ --> |
| | | <div v-if="isQuotationApproval" |
| | | style="margin: 10px 0 18px;"> |
| | | <el-divider content-position="left">æ¥ä»·è¯¦æ
</el-divider> |
| | | <el-skeleton :loading="quotationLoading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="h3" |
| | | style="width: 30%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <template #default> |
| | | <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo" |
| | | description="æªæ¥è¯¢å°å¯¹åºæ¥ä»·è¯¦æ
" /> |
| | | <template v-else> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="æ¥ä»·åå·">{{ currentQuotation.quotationNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ currentQuotation.customer }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¸å¡å">{{ currentQuotation.salesperson }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·æ¥æ">{{ currentQuotation.quotationDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="æææè³">{{ currentQuotation.validDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ currentQuotation.paymentMethod }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·æ»é¢" |
| | | :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <div style="margin-top: 20px;"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="currentQuotation.products || []" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="product" |
| | | label="产ååç§°" /> |
| | | <el-table-column prop="specification" |
| | | label="è§æ ¼åå·" /> |
| | | <el-table-column prop="unit" |
| | | label="åä½" /> |
| | | <el-table-column prop="unitPrice" |
| | | label="åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <div v-if="currentQuotation.remark" |
| | | style="margin-top: 20px;"> |
| | | <h4>夿³¨</h4> |
| | | <p>{{ currentQuotation.remark }}</p> |
| | | </div> |
| | | </template> |
| | | </template> |
| | | </el-skeleton> |
| | | </div> |
| | | <!-- éè´å®¡æ¹ï¼å±ç¤ºéè´è¯¦æ
--> |
| | | <div v-if="isPurchaseApproval" |
| | | style="margin: 10px 0 18px;"> |
| | | <el-divider content-position="left">éè´è¯¦æ
</el-divider> |
| | | <el-skeleton :loading="purchaseLoading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="h3" |
| | | style="width: 30%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | <el-skeleton-item variant="text" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <template #default> |
| | | <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber" |
| | | description="æªæ¥è¯¢å°å¯¹åºéè´è¯¦æ
" /> |
| | | <template v-else> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="éè´ååå·">{{ currentPurchase.purchaseContractNumber }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¾åºååç§°">{{ currentPurchase.supplierName }}</el-descriptions-item> |
| | | <el-descriptions-item label="项ç®åç§°">{{ currentPurchase.projectName }}</el-descriptions-item> |
| | | <el-descriptions-item label="éå®ååå·">{{ currentPurchase.salesContractNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç¾è®¢æ¥æ">{{ currentPurchase.executionDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="å½å
¥æ¥æ">{{ currentPurchase.entryDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ currentPurchase.paymentMethod }}</el-descriptions-item> |
| | | <el-descriptions-item label="ååéé¢" |
| | | :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <div style="margin-top: 20px;"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="currentPurchase.productData || []" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="productCategory" |
| | | label="产ååç§°" /> |
| | | <el-table-column prop="specificationModel" |
| | | label="è§æ ¼åå·" /> |
| | | <el-table-column prop="unit" |
| | | label="åä½" /> |
| | | <el-table-column prop="quantity" |
| | | label="æ°é" /> |
| | | <el-table-column prop="taxInclusiveUnitPrice" |
| | | label="å«ç¨åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="taxInclusiveTotalPrice" |
| | | label="å«ç¨æ»ä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </template> |
| | | </template> |
| | | </el-skeleton> |
| | | </div> |
| | | <el-form :model="{ activities }" |
| | | ref="formRef" |
| | | label-position="top"> |
| | | <el-steps :active="getActiveStep()" |
| | | finish-status="success" |
| | | process-status="process" |
| | | align-center |
| | | direction="vertical"> |
| | | <el-step v-for="(activity, index) in activities" |
| | | :key="index" |
| | | finish-status="success" |
| | | :title="getNodeTitle(index, activities.length)" |
| | | :description="activity.approveNodeUser" |
| | | :icon="getNodeIcon(activity, index)"> |
| | | <template #icon> |
| | | <el-icon v-if="activity.approveNodeStatus === 2" |
| | | color="red" |
| | | :size="22"> |
| | | <WarningFilled /> |
| | | </el-icon> |
| | | <el-icon v-else-if="activity.isShen" |
| | | color="#1890ff" |
| | | :size="22"> |
| | | <Edit /> |
| | | </el-icon> |
| | | <el-icon v-else-if="activity.approveNodeStatus === 1" |
| | | color="#67C23A" |
| | | :size="26"> |
| | | <Check /> |
| | | </el-icon> |
| | | <el-icon v-else |
| | | color="#C0C4CC" |
| | | :size="22"> |
| | | <MoreFilled /> |
| | | </el-icon> |
| | | </template> |
| | | <template #title> |
| | | <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span> |
| | | </template> |
| | | <template #description> |
| | | <div class="node-user"> |
| | | <div class="avatar-wrapper"> |
| | | <img :src="userStore.avatar" |
| | | class="user-avatar" |
| | | alt="" /> |
| | | </div> |
| | | <span style="color: #000000">{{ activity.approveNodeUser }}-{{activity.isApproval}}</span> |
| | | </div> |
| | | <div v-if="!activity.isShen" |
| | | class="node-reason"> |
| | | <span>å®¡æ¹æè§ï¼</span>{{ activity.approveNodeReason }} |
| | | </div> |
| | | <div v-if="!activity.isShen" |
| | | class="node-reason"> |
| | | <span>ç¾åï¼</span> |
| | | <img :src="activity.urlTem" |
| | | class="signImg" |
| | | alt="" |
| | | v-if="activity.urlTem" /> |
| | | </div> |
| | | <div v-else-if="activity.isShen"> |
| | | <el-form-item :prop="'activities.' + index + '.approveNodeReason'" |
| | | :rules="[{ required: true, message: 'å®¡æ¹æè§ä¸è½ä¸ºç©º', trigger: 'blur' }]"> |
| | | <el-input v-model="activity.approveNodeReason" |
| | | clearable |
| | | type="textarea" |
| | | :disabled="operationType === 'view'"></el-input> |
| | | </el-form-item> |
| | | </div> |
| | | </template> |
| | | </el-step> |
| | | </el-steps> |
| | | </el-form> |
| | | <template #footer |
| | | v-if="operationType === 'approval'"> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm(2)">ä¸éè¿</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm(1)">éè¿</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | computed, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | reactive, |
| | | ref, |
| | | toRefs, |
| | | } from "vue"; |
| | | import { |
| | | approveProcessDetails, |
| | | getDept, |
| | | updateApproveNode, |
| | | } from "@/api/collaborativeApproval/approvalProcess.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { |
| | | WarningFilled, |
| | | Edit, |
| | | Check, |
| | | MoreFilled, |
| | | } from "@element-plus/icons-vue"; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js"; |
| | | const emit = defineEmits(["close"]); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const props = defineProps({ |
| | | approveType: { |
| | | type: [Number, String], |
| | | default: 0, |
| | | }, |
| | | }); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const activities = ref([]); |
| | | const formRef = ref(null); |
| | | const userStore = useUserStore(); |
| | | const productOptions = ref([]); |
| | | const userList = ref([]); |
| | | const quotationLoading = ref(false); |
| | | const currentQuotation = ref({}); |
| | | const purchaseLoading = ref(false); |
| | | const currentPurchase = ref({}); |
| | | const isQuotationApproval = computed(() => Number(props.approveType) === 6); |
| | | const isPurchaseApproval = computed(() => Number(props.approveType) === 5); |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | approveTime: "", |
| | | approveId: "", |
| | | approveUser: "", |
| | | approveDeptId: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | }, |
| | | }); |
| | | const { form } = toRefs(data); |
| | | |
| | | // èç¹æ é¢ |
| | | const getNodeTitle = (index, len) => { |
| | | if (index === len - 1) return "ç»æ"; |
| | | return "审æ¹"; |
| | | }; |
| | | |
| | | // è·åå½åæ¿æ´»æ¥éª¤ |
| | | const getActiveStep = () => { |
| | | // å¦æææ isShen é½ä¸º falseï¼è¿åæåä¸ä¸ªæ¥éª¤ï¼å
¨é¨å®æï¼ |
| | | const hasActive = activities.value.some(a => a.isShen === true); |
| | | if (!hasActive) return activities.value.length; |
| | | // å½åèç¹ç´¢å¼ |
| | | return activities.value.findIndex(a => a.isShen == true); |
| | | }; |
| | | // æ¥éª¤icon |
| | | const getNodeIcon = (activity, index) => { |
| | | if (activity.approveNodeStatus === 2) return "el-icon-warning"; // ä¸éè¿ |
| | | if (activity.isShen) return "Edit"; |
| | | return ""; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | currentQuotation.value = {}; |
| | | currentPurchase.value = {}; |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | form.value = { ...row }; |
| | | // ç«å³æ¸
é¤è¡¨åéªè¯ç¶æï¼å ä¸ºåæ®µæ¯disabledçï¼ä¸éè¦éªè¯ï¼ |
| | | nextTick(() => { |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | }); |
| | | // ç¡®ä¿é项å è½½å®æååå¹é
å¼ç±»å |
| | | getProductOptions().then(() => { |
| | | // ç¡®ä¿å¼ç±»åå¹é
ï¼å¦æé项已å è½½ï¼ |
| | | if (productOptions.value.length > 0 && form.value.approveDeptId) { |
| | | const matchedOption = productOptions.value.find( |
| | | opt => |
| | | opt.deptId == form.value.approveDeptId || |
| | | String(opt.deptId) === String(form.value.approveDeptId) |
| | | ); |
| | | if (matchedOption) { |
| | | form.value.approveDeptId = matchedOption.deptId; |
| | | } |
| | | } |
| | | // 忬¡æ¸
é¤éªè¯ï¼ç¡®ä¿é项å è½½åå¼å¹é
æ£ç¡® |
| | | nextTick(() => { |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | // æ¥ä»·å®¡æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"æ¥ä»·åå·"廿¥æ¥ä»·å表 |
| | | if (isQuotationApproval.value) { |
| | | const quotationNo = row?.approveReason; |
| | | if (quotationNo) { |
| | | quotationLoading.value = true; |
| | | getQuotationList({ quotationNo }) |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | | currentQuotation.value = records[0] || {}; |
| | | }) |
| | | .finally(() => { |
| | | quotationLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // éè´å®¡æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"éè´ååå·"廿¥éè´è¯¦æ
|
| | | if (isPurchaseApproval.value) { |
| | | const purchaseContractNumber = row?.approveReason; |
| | | if (purchaseContractNumber) { |
| | | purchaseLoading.value = true; |
| | | getPurchaseByCode({ purchaseContractNumber }) |
| | | .then(res => { |
| | | currentPurchase.value = res; |
| | | }) |
| | | .catch(err => { |
| | | console.error("æ¥è¯¢éè´è¯¦æ
失败:", err); |
| | | proxy.$modal.msgError("æ¥è¯¢éè´è¯¦æ
失败"); |
| | | }) |
| | | .finally(() => { |
| | | purchaseLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | approveProcessDetails(row.approveId).then(res => { |
| | | activities.value = res.data; |
| | | // å¢å isApprovalåæ®µ |
| | | activities.value.forEach(item => { |
| | | if (item.url && item.url.includes("word")) { |
| | | item.urlTem = item.url.replaceAll("word", "img"); |
| | | } else { |
| | | item.urlTem = item.url; |
| | | } |
| | | if (item.approveNodeStatus === 2) { |
| | | item.isApproval = "已驳å"; |
| | | } else if (item.approveNodeStatus === 1) { |
| | | item.isApproval = "å·²åæ"; |
| | | } else { |
| | | item.isApproval = "æªå®¡æ¹"; |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | const getProductOptions = () => { |
| | | return getDept().then(res => { |
| | | productOptions.value = res.data; |
| | | }); |
| | | }; |
| | | // æäº¤å®¡æ¹ |
| | | const submitForm = status => { |
| | | const filteredActivities = activities.value.filter( |
| | | activity => activity.isShen |
| | | ); |
| | | if (!filteredActivities || filteredActivities.length === 0) { |
| | | proxy.$modal.msgError("æªæ¾å°å¾
审æ¹çèç¹"); |
| | | return; |
| | | } |
| | | const currentActivity = filteredActivities[0]; |
| | | if (!currentActivity) { |
| | | proxy.$modal.msgError("æªæ¾å°å¾
审æ¹çèç¹"); |
| | | return; |
| | | } |
| | | currentActivity.approveNodeStatus = status; |
| | | // 夿æ¯å¦ä¸ºæå䏿¥ |
| | | const isLast = |
| | | activities.value.findIndex(a => a.isShen) === activities.value.length - 1; |
| | | updateApproveNode({ ...currentActivity, isLast }).then(() => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | quotationLoading.value = false; |
| | | currentQuotation.value = {}; |
| | | purchaseLoading.value = false; |
| | | currentPurchase.value = {}; |
| | | emit("close"); |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .node-user { |
| | | margin: 10px 0; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .node-status { |
| | | color: #1890ff; |
| | | margin-left: 8px; |
| | | font-size: 14px; |
| | | } |
| | | .node-reason { |
| | | font-size: 15px; |
| | | color: #333; |
| | | margin: 10px 0; |
| | | } |
| | | .user-avatar { |
| | | cursor: pointer; |
| | | width: 30px; |
| | | height: 30px; |
| | | border-radius: 50px; |
| | | } |
| | | .signImg { |
| | | cursor: pointer; |
| | | width: 200px; |
| | | height: 60px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢å®¡æ¹æµç¨' : 'ç¼è¾å®¡æ¹æµç¨'" |
| | | width="50%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="æµç¨ç¼å·ï¼" |
| | | prop="approveId"> |
| | | <el-input v-model="form.approveId" |
| | | placeholder="èªå¨ç¼å·" |
| | | clearable |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="ç³è¯·é¨é¨ï¼" |
| | | prop="approveDeptName"> |
| | | <!-- <el-input v-model="form.approveDeptName" placeholder="请è¾å
¥" clearable/>--> |
| | | <el-select v-model="form.approveDeptId" |
| | | placeholder="éæ©é¨é¨" |
| | | @change="handleDeptChange"> |
| | | <el-option v-for="user in productOptions" |
| | | :key="user.deptId" |
| | | :label="user.deptName" |
| | | :value="user.deptId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item :label="props.approveType == 5 ? 'éè´ååå·ï¼' : '审æ¹äºç±ï¼'" |
| | | prop="approveReason"> |
| | | <el-input v-model="form.approveReason" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | type="textarea" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- è¯·åæ¶é´ï¼ä»
å½ approveType 为 2 æ¶æ¾ç¤ºï¼ --> |
| | | <el-row :gutter="30" |
| | | v-if="props.approveType == 2"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="请åå¼å§æ¶é´ï¼" |
| | | prop="startDate"> |
| | | <el-date-picker v-model="form.startDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©å¼å§æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="请åç»ææ¶é´ï¼" |
| | | prop="endDate"> |
| | | <el-date-picker v-model="form.endDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©ç»ææ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- æ¥ééé¢ï¼ä»
å½ approveType 为 4 æ¶æ¾ç¤ºï¼ --> |
| | | <el-row v-if="props.approveType == 4"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="æ¥ééé¢ï¼" |
| | | prop="price"> |
| | | <el-input-number v-model="form.price" |
| | | placeholder="请è¾å
¥æ¥ééé¢" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="0.01" |
| | | style="width: 100%" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- åºå·®å°ç¹ï¼ä»
å½ approveType 为 3 æ¶æ¾ç¤ºï¼ --> |
| | | <el-row v-if="props.approveType == 3"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="åºå·®å°ç¹ï¼" |
| | | prop="location"> |
| | | <el-input v-model="form.location" |
| | | placeholder="请è¾å
¥åºå·®å°ç¹" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 审æ¹äººéæ©ï¼å¨æèç¹ï¼ --> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item> |
| | | <template #label> |
| | | <span>审æ¹äººéæ©ï¼</span> |
| | | <el-button type="primary" |
| | | @click="addApproverNode" |
| | | style="margin-left: 8px;">æ°å¢èç¹</el-button> |
| | | </template> |
| | | <div style="display: flex; align-items: flex-end; flex-wrap: wrap;"> |
| | | <div v-for="(node, index) in approverNodes" |
| | | :key="node.id" |
| | | style="margin-right: 30px; text-align: center; margin-bottom: 10px;"> |
| | | <div> |
| | | <span>审æ¹äºº</span> |
| | | â |
| | | </div> |
| | | <el-select v-model="node.userId" |
| | | placeholder="éæ©äººå" |
| | | style="width: 120px; margin-bottom: 8px;"> |
| | | <el-option v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | <div> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="removeApproverNode(index)" |
| | | v-if="approverNodes.length > 1">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·äººï¼" |
| | | prop="approveUser"> |
| | | <el-select v-model="form.approveUser" |
| | | placeholder="éæ©äººå" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false"> |
| | | <el-option v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·æ¥æï¼" |
| | | prop="approveTime"> |
| | | <el-date-picker v-model="form.approveTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶ææï¼" |
| | | prop="remark"> |
| | | <el-upload v-model:file-list="fileList" |
| | | :action="upload.url" |
| | | multiple |
| | | ref="fileUpload" |
| | | auto-upload |
| | | :headers="upload.headers" |
| | | :before-upload="handleBeforeUpload" |
| | | :on-error="handleUploadError" |
| | | :on-success="handleUploadSuccess" |
| | | :on-remove="handleRemove"> |
| | | <el-button type="primary" |
| | | v-if="operationType !== 'view'">ä¸ä¼ </el-button> |
| | | <template #tip |
| | | v-if="operationType !== 'view'"> |
| | | <div class="el-upload__tip"> |
| | | æä»¶æ ¼å¼æ¯æ |
| | | docï¼docxï¼xlsï¼xlsxï¼pptï¼pptxï¼pdfï¼txtï¼xmlï¼jpgï¼jpegï¼pngï¼gifï¼bmpï¼rarï¼zipï¼7z |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { |
| | | approveProcessAdd, |
| | | approveProcessGetInfo, |
| | | approveProcessUpdate, |
| | | getDept, |
| | | } from "@/api/collaborativeApproval/approvalProcess.js"; |
| | | import { delLedgerFile } from "@/api/salesManagement/salesLedger.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | import log from "@/views/monitor/job/log.vue"; |
| | | const userStore = useUserStore(); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const fileList = ref([]); |
| | | const upload = reactive({ |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/file/upload", |
| | | // 设置ä¸ä¼ ç请æ±å¤´é¨ |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | }); |
| | | const data = reactive({ |
| | | form: { |
| | | approveTime: "", |
| | | approveId: "", |
| | | approveUser: "", |
| | | approveDeptId: "", |
| | | approveDeptName: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | tempFileIds: [], |
| | | approverList: [], // æ°å¢å段ï¼å卿æèç¹ç审æ¹äººid |
| | | startDate: "", // 请åå¼å§æ¶é´ |
| | | endDate: "", // 请åç»ææ¶é´ |
| | | price: null, // æ¥ééé¢ |
| | | location: "", // åºå·®å°ç¹ |
| | | }, |
| | | rules: { |
| | | approveTime: [{ required: false, message: "请è¾å
¥", trigger: "change" }], |
| | | approveId: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | approveUser: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | approveDeptName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | approveReason: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | startDate: [ |
| | | { required: true, message: "è¯·éæ©è¯·åå¼å§æ¶é´", trigger: "change" }, |
| | | ], |
| | | endDate: [ |
| | | { required: true, message: "è¯·éæ©è¯·åç»ææ¶é´", trigger: "change" }, |
| | | ], |
| | | price: [{ required: true, message: "请è¾å
¥æ¥ééé¢", trigger: "blur" }], |
| | | location: [{ required: true, message: "请è¾å
¥åºå·®å°ç¹", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const productOptions = ref([]); |
| | | const currentApproveStatus = ref(0); |
| | | const props = defineProps({ |
| | | approveType: { |
| | | type: [Number, String], |
| | | default: 0, |
| | | }, |
| | | }); |
| | | |
| | | // 审æ¹äººèç¹ç¸å
³ |
| | | const approverNodes = ref([{ id: 1, userId: null }]); |
| | | let nextApproverId = 2; |
| | | const userList = ref([]); |
| | | function addApproverNode() { |
| | | approverNodes.value.push({ id: nextApproverId++, userId: null }); |
| | | } |
| | | function removeApproverNode(index) { |
| | | approverNodes.value.splice(index, 1); |
| | | } |
| | | // å¤çé¨é¨éæ©åå |
| | | const handleDeptChange = deptId => { |
| | | if (deptId) { |
| | | const selectedDept = productOptions.value.find( |
| | | dept => dept.deptId === deptId |
| | | ); |
| | | if (selectedDept) { |
| | | form.value.approveDeptName = selectedDept.deptName; |
| | | } |
| | | } else { |
| | | form.value.approveDeptName = ""; |
| | | } |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | form.value = {}; |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | form.value.approveUser = userStore.id; |
| | | form.value.approveTime = getCurrentDate(); |
| | | |
| | | // è·åå½åç¨æ·ä¿¡æ¯å¹¶è®¾ç½®é¨é¨ID |
| | | form.value.approveDeptId = userStore.currentDeptId; |
| | | |
| | | // å è½½é¨é¨é项ï¼å¹¶å¨å è½½å®æå设置é¨é¨åç§° |
| | | getProductOptions(); |
| | | if (operationType.value === "edit") { |
| | | fileList.value = row.commonFileList; |
| | | form.value.tempFileIds = fileList.value.map(file => file.id); |
| | | currentApproveStatus.value = row.approveStatus; |
| | | approveProcessGetInfo({ id: row.approveId, approveReason: "1" }).then( |
| | | res => { |
| | | form.value = { ...res.data }; |
| | | // 忾审æ¹äºº |
| | | if (res.data && res.data.approveUserIds) { |
| | | const userIds = res.data.approveUserIds.split(","); |
| | | approverNodes.value = userIds.map((userId, idx) => ({ |
| | | id: idx + 1, |
| | | userId: parseInt(userId.trim()), |
| | | })); |
| | | nextApproverId = userIds.length + 1; |
| | | } else { |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | nextApproverId = 2; |
| | | } |
| | | } |
| | | ); |
| | | } |
| | | }; |
| | | const getProductOptions = () => { |
| | | return getDept().then(res => { |
| | | productOptions.value = res.data; |
| | | // 妿已æé¨é¨IDï¼èªå¨è®¾ç½®é¨é¨åç§°ï¼ç¨äºéªè¯ï¼ |
| | | if (form.value.approveDeptId && productOptions.value.length > 0) { |
| | | const matchedDept = productOptions.value.find( |
| | | dept => |
| | | dept.deptId == form.value.approveDeptId || |
| | | String(dept.deptId) === String(form.value.approveDeptId) |
| | | ); |
| | | if (matchedDept) { |
| | | form.value.approveDeptName = matchedDept.deptName; |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | // æ¶éææèç¹ç审æ¹äººid |
| | | form.value.approveUserIds = approverNodes.value |
| | | .map(node => node.userId) |
| | | .join(","); |
| | | form.value.approveType = props.approveType; |
| | | // 审æ¹äººå¿
å¡«æ ¡éª |
| | | const hasEmptyApprover = approverNodes.value.some(node => !node.userId); |
| | | if (hasEmptyApprover) { |
| | | proxy.$modal.msgError("请为ææå®¡æ¹èç¹éæ©å®¡æ¹äººï¼"); |
| | | return; |
| | | } |
| | | // å½ approveType 为 2 æ¶ï¼æ ¡éªè¯·åæ¶é´ |
| | | if (props.approveType == 2) { |
| | | if (!form.value.startDate) { |
| | | proxy.$modal.msgError("è¯·éæ©è¯·åå¼å§æ¶é´ï¼"); |
| | | return; |
| | | } |
| | | if (!form.value.endDate) { |
| | | proxy.$modal.msgError("è¯·éæ©è¯·åç»ææ¶é´ï¼"); |
| | | return; |
| | | } |
| | | // æ ¡éªç»ææ¶é´ä¸è½æ©äºå¼å§æ¶é´ |
| | | if (new Date(form.value.endDate) < new Date(form.value.startDate)) { |
| | | proxy.$modal.msgError("请åç»ææ¶é´ä¸è½æ©äºå¼å§æ¶é´ï¼"); |
| | | return; |
| | | } |
| | | } |
| | | // å½ approveType 为 3 æ¶ï¼æ ¡éªåºå·®å°ç¹ |
| | | if (props.approveType == 3) { |
| | | if (!form.value.location || form.value.location.trim() === "") { |
| | | proxy.$modal.msgError("请è¾å
¥åºå·®å°ç¹ï¼"); |
| | | return; |
| | | } |
| | | } |
| | | // å½ approveType 为 4 æ¶ï¼æ ¡éªæ¥ééé¢ |
| | | if (props.approveType == 4) { |
| | | if (!form.value.price || form.value.price <= 0) { |
| | | proxy.$modal.msgError("请è¾å
¥ææçæ¥ééé¢ï¼"); |
| | | return; |
| | | } |
| | | } |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add" || currentApproveStatus.value == 3) { |
| | | approveProcessAdd(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | } else { |
| | | approveProcessUpdate(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | fileList.value = []; |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | | |
| | | // ä¸ä¼ åæ ¡æ£ |
| | | function handleBeforeUpload(file) { |
| | | // æ ¡æ£æä»¶å¤§å° |
| | | // if (file.size > 1024 * 1024 * 10) { |
| | | // proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿10MB!"); |
| | | // return false; |
| | | // } |
| | | proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); |
| | | return true; |
| | | } |
| | | // ä¸ä¼ 失败 |
| | | function handleUploadError(err) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | // ä¸ä¼ æååè° |
| | | function handleUploadSuccess(res, file, uploadFiles) { |
| | | proxy.$modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | // ç¡®ä¿ tempFileIds åå¨ä¸ä¸ºæ°ç» |
| | | if (!form.value.tempFileIds) { |
| | | form.value.tempFileIds = []; |
| | | } |
| | | form.value.tempFileIds.push(res.data.tempId); |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | proxy.$refs.fileUpload.handleRemove(file); |
| | | } |
| | | } |
| | | // ç§»é¤æä»¶ |
| | | function handleRemove(file) { |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | | delLedgerFile(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="dialogVisible" title="éä»¶" width="40%" :before-close="handleClose" draggable> |
| | | <el-table :data="tableData" border height="40vh"> |
| | | <el-table-column label="éä»¶åç§°" prop="name" min-width="400" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="æä½" width="150" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">ä¸è½½</el-button> |
| | | <el-button link type="primary" size="small" @click="lookFile(scope.row)">é¢è§</el-button> |
| | | <el-button link type="danger" size="small" @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-dialog> |
| | | <filePreview ref="filePreviewRef" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | import filePreview from '@/components/filePreview/index.vue' |
| | | import { ElMessageBox, ElMessage } from 'element-plus' |
| | | import { delCommonFile } from '@/api/publicApi/commonFile.js' |
| | | |
| | | const dialogVisible = ref(false) |
| | | const tableData = ref([]) |
| | | const { proxy } = getCurrentInstance(); |
| | | const filePreviewRef = ref() |
| | | const handleClose = () => { |
| | | dialogVisible.value = false |
| | | } |
| | | const open = (list) => { |
| | | dialogVisible.value = true |
| | | tableData.value = list |
| | | } |
| | | const downLoadFile = (row) => { |
| | | proxy.$download.name(row.url); |
| | | |
| | | } |
| | | const lookFile = (row) => { |
| | | filePreviewRef.value.open(row.url) |
| | | } |
| | | // å é¤éä»¶ |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm(`确认å é¤éä»¶"${row.name}"åï¼`, 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | delCommonFile([row.id]).then(() => { |
| | | ElMessage.success('å 餿å') |
| | | // ä»å表ä¸ç§»é¤å·²å é¤çéä»¶ |
| | | const index = tableData.value.findIndex(item => item.id === row.id) |
| | | if (index !== -1) { |
| | | tableData.value.splice(index, 1) |
| | | } |
| | | }).catch(() => { |
| | | ElMessage.error('å é¤å¤±è´¥') |
| | | }) |
| | | }).catch(() => { |
| | | ElMessage.info('已忶å é¤') |
| | | }) |
| | | } |
| | | defineExpose({ |
| | | open |
| | | }) |
| | | </script> |
| | | |
| | | <style></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æ ç¾é¡µåæ¢ä¸åç审æ¹ç±»å --> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">æµç¨ç¼å·ï¼</span> |
| | | <el-input v-model="searchForm.approveId" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥æµç¨ç¼å·æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title ml10">审æ¹ç¶æï¼</span> |
| | | <el-select v-model="searchForm.approveStatus" |
| | | clearable |
| | | @change="handleQuery" |
| | | style="width: 240px"> |
| | | <el-option label="å¾
å®¡æ ¸" |
| | | :value="0" /> |
| | | <el-option label="å®¡æ ¸ä¸" |
| | | :value="1" /> |
| | | <el-option label="å®¡æ ¸å®æ" |
| | | :value="2" /> |
| | | <el-option label="å®¡æ ¸æªéè¿" |
| | | :value="3" /> |
| | | <el-option label="已鿰æäº¤" |
| | | :value="4" /> |
| | | </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 @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumnCopy" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <info-form-dia ref="infoFormDia" |
| | | @close="handleQuery" |
| | | :approveType="currentApproveType"></info-form-dia> |
| | | <approval-dia ref="approvalDia" |
| | | @close="handleQuery" |
| | | :approveType="currentApproveType"></approval-dia> |
| | | <FileList ref="fileListRef" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import FileList from "./fileList.vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | computed, |
| | | reactive, |
| | | toRefs, |
| | | nextTick, |
| | | getCurrentInstance, |
| | | } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { useRoute } from "vue-router"; |
| | | import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue"; |
| | | import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue"; |
| | | import { |
| | | approveProcessDelete, |
| | | approveProcessListPage, |
| | | } from "@/api/collaborativeApproval/approvalProcess.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const route = useRoute(); |
| | | |
| | | // å½å审æ¹ç±»åï¼æ ¹æ®éä¸çæ ç¾é¡µè®¡ç® |
| | | const currentApproveType = computed(() => { |
| | | return Number(8); |
| | | }); |
| | | |
| | | // æ ç¾é¡µåæ¢å¤ç |
| | | const handleTabChange = tabName => { |
| | | // 忢æ ç¾é¡µæ¶éç½®æç´¢æ¡ä»¶åå页ï¼å¹¶éæ°å è½½æ°æ® |
| | | searchForm.value.approveId = ""; |
| | | searchForm.value.approveStatus = ""; |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | approveId: "", |
| | | approveStatus: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // å¨æè¡¨æ ¼åé
ç½®ï¼æ ¹æ®å®¡æ¹ç±»åçæå |
| | | const tableColumnCopy = computed(() => { |
| | | // åºç¡åé
ç½® |
| | | const baseColumns = [ |
| | | { |
| | | label: "审æ¹ç¶æ", |
| | | prop: "approveStatus", |
| | | dataType: "tag", |
| | | width: 100, |
| | | formatData: params => { |
| | | if (params == 0) { |
| | | return "å¾
å®¡æ ¸"; |
| | | } else if (params == 1) { |
| | | return "å®¡æ ¸ä¸"; |
| | | } else if (params == 2) { |
| | | return "å®¡æ ¸å®æ"; |
| | | } else if (params == 4) { |
| | | return "已鿰æäº¤"; |
| | | } else { |
| | | return "ä¸éè¿"; |
| | | } |
| | | }, |
| | | formatType: params => { |
| | | if (params == 0) { |
| | | return "warning"; |
| | | } else if (params == 1) { |
| | | return "primary"; |
| | | } else if (params == 2) { |
| | | return "success"; |
| | | } else if (params == 4) { |
| | | return "info"; |
| | | } else { |
| | | return "danger"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "æµç¨ç¼å·", |
| | | prop: "approveId", |
| | | width: 170, |
| | | }, |
| | | { |
| | | label: "ç³è¯·é¨é¨", |
| | | prop: "approveDeptName", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "审æ¹äºç±", |
| | | prop: "approveReason", |
| | | width: 200, |
| | | }, |
| | | { |
| | | label: "ç³è¯·äºº", |
| | | prop: "approveUserName", |
| | | width: 120, |
| | | }, |
| | | ]; |
| | | |
| | | // æ¥æåï¼æ ¹æ®ç±»å卿é
ç½®ï¼ |
| | | baseColumns.push( |
| | | { |
| | | label: "ç³è¯·æ¥æ", |
| | | prop: "approveTime", |
| | | width: 200, |
| | | }, |
| | | { |
| | | label: "ç»ææ¥æ", |
| | | prop: "approveOverTime", |
| | | width: 120, |
| | | } |
| | | ); |
| | | |
| | | // å½å审æ¹äººå |
| | | baseColumns.push({ |
| | | label: "å½å审æ¹äºº", |
| | | prop: "approveUserCurrentName", |
| | | width: 120, |
| | | }); |
| | | |
| | | // æä½å |
| | | baseColumns.push({ |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 230, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | disabled: row => |
| | | row.approveStatus == 2 || |
| | | row.approveStatus == 1 || |
| | | row.approveStatus == 4, |
| | | }, |
| | | { |
| | | name: "å®¡æ ¸", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openApprovalDia("approval", row); |
| | | }, |
| | | disabled: row => |
| | | row.approveUserCurrentId == null || |
| | | row.approveStatus == 2 || |
| | | row.approveStatus == 3 || |
| | | row.approveStatus == 4 || |
| | | row.approveUserCurrentId !== userStore.id, |
| | | }, |
| | | { |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openApprovalDia("view", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "éä»¶", |
| | | type: "text", |
| | | clickFun: row => { |
| | | downLoadFile(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }); |
| | | |
| | | return baseColumns; |
| | | }); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const infoFormDia = ref(); |
| | | const approvalDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const fileListRef = ref(null); |
| | | const downLoadFile = row => { |
| | | fileListRef.value.open(row.commonFileList); |
| | | }; |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | approveProcessListPage({ |
| | | ...page, |
| | | ...searchForm.value, |
| | | approveType: currentApproveType.value, |
| | | }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | const type = currentApproveType.value; |
| | | const urlMap = { |
| | | 0: "/approveProcess/exportZero", |
| | | 1: "/approveProcess/exportOne", |
| | | 2: "/approveProcess/exportTwo", |
| | | 3: "/approveProcess/exportThree", |
| | | 4: "/approveProcess/exportFour", |
| | | 5: "/approveProcess/exportFive", |
| | | 6: "/approveProcess/exportSix", |
| | | 7: "/approveProcess/exportSeven", |
| | | 8: "/approveProcess/exportEight", |
| | | }; |
| | | const url = urlMap[type] || urlMap[0]; |
| | | const nameMap = { |
| | | 0: "åå审æ¹ç®¡ç表", |
| | | 1: "å
¬åºç®¡ç审æ¹è¡¨", |
| | | 2: "请å管ç审æ¹è¡¨", |
| | | 3: "åºå·®ç®¡ç审æ¹è¡¨", |
| | | 4: "æ¥é管ç审æ¹è¡¨", |
| | | 5: "éè´ç³è¯·å®¡æ¹è¡¨", |
| | | 6: "æ¥ä»·å®¡æ¹è¡¨", |
| | | 7: "å货审æ¹è¡¨", |
| | | 8: "å±é©ä½ä¸å®¡æ¹è¡¨", |
| | | }; |
| | | const fileName = nameMap[type] || nameMap[0]; |
| | | proxy.download(url, {}, `${fileName}.xlsx`); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æå¼æ°å¢ãç¼è¾å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | infoFormDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | // æå¼æ°å¢æ£éªå¼¹æ¡ |
| | | const openApprovalDia = (type, row) => { |
| | | nextTick(() => { |
| | | approvalDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.approveId); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | approveProcessDelete(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | const approveId = route.query.approveId; |
| | | |
| | | if (approveId) { |
| | | // 设置æµç¨ç¼å·æ¥è¯¢æ¡ä»¶ |
| | | searchForm.value.approveId = String(approveId); |
| | | } |
| | | |
| | | // æ¥è¯¢å表 |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .approval-tabs { |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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)" |
| | | @click="openForm('edit', scope.row)">ç¼è¾</el-button> |
| | | :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> |
| | | |
| | |
| | | import { getToken } from "@/utils/auth"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | import { |
| | | deliveryLedgerListPage, |
| | | addOrUpdateDeliveryLedger, |
| | | delDeliveryLedger, |
| | | deliveryLedgerListPage, |
| | | addOrUpdateDeliveryLedger, |
| | | delDeliveryLedger, deductStock, |
| | | } from "@/api/salesManagement/deliveryLedger.js"; |
| | | import { delLedgerFile } from "@/api/salesManagement/salesLedger.js"; |
| | | |
| | |
| | | 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 = []; |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | |
| | | expressNumber: form.value.type === "å¿«é" ? form.value.expressNumber : "", |
| | | tempFileIds: tempFileIds, |
| | | }; |
| | | addOrUpdateDeliveryLedger(payload).then((res) => { |
| | | deductStock(payload).then((res) => { |
| | | proxy.$modal.msgSuccess("æä½æå"); |
| | | closeDia(); |
| | | getList(); |
| | |
| | | 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) |
| | |
| | | proxy.$modal.msgError("è¯·éæ©è³å°ä¸æ¡æ°æ®"); |
| | | return; |
| | | } |
| | | const validRows = selectedRows.value.filter((item) => item.noReceiptAmount !== 0); |
| | | // ä»
å
许âå¾
忬¾éé¢ > 0âçè®°å½è¿å
¥æ°å¢å款弹çªï¼å¹¶è¿æ»¤æå¯è½æ··å
¥ç空对象 |
| | | const validRows = selectedRows.value.filter((item) => { |
| | | if (!item || !item.id) return false; |
| | | return Number(item.pendingInvoiceTotal ?? 0) > 0; |
| | | }); |
| | | if (validRows.length === 0) { |
| | | proxy.$modal.msgWarning("æéè®°å½åæ é忬¾"); |
| | | return; |
| | |
| | | const closeDia = () => { |
| | | forms.value = []; |
| | | dialogFormVisible.value = false; |
| | | // é¿å
äºæ¬¡æå¼å¼¹çªæ¶ä»æºå¸¦ä¸ä¸æ¬¡ç鿩坼è´âå¤åºä¸è¡/èæ°æ®â |
| | | selectedRows.value = []; |
| | | }; |
| | | |
| | | // å é¤åæ¬¾è®°å½ |
| | |
| | | </div> |
| | | </div> |
| | | <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%" |
| | | :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%" |
| | | :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" type="selection" width="55" fixed="left"/> |
| | | <el-table-column type="expand" width="60" fixed="left"> |
| | |
| | | <el-table-column label="å½å
¥äºº" prop="entryPersonName" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="å½å
¥æ¥æ" prop="entryDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="äº¤ä»æ¥æ" prop="deliveryDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="æä½" min-width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">ç¼è¾</el-button> |
| | |
| | | <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢éå®å°è´¦é¡µé¢' : 'ç¼è¾éå®å°è´¦é¡µé¢'" :width="'70%'" |
| | | :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <!-- æ¥ä»·å导å
¥å
¥å£ï¼æ¾å¨è¡¨åé¡¶é¨ï¼éæ©å忾客æ·/ä¸å¡åç --> |
| | | <el-row v-if="operationType === 'add'" style="margin-bottom: 10px;"> |
| | | <el-col :span="24" style="text-align: right;"> |
| | | <el-button type="primary" plain @click="openQuotationDialog"> |
| | | ä»é宿¥ä»·å¯¼å
¥ |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" prop="salesContractNo"> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äº¤è´§æ¥æï¼" prop="entryDate"> |
| | | <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" |
| | | type="date" placeholder="è¯·éæ©" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-form-item label="产åä¿¡æ¯ï¼" prop="entryDate"> |
| | | <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">æ·»å </el-button> |
| | |
| | | customerId: "", |
| | | entryPerson: "", |
| | | entryDate: "", |
| | | deliveryDate: "", |
| | | maintenanceTime: "", |
| | | productData: [], |
| | | executionDate: "", |
| | |
| | | customerId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | entryPerson: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | entryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | deliveryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | executionDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | |
| | | // æ·»å 表è¡ç±»åæ¹æ³ |
| | | const tableRowClassName = ({ row }) => { |
| | | 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'; |
| | | } |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | |
| | | selectedQuotation.value = row; |
| | | |
| | | // ä¸å¡å |
| | | form.value.salesman = row.salesperson || ""; |
| | | form.value.salesman = (row.salesperson || "").trim(); |
| | | |
| | | // 客æ·åç§° -> customerId |
| | | const customer = (customerOption.value || []).find((c) => c.customerName === row.customer); |
| | | const qCustomerName = String(row.customer || "").trim(); |
| | | const customer = (customerOption.value || []).find((c) => { |
| | | const name = String(c.customerName || "").trim(); |
| | | return name === qCustomerName || name.includes(qCustomerName) || qCustomerName.includes(name); |
| | | }); |
| | | if (customer?.id) { |
| | | form.value.customerId = customer.id; |
| | | } else { |
| | |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | ::v-deep .yellow { |
| | | background-color: #FAF0DE; |
| | | } |
| | | |
| | | ::v-deep .pink { |
| | | background-color: #FAE1DE; |
| | | } |
| | | |
| | | ::v-deep .red { |
| | | background-color: #f80202; |
| | | } |
| | | |
| | | ::v-deep .purple{ |
| | | background-color: #F4DEFA; |
| | | } |
| | | |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | |
| | | height="calc(100vh - 22em)" |
| | | > |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column prop="quotationNo" label="æ¥ä»·åå·" width="150" /> |
| | | <el-table-column prop="quotationNo" label="æ¥ä»·åå·" /> |
| | | <el-table-column prop="customer" label="客æ·åç§°" /> |
| | | <el-table-column prop="salesperson" label="ä¸å¡å" width="100" /> |
| | | <el-table-column prop="quotationDate" label="æ¥ä»·æ¥æ" width="120" /> |
| | |
| | | @change="getProductModel($event, scope.row)" |
| | | > |
| | | <el-option |
| | | v-for="item in modelOptions" |
| | | v-for="item in scope.row.modelOptions || []" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" |
| | |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div style="margin-top: 20px;"> |
| | | <div style="margin: 20px 0;"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="currentQuotation.products" border style="width: 100%"> |
| | | <el-table-column prop="product" label="产ååç§°" /> |
| | |
| | | if (!value) { |
| | | row.productId = ''; |
| | | row.product = ''; |
| | | modelOptions.value = []; |
| | | row.modelOptions = []; |
| | | row.specificationId = ''; |
| | | row.specification = ''; |
| | | row.unit = ''; |
| | |
| | | if (label) { |
| | | row.product = label; |
| | | } |
| | | // è·åè§æ ¼åå·å表 |
| | | // è·åè§æ ¼åå·å表ï¼è®¾ç½®å°å½åè¡ç modelOptions |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res || []; |
| | | row.modelOptions = res || []; |
| | | }); |
| | | }; |
| | | const getProductModel = (value, row) => { |
| | |
| | | } |
| | | // æ´æ° specificationIdï¼v-model å·²ç»èªå¨æ´æ°ï¼è¿éç¡®ä¿ä¸è´æ§ï¼ |
| | | row.specificationId = value; |
| | | const index = modelOptions.value.findIndex((item) => item.id === value); |
| | | const modelOptions = row.modelOptions || []; |
| | | const index = modelOptions.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | row.specification = modelOptions.value[index].model; |
| | | row.unit = modelOptions.value[index].unit; |
| | | row.specification = modelOptions[index].model; |
| | | row.unit = modelOptions[index].unit; |
| | | } else { |
| | | row.specification = ''; |
| | | row.unit = ''; |
| | |
| | | form.paymentMethod = row.paymentMethod || '' |
| | | form.status = row.status || 'è稿' |
| | | form.remark = row.remark || '' |
| | | form.products = row.products ? row.products.map(product => { |
| | | form.products = row.products ? await Promise.all(row.products.map(async (product) => { |
| | | const productName = product.product || product.productName || '' |
| | | // ä¼å
ç¨ productIdï¼å¦æåªæåç§°ï¼å°è¯åæ¥ id 以便æ éæ©å¨åæ¾ |
| | | const resolvedId = product.productId |
| | | const resolvedProductId = product.productId |
| | | ? Number(product.productId) |
| | | : findNodeIdByLabel(productOptions.value, productName) || '' |
| | | |
| | | // 妿æäº§åIDï¼å 载对åºçè§æ ¼åå·å表 |
| | | let modelOptions = []; |
| | | let resolvedSpecificationId = product.specificationId || ''; |
| | | |
| | | if (resolvedProductId) { |
| | | try { |
| | | const res = await modelList({ id: resolvedProductId }); |
| | | modelOptions = res || []; |
| | | |
| | | // 妿è¿åçæ°æ®æ²¡æ specificationIdï¼ä½æ specification åç§°ï¼æ ¹æ®åç§°æ¥æ¾ ID |
| | | if (!resolvedSpecificationId && product.specification) { |
| | | const foundModel = modelOptions.find(item => item.model === product.specification); |
| | | if (foundModel) { |
| | | resolvedSpecificationId = foundModel.id; |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error('å è½½è§æ ¼åå·å¤±è´¥:', error); |
| | | } |
| | | } |
| | | |
| | | return { |
| | | productId: resolvedId, |
| | | productId: resolvedProductId, |
| | | product: productName, |
| | | specificationId: product.specificationId || '', |
| | | specificationId: resolvedSpecificationId, |
| | | specification: product.specification || '', |
| | | quantity: product.quantity || 0, |
| | | unit: product.unit || '', |
| | | unitPrice: product.unitPrice || 0, |
| | | amount: product.amount || 0 |
| | | amount: product.amount || 0, |
| | | modelOptions: modelOptions // 为æ¯è¡æ·»å ç¬ç«çè§æ ¼åå·å表 |
| | | } |
| | | }) : [] |
| | | })) : [] |
| | | form.subtotal = row.subtotal || 0 |
| | | form.freight = row.freight || 0 |
| | | form.otherFee = row.otherFee || 0 |
| | |
| | | quantity: 1, |
| | | unit: '', |
| | | unitPrice: 0, |
| | | amount: 0 |
| | | amount: 0, |
| | | modelOptions: [] // 为æ¯è¡æ·»å ç¬ç«çè§æ ¼åå·å表 |
| | | }) |
| | | } |
| | | |