¶Ô±ÈÐÂÎļþ |
| | |
| | | // ååå®¡æ¹ |
| | | import request from "@/utils/request"; |
| | | |
| | | export function approveProcessListPage(query) { |
| | | return request({ |
| | | url: '/approveProcess/list', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | export function getDept(query) { |
| | | return request({ |
| | | url: '/approveProcess/getDept', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | export function approveProcessGetInfo(query) { |
| | | return request({ |
| | | url: '/approveProcess/get', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | // æ°å¢å®¡æ¹æµç¨ |
| | | export function approveProcessAdd(query) { |
| | | return request({ |
| | | url: '/approveProcess/add', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | } |
| | | // ä¿®æ¹å®¡æ¹æµç¨ |
| | | export function approveProcessUpdate(query) { |
| | | return request({ |
| | | url: '/approveProcess/update', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | } |
| | | // æäº¤å®¡æ¹ |
| | | export function updateApproveNode(query) { |
| | | return request({ |
| | | url: '/approveNode/updateApproveNode', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | } |
| | | // å é¤å®¡æ¹æµç¨ |
| | | export function approveProcessDelete(query) { |
| | | return request({ |
| | | url: '/approveProcess/deleteIds', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | } |
| | | // æ¥è¯¢å®¡æ¹æµç¨ |
| | | export function approveProcessDetails(query) { |
| | | return request({ |
| | | url: '/approveNode/details/' + query, |
| | | method: 'get', |
| | | }) |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢å
¬åå表 |
| | | export function listNotice(query) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢å
¬åè¯¦ç» |
| | | export function getNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/' + noticeId, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢å
Œ |
| | | export function addNotice(data) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹å
Œ |
| | | export function updateNotice(data) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // å é¤å
Œ |
| | | export function delNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/' + noticeId, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | // æ¹éå é¤å
Œ |
| | | export function delNoticeBatch(noticeIds) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/batch', |
| | | method: 'delete', |
| | | data: noticeIds |
| | | }) |
| | | } |
| | | |
| | | // åå¸å
Œ |
| | | export function publishNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/publish/' + noticeId, |
| | | method: 'put' |
| | | }) |
| | | } |
| | | |
| | | // ä¸çº¿å
Œ |
| | | export function offlineNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/offline/' + noticeId, |
| | | method: 'put' |
| | | }) |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢RPAå表 |
| | | export function listRpa(query) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ¥è¯¢RPAè¯¦ç» |
| | | export function getRpa(rpaId) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa/" + rpaId, |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢RPA |
| | | export function addRpa(data) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹RPA |
| | | export function updateRpa(data) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤RPA |
| | | export function delRpa(rpaId) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa/" + rpaId, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | // æ¹éå é¤RPA |
| | | export function delRpaBatch(rpaIds) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa/batch", |
| | | method: "delete", |
| | | data: rpaIds, |
| | | }); |
| | | } |
| | | |
| | | // å¯å¨RPA |
| | | export function startRpa(rpaId) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa/start/" + rpaId, |
| | | method: "post", |
| | | }); |
| | | } |
| | | |
| | | // 忢RPA |
| | | export function stopRpa(rpaId) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa/stop/" + rpaId, |
| | | method: "post", |
| | | }); |
| | | } |
| | | |
| | | // è·åRPAç¶æ |
| | | export function getRpaStatus(rpaId) { |
| | | return request({ |
| | | url: "/collaborativeApproval/rpa/status/" + rpaId, |
| | | method: "get", |
| | | }); |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | // æ£å®æ ¡åè®°å½ |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function ledgerRecordListPage(query) { |
| | | return request({ |
| | | url: "/measuringInstrumentLedgerRecord/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ ¡å |
| | | export function ledgerRecordVerifying(query) { |
| | | return request({ |
| | | url: "/measuringInstrumentLedger/verifying", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¿®æ¹æ ¡å |
| | | export function ledgerRecordUpdate(query) { |
| | | return request({ |
| | | url: "/measuringInstrumentLedgerRecord/update", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | export const getLedgerPage = (params) => { |
| | | return request({ |
| | | url: "/device/ledger/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | export const getLedgerById = (id) => { |
| | | return request({ |
| | | url: `/device/ledger/${id}`, |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | export const addLedger = (data) => { |
| | | return request({ |
| | | url: "/device/ledger", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| | | export const editLedger = (data) => { |
| | | return request({ |
| | | url: "/device/ledger", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | export const delLedger = (id) => { |
| | | return request({ |
| | | url: `/device/ledger/${id}`, |
| | | method: "delete", |
| | | }); |
| | | }; |
| | | |
| | | export const getDeviceLedger = () => { |
| | | return request({ |
| | | url: "/device/ledger/getDeviceLedger", |
| | | method: "get", |
| | | }); |
| | | }; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | // 计éå¨å
·å°è´¦ |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function measuringInstrumentListPage(query) { |
| | | return request({ |
| | | url: "/measuringInstrumentLedger/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // å é¤ |
| | | export function measuringInstrumentDelete(query) { |
| | | return request({ |
| | | url: "/measuringInstrumentLedger/delete", |
| | | method: "delete", |
| | | data: query, |
| | | }); |
| | | } |
| | | // æ°å¢ |
| | | export function measuringInstrumentAdd(query) { |
| | | return request({ |
| | | url: "/measuringInstrumentLedger/add", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¿®æ¹ |
| | | export function measuringInstrumentUpdate(query) { |
| | | return request({ |
| | | url: "/measuringInstrumentLedger/update", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | /** |
| | | * @desc è®¾å¤æ¥ä¿®å表 |
| | | * @param {å页æ¥è¯¢} params |
| | | * @returns |
| | | */ |
| | | export const getRepairPage = (params) => { |
| | | return request({ |
| | | url: "/device/repair/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc æ°å¢æ¥ä¿® |
| | | * @param {æ¥ä¿®åæ°} data |
| | | * @returns |
| | | */ |
| | | export const addRepair = (data) => { |
| | | return request({ |
| | | url: "/device/repair", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc ç¼è¾æ¥ä¿® |
| | | * @param {æ¥ä¿®åæ°} data |
| | | * @returns |
| | | */ |
| | | export const editRepair = (data) => { |
| | | return request({ |
| | | url: "/device/repair", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc æ ¹æ®idæ¥è¯¢ä¸æ¡æ¥ä¿® |
| | | * @param {æ¥ä¿®id} id |
| | | * @returns |
| | | */ |
| | | export const getRepairById = (id) => { |
| | | return request({ |
| | | url: `/device/repair/${id}`, |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc å 餿¥ä¿® |
| | | * @param {ç¼å·} ids |
| | | * @returns |
| | | */ |
| | | export const delRepair = (ids) => { |
| | | return request({ |
| | | url: `/device/repair/${ids}`, |
| | | method: "delete", |
| | | }); |
| | | }; |
| | | |
| | | export const addMaintain = (data) => { |
| | | return request({ |
| | | url: `/device/repair/repair`, |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | /** |
| | | * @desc 设å¤ä¿å
»å表å页æ¥è¯¢ |
| | | * @param {å页æ¥è¯¢å
¥å} params |
| | | * @returns |
| | | */ |
| | | export const getUpkeepPage = (params) => { |
| | | return request({ |
| | | url: "/device/maintenance/page", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc 设å¤ä¿å
»è¯¦æ
|
| | | * @param {ä¿å
»ä½ç¼å·} id |
| | | * @returns |
| | | */ |
| | | export const getUpkeepById = (id) => { |
| | | return request({ |
| | | url: `/device/maintenance/${id}`, |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc 设å¤ä¿å
»æ°å¢ |
| | | * @param {æ°å¢ä¿å
»è¡¨å} data |
| | | * @returns |
| | | */ |
| | | export const addUpkeep = (data) => { |
| | | return request({ |
| | | url: "/device/maintenance", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc 设å¤ä¿å
»ç¼è¾ |
| | | * @param {ç¼è¾ä¿å
»è¡¨å} data |
| | | * @returns |
| | | */ |
| | | export const editUpkeep = (data) => { |
| | | return request({ |
| | | url: "/device/maintenance", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc æ°å¢ä¿å
»è¡¨å |
| | | * @param {æ°å¢ä¿å
»è¡¨å} data |
| | | * @returns |
| | | */ |
| | | export const addMaintenance = (data) => { |
| | | return request({ |
| | | url: "/device/maintenance/maintenance", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | export const delUpkeep = (id) => { |
| | | return request({ |
| | | url: `/device/maintenance/${id}`, |
| | | method: "delete", |
| | | }); |
| | | }; |
| | |
| | | url: '/system/user/userListNoPage', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | // æ¥è¯¢ç¨æ·å表 |
| | | export function userListNoPageByTenantId() { |
| | | return request({ |
| | | url: '/system/user/userListNoPageByTenantId', |
| | | method: 'get' |
| | | }) |
| | | } |
| | |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/cooperativeOffice/collaborativeApproval/approve", |
| | | "style": { |
| | | "navigationBarTitleText": "å®¡æ ¸", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/cooperativeOffice/collaborativeApproval/contactSelect", |
| | | "style": { |
| | | "navigationBarTitleText": "éæ©è系人", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/cooperativeOffice/clientVisit/index", |
| | | "style": { |
| | | "navigationBarTitleText": "å®¢æ·æè®¿", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/ledger/index", |
| | | "style": { |
| | | "navigationBarTitleText": "设å¤å°è´¦", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/ledger/detail", |
| | | "style": { |
| | | "navigationBarTitleText": "设å¤å°è´¦è¯¦æ
", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/repair/index", |
| | | "style": { |
| | | "navigationBarTitleText": "è®¾å¤æ¥ä¿®", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/repair/add", |
| | | "style": { |
| | | "navigationBarTitleText": "æ°å¢è®¾å¤æ¥ä¿®", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/repair/maintain", |
| | | "style": { |
| | | "navigationBarTitleText": "设å¤ç»´ä¿®", |
| | | "navigationStyle": "custom" |
| | | } |
| | | } |
| | | { |
| | | "path": "pages/equipmentManagement/upkeep/index", |
| | | "style": { |
| | | "navigationBarTitleText": "设å¤ä¿å
»", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/upkeep/add", |
| | | "style": { |
| | | "navigationBarTitleText": "æ°å¢ä¿å
»è®¡å", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/upkeep/maintain", |
| | | "style": { |
| | | "navigationBarTitleText": "ç»´ä¿®ä¿å
»", |
| | | "navigationStyle": "custom" |
| | | } |
| | | } |
| | | ], |
| | | "subPackages": [ |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="approve-page"> |
| | | |
| | | <PageHeader title="å®¡æ ¸" @back="goBack" /> |
| | | |
| | | <!-- ç³è¯·ä¿¡æ¯ --> |
| | | <view class="application-info"> |
| | | <view class="info-header"> |
| | | <text class="info-title">ç³è¯·ä¿¡æ¯</text> |
| | | </view> |
| | | <view class="info-content"> |
| | | <view class="info-row"> |
| | | <text class="info-label">ç³è¯·äºº</text> |
| | | <text class="info-value">{{ approvalData.approveUserName }}</text> |
| | | </view> |
| | | <view class="info-row"> |
| | | <text class="info-label">ç³è¯·é¨é¨</text> |
| | | <text class="info-value">{{ approvalData.approveDeptName }}</text> |
| | | </view> |
| | | <view class="info-row"> |
| | | <text class="info-label">ç³è¯·äºç±</text> |
| | | <text class="info-value">{{ approvalData.approveReason }}</text> |
| | | </view> |
| | | <view class="info-row"> |
| | | <text class="info-label">ç³è¯·æ¥æ</text> |
| | | <text class="info-value">{{ approvalData.approveTime }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å®¡æ¹æµç¨ --> |
| | | <view class="approval-process"> |
| | | <view class="process-header"> |
| | | <text class="process-title">å®¡æ¹æµç¨</text> |
| | | </view> |
| | | |
| | | <view class="process-steps"> |
| | | <view |
| | | v-for="(step, index) in approvalSteps" |
| | | :key="index" |
| | | class="process-step" |
| | | :class="{ |
| | | 'completed': step.status === 'completed', |
| | | 'current': step.status === 'current', |
| | | 'pending': step.status === 'pending', |
| | | 'rejected': step.status === 'rejected' |
| | | }" |
| | | > |
| | | <view class="step-indicator"> |
| | | <view class="step-dot"> |
| | | <text v-if="step.status === 'completed'" class="step-icon">â</text> |
| | | <text v-else-if="step.status === 'rejected'" class="step-icon">â</text> |
| | | <text v-else class="step-number">{{ index + 1 }}</text> |
| | | </view> |
| | | <view v-if="index < approvalSteps.length - 1" class="step-line"></view> |
| | | </view> |
| | | |
| | | <view class="step-content"> |
| | | <view class="step-info"> |
| | | <text class="step-title">{{ step.title }}</text> |
| | | <text class="step-approver">{{ step.approverName }}</text> |
| | | <text v-if="step.approveTime" class="step-time">{{ step.approveTime }}</text> |
| | | </view> |
| | | |
| | | <view v-if="step.opinion" class="step-opinion"> |
| | | <text class="opinion-label">å®¡æ¹æè§ï¼</text> |
| | | <text class="opinion-content">{{ step.opinion }}</text> |
| | | </view> |
| | | <!-- ç¾åå±ç¤º --> |
| | | <view v-if="step.urlTem" class="step-opinion" style="margin-top:8px;"> |
| | | <text class="opinion-label">ç¾åï¼</text> |
| | | <image :src="step.urlTem" mode="widthFix" style="width:180px;border-radius:6px;border:1px solid #eee;" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å®¡æ ¸æè§è¾å
¥ --> |
| | | <view v-if="canApprove" class="approval-input"> |
| | | <view class="input-header"> |
| | | <text class="input-title">å®¡æ ¸æè§</text> |
| | | </view> |
| | | |
| | | <view class="input-content"> |
| | | <van-field |
| | | v-model="approvalOpinion" |
| | | type="textarea" |
| | | rows="4" |
| | | placeholder="请è¾å
¥å®¡æ ¸æè§" |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åºé¨æä½æé® --> |
| | | <view v-if="canApprove" class="footer-actions"> |
| | | <van-button class="reject-btn" @click="handleReject">驳å</van-button> |
| | | <van-button class="approve-btn" @click="handleApprove">éè¿</van-button> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, computed } from 'vue' |
| | | import { approveProcessGetInfo, approveProcessDetails, updateApproveNode } from '@/api/collaborativeApproval/approvalProcess' |
| | | import useUserStore from '@/store/modules/user' |
| | | import { showToast } from 'vant' |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | const userStore = useUserStore() |
| | | const approvalData = ref({}) |
| | | const approvalSteps = ref([]) |
| | | const approvalOpinion = ref('') |
| | | const approveId = ref('') |
| | | |
| | | // ä»è¯¦æ
æ¥å£åæ®µå¯¹é½ canApproveï¼ä»
彿 isShen çèç¹æ¶å¯å®¡æ¹ |
| | | const canApprove = computed(() => { |
| | | return approvalSteps.value.some(step => step.isShen === true) |
| | | }) |
| | | |
| | | onMounted(() => { |
| | | const pages = getCurrentPages() |
| | | const currentPage = pages[pages.length - 1] |
| | | approveId.value = currentPage.options.approveId |
| | | if (approveId.value) { |
| | | loadApprovalData() |
| | | } |
| | | }) |
| | | |
| | | const loadApprovalData = () => { |
| | | // åºæ¬ç³è¯·ä¿¡æ¯ |
| | | approveProcessGetInfo({ id: approveId.value }).then(res => { |
| | | approvalData.value = res.data || {} |
| | | }) |
| | | // 审æ¹èç¹è¯¦æ
|
| | | approveProcessDetails(approveId.value).then(res => { |
| | | const list = Array.isArray(res.data) ? res.data : [] |
| | | // ä¿ååå§èç¹æ°æ®ä¾æäº¤ä½¿ç¨ |
| | | activities.value = list |
| | | |
| | | approvalSteps.value = list.map((it, idx) => { |
| | | // èç¹ç¶ææ å°ï¼1=éè¿ï¼2=ä¸éè¿ï¼å¦åçæ¯å¦å½å(isShen)ï¼åé»è®¤ä¸ºå¾
å¤ç |
| | | let status = 'pending' |
| | | if (it.approveNodeStatus === 1) status = 'completed' |
| | | else if (it.approveNodeStatus === 2) status = 'rejected' |
| | | else if (it.isShen) status = 'current' |
| | | return { |
| | | title: `第${idx + 1}æ¥å®¡æ¹`, |
| | | approverName: it.approveNodeUser || 'æªç¥ç¨æ·', |
| | | status, |
| | | approveTime: it.approveTime || null, |
| | | opinion: it.approveNodeReason || '', |
| | | urlTem: it.urlTem || '', |
| | | isShen: !!it.isShen |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | const submitForm = (status) => { |
| | | // å¯éï¼æ ¡éªå®¡æ ¸æè§ |
| | | if (!approvalOpinion.value?.trim()) { |
| | | showToast('请è¾å
¥å®¡æ ¸æè§') |
| | | return |
| | | } |
| | | // æ¾å°å½åå¯å®¡æ¹èç¹ |
| | | const filteredActivities = activities.value.filter(activity => activity.isShen) |
| | | if (!filteredActivities.length) { |
| | | showToast('å½åæ å¯å®¡æ¹èç¹') |
| | | return |
| | | } |
| | | // åå
¥ç¶æåæè§ |
| | | filteredActivities[0].approveNodeStatus = status |
| | | filteredActivities[0].approveNodeReason = approvalOpinion.value || '' |
| | | // è®¡ç®æ¯å¦ä¸ºæå䏿¥ |
| | | const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length - 1 |
| | | // è°ç¨å端 |
| | | updateApproveNode({ ...filteredActivities[0], isLast }).then(() => { |
| | | const msg = status === 1 ? '审æ¹éè¿' : '审æ¹å·²é©³å' |
| | | showToast(msg) |
| | | // æç¤ºåè¿åä¸ä¸ä¸ªé¡µé¢ |
| | | setTimeout(() => { |
| | | goBack() // å
鍿¯ uni.navigateBack() |
| | | }, 800) |
| | | }) |
| | | } |
| | | |
| | | const handleApprove = () => { |
| | | uni.showModal({ |
| | | title: '确认æä½', |
| | | content: 'ç¡®å®è¦éè¿æ¤å®¡æ¹åï¼', |
| | | success: (res) => { |
| | | if (res.confirm) submitForm(1) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const handleReject = () => { |
| | | uni.showModal({ |
| | | title: '确认æä½', |
| | | content: 'ç¡®å®è¦é©³åæ¤å®¡æ¹åï¼', |
| | | success: (res) => { |
| | | if (res.confirm) submitForm(2) |
| | | } |
| | | }) |
| | | } |
| | | // åå§èç¹æ°æ®ï¼ç¨äºæäº¤é»è¾ï¼ |
| | | const activities = ref([]) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .approve-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | .header { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #fff; |
| | | padding: 16px 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 100; |
| | | } |
| | | |
| | | .title { |
| | | flex: 1; |
| | | text-align: center; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .application-info { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .info-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .info-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .info-content { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .info-row { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | width: 80px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | flex: 1; |
| | | } |
| | | |
| | | .approval-process { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .process-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .process-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .process-steps { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .process-step { |
| | | display: flex; |
| | | position: relative; |
| | | margin-bottom: 24px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | |
| | | .step-line { |
| | | display: none; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .step-indicator { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | margin-right: 16px; |
| | | } |
| | | |
| | | .step-dot { |
| | | width: 32px; |
| | | height: 32px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | position: relative; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .process-step.completed .step-dot { |
| | | background: #52c41a; |
| | | color: #fff; |
| | | } |
| | | |
| | | .process-step.current .step-dot { |
| | | background: #1890ff; |
| | | color: #fff; |
| | | animation: pulse 2s infinite; |
| | | } |
| | | |
| | | .process-step.pending .step-dot { |
| | | background: #d9d9d9; |
| | | color: #999; |
| | | } |
| | | |
| | | .step-line { |
| | | width: 2px; |
| | | height: 40px; |
| | | background: #d9d9d9; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .process-step.completed .step-line { |
| | | background: #52c41a; |
| | | } |
| | | |
| | | .process-step.rejected .step-dot { |
| | | background: #ff4d4f; |
| | | color: #fff; |
| | | } |
| | | .process-step.rejected .step-line { |
| | | background: #ff4d4f; |
| | | } |
| | | |
| | | .step-content { |
| | | flex: 1; |
| | | padding-top: 4px; |
| | | } |
| | | |
| | | .step-info { |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .step-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .step-approver { |
| | | font-size: 14px; |
| | | color: #666; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .step-time { |
| | | font-size: 12px; |
| | | color: #999; |
| | | display: block; |
| | | } |
| | | |
| | | .step-opinion { |
| | | background: #f8f9fa; |
| | | padding: 12px; |
| | | border-radius: 8px; |
| | | border-left: 4px solid #52c41a; |
| | | } |
| | | |
| | | .opinion-label { |
| | | font-size: 12px; |
| | | color: #666; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .opinion-content { |
| | | font-size: 14px; |
| | | color: #333; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .approval-input { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .input-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .input-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .input-content { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .footer-actions { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 16px; |
| | | box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .reject-btn { |
| | | width: 120px; |
| | | background: #ff4d4f; |
| | | color: #fff; |
| | | border: none; |
| | | } |
| | | |
| | | .approve-btn { |
| | | width: 120px; |
| | | background: #52c41a; |
| | | color: #fff; |
| | | border: none; |
| | | } |
| | | |
| | | @keyframes pulse { |
| | | 0% { |
| | | box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7); |
| | | } |
| | | 70% { |
| | | box-shadow: 0 0 0 10px rgba(24, 144, 255, 0); |
| | | } |
| | | 100% { |
| | | box-shadow: 0 0 0 0 rgba(24, 144, 255, 0); |
| | | } |
| | | } |
| | | .signature-section { |
| | | background: #fff; |
| | | padding: 12px 16px 16px; |
| | | border-top: 1px solid #f0f0f0; |
| | | } |
| | | .signature-header { |
| | | margin-bottom: 8px; |
| | | } |
| | | .signature-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | .signature-box { |
| | | width: 100%; |
| | | height: 180px; |
| | | background: #fff; |
| | | border: 1px dashed #d9d9d9; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | .signature-actions { |
| | | margin-top: 8px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="contact-select"> |
| | | <!-- 顶鍿 颿 --> |
| | | <view class="header"> |
| | | <up-icon name="arrow-left" size="20" color="#333" @click="goBack" /> |
| | | <text class="title">éæ©è系人</text> |
| | | <text class="confirm-btn" @click="confirmSelect">ç¡®å®</text> |
| | | </view> |
| | | |
| | | <!-- æç´¢æ¡ --> |
| | | <!-- <view class="search-section">--> |
| | | <!-- <van-search--> |
| | | <!-- v-model="searchValue"--> |
| | | <!-- placeholder="æç´¢è系人"--> |
| | | <!-- @search="onSearch"--> |
| | | <!-- @input="onSearch"--> |
| | | <!-- />--> |
| | | <!-- </view>--> |
| | | |
| | | <!-- 已鿩çè系人 --> |
| | | <view class="selected-section" v-if="selectedContact"> |
| | | <view class="selected-header"> |
| | | <text class="selected-title">已鿩</text> |
| | | <text class="clear-btn" @click="clearSelected">æ¸
空</text> |
| | | </view> |
| | | <view class="selected-item"> |
| | | <view class="contact-avatar"> |
| | | <text class="avatar-text">{{ selectedContact.nickName.charAt(0) }}</text> |
| | | </view> |
| | | <view class="contact-details"> |
| | | <text class="contact-name">{{ selectedContact.nickName }}</text> |
| | | </view> |
| | | <van-icon name="cross" size="16" color="#999" @click="clearSelected" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- è系人å表 --> |
| | | <view class="contact-list"> |
| | | <view class="list-header"> |
| | | <text class="list-title">å
¨é¨è系人</text> |
| | | </view> |
| | | |
| | | <van-list |
| | | v-model:loading="loading" |
| | | :finished="finished" |
| | | finished-text="æ²¡ææ´å¤äº" |
| | | @load="onLoad" |
| | | > |
| | | <view |
| | | v-for="contact in userList" |
| | | :key="contact.userId" |
| | | class="contact-item" |
| | | :class="{ 'selected': isSelected(contact) }" |
| | | @click="selectContact(contact)" |
| | | > |
| | | <view class="contact-info"> |
| | | <view class="contact-avatar"> |
| | | <text class="avatar-text">{{ contact.nickName.charAt(0) }}</text> |
| | | </view> |
| | | <view class="contact-details"> |
| | | <text class="contact-name">{{ contact.nickName }}</text> |
| | | <!-- <text class="contact-dept">{{ contact.department }}</text>--> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </van-list> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { userListNoPageByTenantId } from "@/api/system/user" |
| | | |
| | | const loading = ref(false) |
| | | const finished = ref(false) |
| | | const selectedContact = ref(null) |
| | | const userList = ref([]) |
| | | |
| | | // æ¥æ¶ä¼ éçåæ° |
| | | const stepIndex = ref(0) |
| | | |
| | | onMounted(() => { |
| | | // è·å页é¢åæ° |
| | | const pages = getCurrentPages() |
| | | const currentPage = pages[pages.length - 1] |
| | | if (currentPage.options.stepIndex !== undefined) { |
| | | stepIndex.value = parseInt(currentPage.options.stepIndex) |
| | | } |
| | | |
| | | // åå§åèç³»äººæ°æ® |
| | | initContacts() |
| | | }) |
| | | |
| | | const initContacts = () => { |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data |
| | | }) |
| | | finished.value = true |
| | | } |
| | | |
| | | const onLoad = () => { |
| | | // 模æå è½½æ´å¤æ°æ® |
| | | setTimeout(() => { |
| | | loading.value = false |
| | | finished.value = true |
| | | }, 1000) |
| | | } |
| | | |
| | | const isSelected = (contact) => { |
| | | return selectedContact.value && selectedContact.value.userId === contact.userId |
| | | } |
| | | |
| | | const selectContact = (contact) => { |
| | | // å鿍¡å¼ï¼ç´æ¥æ¿æ¢éä¸çè系人 |
| | | selectedContact.value = contact |
| | | } |
| | | |
| | | const clearSelected = () => { |
| | | selectedContact.value = null |
| | | } |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | const confirmSelect = () => { |
| | | if (!selectedContact.value) { |
| | | uni.showToast({ |
| | | title: 'è¯·éæ©ä¸ä¸ªè系人', |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | } |
| | | // ä½¿ç¨ uni.$emit åéæ°æ® |
| | | uni.$emit('selectContact', { |
| | | stepIndex: stepIndex.value, |
| | | contact: selectedContact.value |
| | | }) |
| | | uni.navigateBack() |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .contact-select { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .header { |
| | | background: #ffffff; |
| | | padding: 16px 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | position: sticky; |
| | | /* å
¼å®¹ iOS åæµ·/çµå¨å²å®å
¨åº */ |
| | | padding-top: calc(env(safe-area-inset-top)); |
| | | top: 0; |
| | | z-index: 100; |
| | | position: relative; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .confirm-btn { |
| | | color: #006cfb; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .search-section { |
| | | background: #fff; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .selected-section { |
| | | background: #fff; |
| | | margin-top: 8px; |
| | | padding: 16px; |
| | | } |
| | | |
| | | .selected-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .selected-title { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .clear-btn { |
| | | color: #006cfb; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .selected-item { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #f0f8ff; |
| | | border: 1px solid #006cfb; |
| | | border-radius: 12px; |
| | | padding: 12px; |
| | | gap: 12px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: -2px; |
| | | right: -2px; |
| | | width: 16px; |
| | | height: 16px; |
| | | background: #52c41a; |
| | | border-radius: 50%; |
| | | border: 2px solid #fff; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | &::after { |
| | | content: 'â'; |
| | | position: absolute; |
| | | top: -1px; |
| | | right: -1px; |
| | | width: 16px; |
| | | height: 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 10px; |
| | | color: #fff; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .contact-list { |
| | | background: #fff; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .list-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .list-title { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .contact-item { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f8f9fa; |
| | | transition: all 0.2s; |
| | | position: relative; |
| | | |
| | | &.selected { |
| | | background-color: #f0f8ff; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 8px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 4px; |
| | | background: #006cfb; |
| | | border-radius: 50%; |
| | | box-shadow: 0 0 0 4px rgba(0, 108, 251, 0.2); |
| | | } |
| | | } |
| | | |
| | | &:active { |
| | | background-color: #f5f5f5; |
| | | } |
| | | } |
| | | |
| | | .contact-info { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | padding-left: 16px; |
| | | } |
| | | |
| | | .contact-avatar { |
| | | width: 40px; |
| | | height: 40px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 12px; |
| | | position: relative; |
| | | } |
| | | |
| | | .avatar-text { |
| | | color: #fff; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .contact-details { |
| | | flex: 1; |
| | | } |
| | | |
| | | .contact-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #333; |
| | | } |
| | | |
| | | .contact-dept { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | // èªå®ä¹åéæé®æ ·å¼ |
| | | :deep(.van-radio) { |
| | | .van-radio__icon { |
| | | width: 20px; |
| | | height: 20px; |
| | | border: 2px solid #ddd; |
| | | border-radius: 50%; |
| | | background: #fff; |
| | | position: relative; |
| | | transition: all 0.2s; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%) scale(0); |
| | | width: 8px; |
| | | height: 8px; |
| | | background: #006cfb; |
| | | border-radius: 50%; |
| | | transition: transform 0.2s; |
| | | } |
| | | } |
| | | |
| | | &.van-radio--checked { |
| | | .van-radio__icon { |
| | | border-color: #006cfb; |
| | | background: #fff; |
| | | |
| | | &::before { |
| | | transform: translate(-50%, -50%) scale(1); |
| | | } |
| | | |
| | | &::after { |
| | | content: ''; |
| | | position: absolute; |
| | | top: -2px; |
| | | left: -2px; |
| | | right: -2px; |
| | | bottom: -2px; |
| | | border: 2px solid rgba(0, 108, 251, 0.2); |
| | | border-radius: 50%; |
| | | animation: ripple 0.6s ease-out; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @keyframes ripple { |
| | | 0% { |
| | | transform: scale(0.8); |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: scale(1.2); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view class="account-detail"> |
| | | <!-- 顶鍿 颿 --> |
| | | <view class="header"> |
| | | <up-icon name="arrow-left" size="20" color="#333" @click="goBack" /> |
| | | <text class="title">å®¡æ¹æµç¨</text> |
| | | </view> |
| | | <PageHeader title="å®¡æ¹æµç¨" @back="goBack" /> |
| | | |
| | | <!-- 表ååºå --> |
| | | <view class="form-section"> |
| | | <van-form ref="formRef" @submit="submitForm" :rules="rules" input-align="right"> |
| | | <van-cell-group inset style="height:auto"> |
| | | <van-form ref="formRef" @submit="submitForm" :rules="rules" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center"> |
| | | <van-cell-group style="margin-bottom: 16px;"> |
| | | <van-field |
| | | v-model="form.approveReason" |
| | | name="approveReason" |
| | | rows="2" |
| | | autosize |
| | | label="ç³è¯·äºç±" |
| | | type="textarea" |
| | | maxlength="200" |
| | | :rules="[{ required: true, message: 'ç³è¯·äºç±ä¸è½ä¸ºç©º' }]" |
| | | placeholder="请è¾å
¥ç³è¯·äºç±" |
| | | show-word-limit |
| | | required |
| | | /> |
| | | </van-cell-group> |
| | | <van-cell-group> |
| | | <van-field |
| | | v-model="form.approveDeptName" |
| | | readonly |
| | | name="picker" |
| | | label="ç³è¯·é¨é¨" |
| | | placeholder="è¯·éæ©ç³è¯·é¨é¨" |
| | | :rules="[{ required: true, message: 'è¯·éæ©ç³è¯·é¨é¨' }]" |
| | | @click="showPicker = true" |
| | | required |
| | | /> |
| | | <van-field |
| | | v-model="taxPrice" |
| | | v-model="form.approveUserName" |
| | | name="taxPrice" |
| | | label="å§å" |
| | | placeholder="请è¾å
¥å§å" |
| | | :rules="[{ required: true, message: 'å§åä¸è½ä¸ºç©º' }]" |
| | | label="ç³è¯·äºº" |
| | | placeholder="请è¾å
¥ç³è¯·äºº" |
| | | :rules="[{ required: true, message: 'ç³è¯·äººä¸è½ä¸ºç©º' }]" |
| | | required |
| | | readonly |
| | | /> |
| | | <van-field |
| | | v-model="result" |
| | | readonly |
| | | name="picker" |
| | | label="ç³è¯·é¨é¨" |
| | | placeholder="è¯·éæ©ç³è¯·é¨é¨" |
| | | :rules="[{ required: true, message: 'è¯·éæ©ç³è¯·é¨é¨' }]" |
| | | @click="showPicker = true" |
| | | required |
| | | /> |
| | | <van-popup |
| | | v-model:show="showPicker" |
| | | destroy-on-close |
| | | position="bottom" |
| | | > |
| | | <van-picker |
| | | :columns="columns" |
| | | :columns="productOptions" |
| | | :model-value="pickerValue" |
| | | @confirm="onConfirm" |
| | | @cancel="showPicker = false" |
| | | /> |
| | | </van-popup> |
| | | <van-field |
| | | v-model="message" |
| | | name="message" |
| | | rows="1" |
| | | autosize |
| | | label="ç³è¯·äºç±" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥ç³è¯·äºç±" |
| | | height="100" |
| | | :rules="[{ required: true, message: 'ç³è¯·äºç±ä¸è½ä¸ºç©º' }]" |
| | | required |
| | | /> |
| | | <van-field |
| | | v-model="form.approveTime" |
| | | label="ç³è¯·æ¥æ" |
| | | placeholder="è¯·éæ©" |
| | | readonly |
| | | required |
| | | @click="showDatePicker" |
| | | :rules="[{ required: true, message: 'è¯·éæ©æ¥æ¬¾æ¥æ' }]" |
| | | /> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <van-popup v-model:show="showDate" position="bottom"> |
| | | <van-date-picker |
| | | v-model="currentDate" |
| | | title="éæ©æ¥æ" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | /> |
| | | </van-popup> |
| | | </van-cell-group> |
| | | </van-form> |
| | | </view> |
| | |
| | | <view class="approval-process"> |
| | | <view class="approval-header"> |
| | | <text class="approval-title">å®¡æ ¸æµç¨</text> |
| | | <text class="approval-desc">å·²ç±ç®¡çåé¢è®¾ä¸å¯ä¿®æ¹</text> |
| | | <text class="approval-desc">æ¯ä¸ªæ¥éª¤åªè½éæ©ä¸ä¸ªå®¡æ¹äºº</text> |
| | | </view> |
| | | |
| | | <view class="approval-steps"> |
| | | <view v-for="(step, stepIndex) in approvalSteps" :key="stepIndex" class="approval-step"> |
| | | <view v-for="(step, stepIndex) in approverNodes" :key="stepIndex" class="approval-step"> |
| | | <view class="step-dot"></view> |
| | | <view class="step-title"> |
| | | <text>审æ¹äºº</text> |
| | | </view> |
| | | <view class="approvers-container"> |
| | | <view v-for="(approver, approverIndex) in step.approvers" :key="approverIndex" class="approver-item"> |
| | | <view class="approver-avatar"></view> |
| | | <text class="approver-name">{{ approver.name }}</text> |
| | | <view class="delete-approver-btn" @click="removeApprover(stepIndex, approverIndex)">Ã</view> |
| | | <view class="approver-container"> |
| | | <view v-if="step.nickName" class="approver-item"> |
| | | <view class="approver-avatar"> |
| | | <text class="avatar-text">{{ step.nickName.charAt(0) }}</text> |
| | | <view class="status-dot"></view> |
| | | </view> |
| | | <view class="approver-info"> |
| | | <text class="approver-name">{{ step.nickName }}</text> |
| | | </view> |
| | | <view class="delete-approver-btn" @click="removeApprover(stepIndex)">Ã</view> |
| | | </view> |
| | | <view class="add-approver-btn" @click="addApprover(stepIndex)">+ |
| | | <view v-else class="add-approver-btn" @click="addApprover(stepIndex)"> |
| | | <view class="add-circle">+</view> |
| | | <text class="add-label">鿩审æ¹äºº</text> |
| | | </view> |
| | | </view> |
| | | <view class="step-line" v-if="stepIndex < approvalSteps.length - 1"></view> |
| | | <view class="delete-step-btn" @click="removeApprovalStep(stepIndex)">å é¤èç¹</view> |
| | | <view class="step-line" v-if="stepIndex < approverNodes.length - 1"></view> |
| | | <view class="delete-step-btn" v-if="approverNodes.length > 1" @click="removeApprovalStep(stepIndex)">å é¤èç¹</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="add-step-btn" @click="addApprovalStep"> |
| | | <text>æ°å¢èç¹å®¡æ ¸äºº</text> |
| | | <view class="add-step-btn"> |
| | | <van-button icon="plus" plain type="primary" style="width: 100%" @click="addApprovalStep">æ°å¢èç¹</van-button> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ref, onMounted } from "vue"; |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import {getDept, approveProcessGetInfo, approveProcessAdd, approveProcessUpdate} from "@/api/collaborativeApproval/approvalProcess"; |
| | | import { showToast } from 'vant' |
| | | import {userListNoPageByTenantId} from "@/api/system/user"; |
| | | |
| | | export default { |
| | | setup() { |
| | | const rules = ref({ |
| | | |
| | | taxPrice: { |
| | | rules: [{ required: true, errorMessage: 'å§åä¸è½ä¸ºç©º' }] |
| | | }, |
| | | result: { |
| | | rules: [{ required: true, errorMessage: 'è¯·éæ©ç³è¯·é¨é¨' }] |
| | | }, |
| | | message: { |
| | | rules: [{ required: true, errorMessage: 'ç³è¯·äºç±ä¸è½ä¸ºç©º' }] |
| | | }, |
| | | const data = reactive({ |
| | | form: { |
| | | approveTime: "", |
| | | approveId: "", |
| | | approveUser: "", |
| | | approveUserName: "", |
| | | approveDeptName: "", |
| | | approveDeptId: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | tempFileIds: [], |
| | | approverList: [] // æ°å¢å段ï¼å卿æèç¹ç审æ¹äººid |
| | | }, |
| | | rules: { |
| | | approveTime: [{ required: false, message: "请è¾å
¥", trigger: "change" },], |
| | | approveId: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | approveUser: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | approveDeptId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | approveReason: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const result = ref(""); |
| | | const pickerValue = ref([]); |
| | | const showPicker = ref(false); |
| | | const columns = ref([]); |
| | | onMounted(async () => { |
| | | try { |
| | | // æ¿æ¢ä¸ºå®é
æ¥å£å°å |
| | | // const response = await axios.get('/api/getDepartments'); |
| | | columns.value = [ |
| | | { |
| | | text: "æå·", |
| | | value: "Hangzhou", |
| | | }, |
| | | { |
| | | text: "宿³¢", |
| | | value: "Ningbo", |
| | | }, |
| | | { |
| | | text: "温å·", |
| | | value: "Wenzhou", |
| | | }, |
| | | { |
| | | text: "ç»å
´", |
| | | value: "Shaoxing", |
| | | }, |
| | | { |
| | | text: "æ¹å·", |
| | | value: "Huzhou", |
| | | }, |
| | | ]; |
| | | } catch (error) { |
| | | console.error("è·åé¨é¨æ°æ®å¤±è´¥:", error); |
| | | } |
| | | }); |
| | | const onConfirm = ({ selectedValues, selectedOptions }) => { |
| | | result.value = selectedOptions[0]?.text; |
| | | pickerValue.value = selectedValues; |
| | | showPicker.value = false; |
| | | }; |
| | | const taxPrice = ref(""); |
| | | const contractAmount = ref(""); |
| | | const approvalSteps = ref([ |
| | | { approvers: [{ name: 'å¢å°æ' }, { name: 'å¢å°æ' }] }, |
| | | { approvers: [{ name: 'å¢å°æ' }] }, |
| | | { approvers: [{ name: 'å¢å°æ' }] }, |
| | | { approvers: [{ name: 'å¢å°æ' }] } |
| | | ]); |
| | | const { form, rules } = toRefs(data); |
| | | const result = ref(""); |
| | | const pickerValue = ref([]); |
| | | const showPicker = ref(false); |
| | | const productOptions = ref([]); |
| | | const operationType = ref(""); |
| | | const currentApproveStatus = ref(""); |
| | | const approverNodes = ref([]); |
| | | const userList = ref([]); |
| | | const formRef = ref(null); |
| | | const message = ref(""); |
| | | const showDate = ref(false) |
| | | const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]) |
| | | const userStore = useUserStore() |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const formRef = ref(null); |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate().then(() => { |
| | | // è¡¨åæ ¡éªéè¿ï¼å¯ä»¥æäº¤æ°æ® |
| | | console.log("è¡¨åæ°æ®:", { |
| | | taxPrice: taxPrice.value, |
| | | department: result.value, |
| | | message: message.value, |
| | | approvalSteps: approvalSteps.value |
| | | }); |
| | | |
| | | uni.showToast({ |
| | | title: "ä¿åæå", |
| | | icon: "success", |
| | | }); |
| | | }).catch((error) => { |
| | | console.error("è¡¨åæ ¡éªå¤±è´¥:", error); |
| | | // æ¾ç¤ºå
·ä½çéè¯¯ä¿¡æ¯ |
| | | if (error.length > 0) { |
| | | const firstError = error[0]; |
| | | uni.showToast({ |
| | | title: firstError.message || 'è¡¨åæ ¡éªå¤±è´¥', |
| | | icon: 'none' |
| | | }); |
| | | } else { |
| | | uni.showToast({ |
| | | title: 'è¡¨åæ ¡éªå¤±è´¥ï¼è¯·æ£æ¥å¿
填项', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const message = ref(""); |
| | | |
| | | const addApprover = (stepIndex) => { |
| | | // 卿å®å®¡æ¹æ¥éª¤æ·»å æ°ç审æ¹äºº |
| | | approvalSteps.value[stepIndex].approvers.push({ name: 'å¢å°æ' }); |
| | | }; |
| | | |
| | | const addApprovalStep = () => { |
| | | // æ·»å æ°çå®¡æ¹æ¥éª¤ |
| | | approvalSteps.value.push({ approvers: [{ name: 'å¢å°æ' }] }); |
| | | }; |
| | | |
| | | const removeApprover = (stepIndex, approverIndex) => { |
| | | // ç¡®ä¿æ¯ä¸ªæ¥éª¤è³å°ä¿çä¸ä¸ªå®¡æ¹äºº |
| | | if (approvalSteps.value[stepIndex].approvers.length > 1) { |
| | | approvalSteps.value[stepIndex].approvers.splice(approverIndex, 1); |
| | | } else { |
| | | uni.showToast({ |
| | | title: 'æ¯ä¸ªæ¥éª¤è³å°éè¦ä¸ä¸ªå®¡æ¹äºº', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const removeApprovalStep = (stepIndex) => { |
| | | // ç¡®ä¿è³å°ä¿çä¸ä¸ªå®¡æ¹æ¥éª¤ |
| | | if (approvalSteps.value.length > 1) { |
| | | approvalSteps.value.splice(stepIndex, 1); |
| | | } else { |
| | | uni.showToast({ |
| | | title: 'è³å°éè¦ä¸ä¸ªå®¡æ¹æ¥éª¤', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | return { |
| | | rules, |
| | | removeApprovalStep, |
| | | removeApprover, |
| | | result, |
| | | pickerValue, |
| | | columns, |
| | | onConfirm, |
| | | showPicker, |
| | | taxPrice, |
| | | contractAmount, |
| | | goBack, |
| | | submitForm, |
| | | approvalSteps, |
| | | addApprover, |
| | | addApprovalStep, |
| | | formRef, |
| | | message |
| | | }; |
| | | }, |
| | | const getProductOptions = () => { |
| | | getDept().then((res) => { |
| | | productOptions.value = res.data.map(item => ({ |
| | | value: item.deptId, |
| | | text: item.deptName |
| | | })) |
| | | }); |
| | | }; |
| | | const fileList = ref([]); |
| | | let nextApproverId = 2; |
| | | |
| | | onMounted(async () => { |
| | | try { |
| | | getProductOptions() |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data |
| | | }) |
| | | form.value.approveUser = userStore.id |
| | | form.value.approveUserName = userStore.nickName |
| | | form.value.approveTime = getCurrentDate(); |
| | | |
| | | // è·åURLåæ° |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | operationType.value = currentPage.options.operationType || 'add'; |
| | | |
| | | // 妿æ¯ç¼è¾æ¨¡å¼ï¼ä»æ¬å°åå¨è·åæ°æ® |
| | | if (operationType.value === 'edit') { |
| | | const storedData = uni.getStorageSync('invoiceLedgerEditRow'); |
| | | if (storedData) { |
| | | const row = JSON.parse(storedData); |
| | | 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) => { |
| | | const userIdNum = parseInt(userId.trim()); |
| | | // ä»userList䏿¾å°å¯¹åºçç¨æ·ä¿¡æ¯ |
| | | const userInfo = userList.value.find(user => user.userId === userIdNum); |
| | | return { |
| | | id: idx + 1, |
| | | userId: userIdNum, |
| | | nickName: userInfo ? userInfo.nickName : null |
| | | }; |
| | | }); |
| | | nextApproverId = userIds.length + 1; |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼åå§åä¸ä¸ªç©ºç审æ¹èç¹ |
| | | approverNodes.value = [{ id: 1, userId: null, nickName: null }]; |
| | | nextApproverId = 2; |
| | | } |
| | | }); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼åå§åä¸ä¸ªç©ºç审æ¹èç¹ |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | } |
| | | |
| | | // çå¬èç³»äººéæ©äºä»¶ |
| | | uni.$on('selectContact', handleSelectContact); |
| | | } catch (error) { |
| | | console.error("è·åé¨é¨æ°æ®å¤±è´¥:", error); |
| | | } |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | // ç§»é¤äºä»¶çå¬ |
| | | uni.$off('selectContact', handleSelectContact); |
| | | }); |
| | | |
| | | const onConfirm = ({ selectedValues, selectedOptions }) => { |
| | | form.value.approveDeptName = selectedOptions[0]?.text; |
| | | form.value.approveDeptId = selectedOptions[0]?.value; |
| | | pickerValue.value = selectedValues; |
| | | showPicker.value = false; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | // æ¸
餿¬å°åå¨çæ°æ® |
| | | uni.removeStorageSync('invoiceLedgerEditRow'); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | // æ£æ¥æ¯ä¸ªå®¡æ¹æ¥éª¤æ¯å¦é½æå®¡æ¹äºº |
| | | const hasEmptyStep = approverNodes.value.some(step => !step.nickName); |
| | | if (hasEmptyStep) { |
| | | showToast('请为æ¯ä¸ªå®¡æ¹æ¥éª¤éæ©å®¡æ¹äºº'); |
| | | return; |
| | | } |
| | | |
| | | formRef.value.validate().then(() => { |
| | | // è¡¨åæ ¡éªéè¿ï¼å¯ä»¥æäº¤æ°æ® |
| | | // æ¶éææèç¹ç审æ¹äººid |
| | | console.log('approverNodes---', approverNodes.value) |
| | | form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',') |
| | | form.value.approveType = 0 |
| | | if (operationType.value === "add" || currentApproveStatus.value == 3) { |
| | | approveProcessAdd(form.value).then(res => { |
| | | showToast("æäº¤æå"); |
| | | goBack() |
| | | }) |
| | | } else { |
| | | approveProcessUpdate(form.value).then(res => { |
| | | showToast("æäº¤æå"); |
| | | goBack() |
| | | }) |
| | | } |
| | | }).catch((error) => { |
| | | console.error("è¡¨åæ ¡éªå¤±è´¥:", error); |
| | | // æ¾ç¤ºå
·ä½çéè¯¯ä¿¡æ¯ |
| | | if (error.length > 0) { |
| | | const firstError = error[0]; |
| | | uni.showToast({ |
| | | title: firstError.message || 'è¡¨åæ ¡éªå¤±è´¥', |
| | | icon: 'none' |
| | | }); |
| | | } else { |
| | | uni.showToast({ |
| | | title: 'è¡¨åæ ¡éªå¤±è´¥ï¼è¯·æ£æ¥å¿
填项', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å¤çèç³»äººéæ©ç»æ |
| | | const handleSelectContact = (data) => { |
| | | const { stepIndex, contact } = data; |
| | | // å°éä¸çè系人设置为对åºå®¡æ¹æ¥éª¤ç审æ¹äºº |
| | | approverNodes.value[stepIndex].userId = contact.userId; |
| | | approverNodes.value[stepIndex].nickName = contact.nickName; |
| | | }; |
| | | |
| | | const addApprover = (stepIndex) => { |
| | | // 跳转å°èç³»äººéæ©é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: `/pages/cooperativeOffice/collaborativeApproval/contactSelect?stepIndex=${stepIndex}` |
| | | }); |
| | | }; |
| | | |
| | | const addApprovalStep = () => { |
| | | // æ·»å æ°çå®¡æ¹æ¥éª¤ |
| | | approverNodes.value.push({ userId: null, nickName: null }); |
| | | }; |
| | | |
| | | const removeApprover = (stepIndex) => { |
| | | // ç§»é¤å®¡æ¹äºº |
| | | approverNodes.value[stepIndex].userId = null; |
| | | approverNodes.value[stepIndex].nickName = null; |
| | | }; |
| | | |
| | | const removeApprovalStep = (stepIndex) => { |
| | | // ç¡®ä¿è³å°ä¿çä¸ä¸ªå®¡æ¹æ¥éª¤ |
| | | if (approverNodes.value.length > 1) { |
| | | approverNodes.value.splice(stepIndex, 1); |
| | | } else { |
| | | uni.showToast({ |
| | | title: 'è³å°éè¦ä¸ä¸ªå®¡æ¹æ¥éª¤', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }; |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true |
| | | } |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = ({ selectedValues }) => { |
| | | form.value.approveTime = selectedValues.join('-') |
| | | currentDate.value = selectedValues |
| | | showDate.value = false |
| | | } |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | | const today = new Date(); |
| | | const year = today.getFullYear(); |
| | | const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ |
| | | const day = String(today.getDate()).padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | |
| | | margin-top: 16px; |
| | | } |
| | | |
| | | .van-field { |
| | | height: 56px; |
| | | line-height: 36px; |
| | | } |
| | | |
| | | .product-section { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 16px; |
| | | padding: 20px 16px 8px 16px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .add-btn { |
| | | background: #2979ff; |
| | | color: #fff; |
| | | border-radius: 8px; |
| | | padding: 4px 16px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .product-card { |
| | | background: #f8f9fa; |
| | | border-radius: 12px; |
| | | padding: 12px; |
| | | margin-bottom: 16px; |
| | | box-shadow: 0 1px 4px rgba(41, 121, 255, 0.06); |
| | | position: relative; |
| | | } |
| | | |
| | | .product-row { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .product-label { |
| | | min-width: 60px; |
| | | color: #888; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .del-row { |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .del-btn { |
| | | background: #ff4d4f; |
| | | color: #fff; |
| | | border-radius: 8px; |
| | | padding: 4px 16px; |
| | | font-size: 13px; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .approval-process { |
| | | background: #fff; |
| | | margin: 16px; |
| | |
| | | color: #999; |
| | | } |
| | | |
| | | /* æ ·å¼å¢å¼ºä¸ºâç®æ´å°åå飿 ¼â */ |
| | | .approval-steps { |
| | | padding-left: 16px; |
| | | padding-left: 22px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 11px; |
| | | top: 40px; |
| | | bottom: 40px; |
| | | width: 2px; |
| | | background: linear-gradient(to bottom, #e6f7ff 0%, #bae7ff 50%, #91d5ff 100%); |
| | | border-radius: 1px; |
| | | } |
| | | } |
| | | |
| | | .approval-step { |
| | | position: relative; |
| | | margin-bottom: 20px; |
| | | margin-bottom: 24px; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: -18px; |
| | | top: 14px; // ä» 8px è°æ´ä¸º 14pxï¼ä¸æåä¸å¿å¯¹é½ |
| | | width: 12px; |
| | | height: 12px; |
| | | background: #fff; |
| | | border: 3px solid #006cfb; |
| | | border-radius: 50%; |
| | | z-index: 2; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | } |
| | | |
| | | .step-title { |
| | | top: 12px; |
| | | margin-bottom: 12px; |
| | | position: relative; |
| | | margin-left: 6px; |
| | | } |
| | | |
| | | .step-title text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | background: #f0f0f0; |
| | | padding: 2px 8px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .approvers-container { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | margin-bottom: 8px; |
| | | padding: 4px 12px; |
| | | border-radius: 12px; |
| | | position: relative; |
| | | line-height: 1.4; // ç¡®ä¿æåè¡é«ä¸è´ |
| | | } |
| | | |
| | | .approver-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | width: 60px; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | gap: 12px; |
| | | position: relative; |
| | | border: 1px solid #e6f7ff; |
| | | box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .approver-avatar { |
| | | width: 40px; |
| | | height: 40px; |
| | | background: #e6f7ff; |
| | | width: 48px; |
| | | height: 48px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 50%; |
| | | margin-bottom: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); |
| | | } |
| | | |
| | | .approver-avatar::after { |
| | | content: 'ð¤'; |
| | | font-size: 20px; |
| | | .avatar-text { |
| | | color: #fff; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .approver-info { |
| | | flex: 1; |
| | | position: relative; |
| | | } |
| | | |
| | | .approver-name { |
| | | font-size: 12px; |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #333; |
| | | text-align: center; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | margin-bottom: 2px; |
| | | font-weight: 500; |
| | | position: relative; |
| | | } |
| | | |
| | | .approver-dept { |
| | | font-size: 12px; |
| | | color: #999; |
| | | background: rgba(0, 108, 251, 0.05); |
| | | padding: 2px 8px; |
| | | border-radius: 8px; |
| | | display: inline-block; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 4px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 2px; |
| | | height: 2px; |
| | | background: #006cfb; |
| | | border-radius: 50%; |
| | | } |
| | | } |
| | | |
| | | .delete-approver-btn { |
| | | font-size: 12px; |
| | | font-size: 16px; |
| | | color: #ff4d4f; |
| | | background: rgba(255, 77, 79, 0.1); |
| | | width: 16px; |
| | | height: 16px; |
| | | background: linear-gradient(135deg, rgba(255, 77, 79, 0.1) 0%, rgba(255, 77, 79, 0.05) 100%); |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-top: 2px; |
| | | transition: all 0.3s ease; |
| | | position: relative; |
| | | } |
| | | |
| | | .add-approver-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%); |
| | | border: 2px dashed #006cfb; |
| | | border-radius: 16px; |
| | | padding: 20px; |
| | | color: #006cfb; |
| | | font-size: 14px; |
| | | position: relative; |
| | | transition: all 0.3s ease; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: 32px; |
| | | height: 32px; |
| | | border: 2px solid #006cfb; |
| | | border-radius: 50%; |
| | | opacity: 0; |
| | | transition: all 0.3s ease; |
| | | } |
| | | } |
| | | |
| | | .delete-step-btn { |
| | | margin-top: 8px; |
| | | color: #ff4d4f; |
| | | font-size: 12px; |
| | | background: rgba(255, 77, 79, 0.1); |
| | | padding: 2px 8px; |
| | | border-radius: 4px; |
| | | background: linear-gradient(135deg, rgba(255, 77, 79, 0.1) 0%, rgba(255, 77, 79, 0.05) 100%); |
| | | padding: 6px 12px; |
| | | border-radius: 12px; |
| | | display: inline-block; |
| | | position: relative; |
| | | transition: all 0.3s ease; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 6px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 4px; |
| | | background: #ff4d4f; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .add-approver-btn { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: 1px dashed #ccc; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 20px; |
| | | color: #999; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .step-line { |
| | | position: absolute; |
| | | left: 20px; |
| | | top: 100%; |
| | | width: 1px; |
| | | height: 30px; |
| | | background: #e0e0e0; |
| | | display: none; // éè忥ç线æ¡ï¼ä½¿ç¨ä¼ªå
ç´ ä»£æ¿ |
| | | } |
| | | |
| | | .add-step-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-top: 16px; |
| | | color: #006cfb; |
| | | font-size: 14px; |
| | | padding: 8px 0; |
| | | border: 1px dashed #006cfb; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 12px 0; |
| | | box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05); |
| | | z-index: 1000; |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: #ffffff; |
| | | width: 102px; |
| | | background: #c7c9cc; |
| | | box-shadow: 0px 4px 10px 0px rgba(3, 88, 185, 0.2); |
| | | border-radius: 40px 40px 40px 40px; |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: #ffffff; |
| | | width: 224px; |
| | | background: linear-gradient(140deg, #00baff 0%, #006cfb 100%); |
| | | box-shadow: 0px 4px 10px 0px rgba(3, 88, 185, 0.2); |
| | | border-radius: 40px 40px 40px 40px; |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // å¨ç»å®ä¹ |
| | | @keyframes pulse { |
| | | 0% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | 50% { |
| | | transform: scale(1.2); |
| | | opacity: 0.7; |
| | | } |
| | | 100% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | } |
| | | |
| | | @keyframes rotate { |
| | | 0% { |
| | | transform: rotate(0deg); |
| | | } |
| | | 100% { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes ripple { |
| | | 0% { |
| | | transform: translate(-50%, -50%) scale(0.8); |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: translate(-50%, -50%) scale(1.6); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | /* 妿已æ .step-lineï¼è¿éæ´ç²¾åå®ä½å°å·¦ä¾§ä¸å°åç¹å¯¹é½ */ |
| | | .step-line { |
| | | position: absolute; |
| | | left: 4px; |
| | | top: 48px; |
| | | width: 2px; |
| | | height: calc(100% - 48px); |
| | | background: #E5E7EB; |
| | | } |
| | | |
| | | .approver-container { |
| | | display: flex; |
| | | align-items: center; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); |
| | | border-radius: 16px; |
| | | gap: 12px; |
| | | padding: 10px 0; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | } |
| | | |
| | | .approver-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 8px 10px; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | border-radius: 0; |
| | | } |
| | | |
| | | .approver-avatar { |
| | | position: relative; |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 50%; |
| | | background: #F3F4F6; |
| | | border: 2px solid #E5E7EB; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | animation: none; /* ç¦ç¨æè½¬çå¨ç»ï¼åå½ç®æ´ */ |
| | | } |
| | | |
| | | .avatar-text { |
| | | font-size: 14px; |
| | | color: #374151; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .add-approver-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | background: transparent; |
| | | border: none; |
| | | box-shadow: none; |
| | | padding: 0; |
| | | } |
| | | |
| | | .add-approver-btn .add-circle { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: 2px dashed #A0AEC0; |
| | | border-radius: 50%; |
| | | color: #6B7280; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 22px; |
| | | line-height: 1; |
| | | } |
| | | |
| | | .add-approver-btn .add-label { |
| | | color: #3B82F6; |
| | | font-size: 14px; |
| | | } |
| | | </style> |
| | |
| | | <view class="search-filter-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <u-input placeholder="请è¾å
¥éè´ååå·" class="search-text" v-model="searchKeyword"> |
| | | <template #suffix> |
| | | <up-icon name="search" size="24" color="#999" @click="getList"></up-icon> |
| | | </template> |
| | | </u-input> |
| | | <input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥æµç¨ç¼å·" |
| | | v-model="searchForm.approveId" |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="showFilterOptions"> |
| | | <van-icon name="filter-o" size="24" color="#999"></van-icon> |
| | | <view class="search-button" @click="getList"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- éå®å°è´¦ç叿µ --> |
| | | <view class="ledger-list" v-if="total > 0"> |
| | | <!-- 审æ¹å表 --> |
| | | <view class="ledger-list" v-if="ledgerList.length > 0"> |
| | | <view v-for="(item, index) in ledgerList" :key="index"> |
| | | <view class="ledger-item" @click="handleItemClick(item)"> |
| | | <view class="ledger-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.salesContractNo }}</text> |
| | | <text class="item-id">{{ item.approveId }}</text> |
| | | </view> |
| | | <view class="item-tag"> |
| | | <van-tag :type="getTagClass(item.approveStatus)" size="medium">{{ formatReceiptType(item.approveStatus) }}</van-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-info"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç³è¯·äºº</text> |
| | | <text class="detail-value">{{ item.entryPersonName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç³è¯·æ¥æ</text> |
| | | <text class="detail-value highlightBlue">{{ item.entryDate }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç³è¯·äºº</text> |
| | | <text class="detail-value">{{ item.approveUserName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç³è¯·é¨é¨</text> |
| | | <text class="detail-value">{{ item.approveDeptName }}</text> |
| | | </view> |
| | | <view class="detail-row-approveReason"> |
| | | <text class="detail-label">审æ¹äºç±</text> |
| | | <text class="detail-value highlightBlue">{{ item.approveReason }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç³è¯·æ¥æ</text> |
| | | <text class="detail-value">{{ item.approveTime }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç»ææ¥æ</text> |
| | | <text class="detail-value">{{ item.approveOverTime }}</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-info"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç³è¯·é¨é¨</text> |
| | | <text class="detail-value">{{ item.entryPersonName }}</text> |
| | | <view class="detail-row-user"> |
| | | <text class="detail-label">å½å审æ¹äºº</text> |
| | | <view class="detail-value approver-value"> |
| | | <view class="approver-chip"> |
| | | <text class="approver-name">{{ item.approveUserCurrentName || 'æªåé
' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">审æ¹ç¶æ</text> |
| | | <text class="detail-value highlightYellow">{{ item.entryDate }}</text> |
| | | <view class="actions"> |
| | | <van-button |
| | | type="primary" |
| | | size="small" |
| | | class="action-btn edit" |
| | | :disabled="item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4" |
| | | @click="handleItemClick(item)" |
| | | > |
| | | ç¼è¾ |
| | | </van-button> |
| | | <van-button |
| | | type="success" |
| | | size="small" |
| | | class="action-btn approve" |
| | | :disabled="item.approveUserCurrentId == null || item.approveStatus == 2 || item.approveStatus == 3 || item.approveStatus == 4 || item.approveUserCurrentId !== userStore.id" |
| | | @click="approve(item)" |
| | | > |
| | | å®¡æ ¸ |
| | | </van-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | <view v-else class="no-data"> |
| | | <text>ææ å®¡æ¹æ°æ®</text> |
| | | </view> |
| | | |
| | | <van-floating-bubble icon="plus" @click="handleAdd"/> |
| | | <!-- æµ®å¨æä½æé® --> |
| | | <view class="fab-button" @click="handleAdd"> |
| | | <!-- <view class="fab-button" @click="handleAdd"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> --> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | reactive, |
| | | onMounted |
| | | toRefs, |
| | | reactive |
| | | } from "vue"; |
| | | import { |
| | | ledgerListPage |
| | | } from "@/api/cooperativeOffice/collaborativeApproval"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref(""); |
| | | |
| | | // éå®å°è´¦æ°æ® |
| | | import {approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess"; |
| | | import {onShow} from "@dcloudio/uni-app"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | | const userStore = useUserStore() |
| | | // æ°æ® |
| | | const ledgerList = ref([]); |
| | | const total = ref(0); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | approveId: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | |
| | | current: -1, |
| | | size: -1, |
| | | }; |
| | | ledgerListPage({ |
| | | ...page |
| | | approveProcessListPage({ |
| | | ...page,approveType: 0,...searchForm.value |
| | | }) |
| | | .then((res) => { |
| | | ledgerList.value = res.records; |
| | | total.value = res.total; |
| | | ledgerList.value = res.data.records; |
| | | }) |
| | | .catch(() => { |
| | | // tableLoading.value = false; |
| | |
| | | }, |
| | | }); |
| | | }; |
| | | // æ ¼å¼å忬¾æ¹å¼ |
| | | const formatReceiptType = (params) => { |
| | | if (params == 0) { |
| | | return "å¾
å®¡æ ¸"; |
| | | } else if (params == 1) { |
| | | return "å®¡æ ¸ä¸"; |
| | | } else if (params == 2) { |
| | | return "å®¡æ ¸å®æ"; |
| | | } else if (params == 4) { |
| | | return "已鿰æäº¤"; |
| | | } else { |
| | | return 'ä¸éè¿'; |
| | | } |
| | | }; |
| | | // è·åæ ç¾æ ·å¼ç±» |
| | | const getTagClass = (type) => { |
| | | if (type == 0) { |
| | | return "warning"; |
| | | } else if (type == 1) { |
| | | return "primary"; |
| | | } else if (type == 2) { |
| | | return "success"; |
| | | } else if (type == 4) { |
| | | return "primary"; |
| | | } else { |
| | | return "danger"; |
| | | } |
| | | }; |
| | | |
| | | // ç¹å»å表项 |
| | | const handleItemClick = (item) => { |
| | | uni.showToast({ |
| | | title: `æ¥çåå: ${item.contractId}`, |
| | | icon: "none", |
| | | // ä½¿ç¨æ¬å°åå¨ä¼ éæ°æ® |
| | | uni.setStorageSync('invoiceLedgerEditRow', JSON.stringify(item)); |
| | | uni.navigateTo({ |
| | | url: `/pages/cooperativeOffice/collaborativeApproval/detail?operationType=edit&approveId=${item.approveId}`, |
| | | }); |
| | | }; |
| | | |
| | | // æ·»å æ°è®°å½ |
| | | const handleAdd = () => { |
| | | uni.navigateTo({ |
| | | url: "/pages/cooperativeOffice/collaborativeApproval/detail", |
| | | url: "/pages/cooperativeOffice/collaborativeApproval/detail?operationType=add", |
| | | }); |
| | | }; |
| | | // ç¹å»å®¡æ ¸ |
| | | const approve = (item) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/cooperativeOffice/collaborativeApproval/approve?approveId=${item.approveId}` |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | onShow(() => { |
| | | // 页é¢å è½½å®æåçåå§åé»è¾ |
| | | getList(); |
| | | }); |
| | |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | background: #f5f5f5; |
| | | border-radius: 24px; |
| | | padding: 10px 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .search-text { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #333; |
| | | background: transparent; |
| | | border: none; |
| | | outline: none; |
| | | } |
| | | |
| | | .search-text::placeholder { |
| | | color: #999; |
| | | } |
| | | |
| | | |
| | | .search-filter-section { |
| | |
| | | align-items: center; |
| | | gap: 12px; |
| | | } |
| | | |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | background: #f5f5f5; |
| | | border-radius: 24px; |
| | | padding: 4px 16px; |
| | | padding: 10px 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .search-text { |
| | | flex: 1; |
| | | font-size: 14px; |
| | |
| | | border: none; |
| | | outline: none; |
| | | } |
| | | |
| | | |
| | | .search-text::placeholder { |
| | | color: #999; |
| | | } |
| | |
| | | } |
| | | |
| | | .item-tag { |
| | | background: #4caf50; |
| | | border-radius: 4px; |
| | | padding: 2px 4px; |
| | | } |
| | |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | .detail-row-user { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | .detail-row-approveReason { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .detail-info { |
| | |
| | | box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .approver-value { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | .approver-chip { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | background: #f0f6ff; |
| | | color: #2b7cff; |
| | | border: 1px solid #e0efff; |
| | | border-radius: 999px; |
| | | padding: 4px 10px; |
| | | max-width: 100%; |
| | | } |
| | | .approver-name { |
| | | font-size: 12px; |
| | | color: #2b7cff; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | align-items: center; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .action-btn { |
| | | border-radius: 16px; |
| | | height: 28px; |
| | | line-height: 28px; |
| | | padding: 0 12px; |
| | | } |
| | | .action-btn.edit { |
| | | /* primary æ ·å¼æ¥èªç»ä»¶ï¼è¿éä¿çé©å以便åç»éè¦æ©å± */ |
| | | } |
| | | .action-btn.approve { |
| | | /* success æ ·å¼æ¥èªç»ä»¶ï¼è¿éä¿çé©å以便åç»éè¦æ©å± */ |
| | | } |
| | | :deep(.van-floating-bubble) { |
| | | background: #ed8d05; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="ledger-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="operationType === 'edit' ? 'ç¼è¾è®¾å¤å°è´¦' : 'æ°å¢è®¾å¤å°è´¦'" @back="goBack" /> |
| | | |
| | | <!-- 表åå
容 --> |
| | | <van-form @submit="sendForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <van-cell-group title="åºæ¬ä¿¡æ¯" inset> |
| | | <van-field |
| | | v-model="form.deviceName" |
| | | label="设å¤åç§°" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°" |
| | | :rules="formRules.deviceName" |
| | | required |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.deviceModel" |
| | | label="è§æ ¼åå·" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | :readonly="form.deviceModel != null && operationType === 'edit'" |
| | | :rules="formRules.deviceModel" |
| | | required |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.supplierName" |
| | | label="ä¾åºå" |
| | | required |
| | | placeholder="请è¾å
¥ä¾åºå" |
| | | :rules="formRules.supplierName" |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.unit" |
| | | label="åä½" |
| | | required |
| | | placeholder="请è¾å
¥åä½" |
| | | :rules="formRules.unit" |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.taxRate" |
| | | required |
| | | label="ç¨ç(%)" |
| | | placeholder="è¯·éæ©" |
| | | readonly |
| | | :rules="formRules.taxRate" |
| | | @click="showTaxRatePicker" |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.number" |
| | | label="æ°é" |
| | | required |
| | | type="number" |
| | | placeholder="请è¾å
¥æ°é" |
| | | :rules="formRules.number" |
| | | @blur="mathNum" |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.taxIncludingPriceUnit" |
| | | label="å«ç¨åä»·" |
| | | required |
| | | type="number" |
| | | placeholder="请è¾å
¥å«ç¨åä»·" |
| | | :rules="formRules.taxIncludingPriceUnit" |
| | | @blur="mathNum" |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.taxIncludingPriceTotal" |
| | | label="å«ç¨æ»ä»·" |
| | | placeholder="èªå¨çæ" |
| | | readonly |
| | | /> |
| | | <van-field |
| | | v-model="form.unTaxIncludingPriceTotal" |
| | | label="ä¸å«ç¨æ»ä»·" |
| | | placeholder="èªå¨çæ" |
| | | readonly |
| | | /> |
| | | <van-field |
| | | v-model="form.createTime" |
| | | label="å½å
¥æ¥æ" |
| | | placeholder="è¯·éæ©" |
| | | readonly |
| | | @click="showDatePicker" |
| | | required |
| | | clearable |
| | | /> |
| | | </van-cell-group> |
| | | |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <van-button class="cancel-btn" @click="goBack">åæ¶</van-button> |
| | | <van-button class="save-btn" native-type="submit" form-type="submit" :loading="loading">ä¿å</van-button> |
| | | </view> |
| | | </van-form> |
| | | |
| | | <!-- ç¨çéæ©å¨ --> |
| | | <van-popup v-model:show="showTaxRate" position="bottom"> |
| | | <van-picker |
| | | :model-value="taxRatePickerValue" |
| | | :columns="taxRateOptions" |
| | | @confirm="onTaxRateConfirm" |
| | | @cancel="showTaxRate = false" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <van-popup v-model:show="showDate" position="bottom"> |
| | | <van-date-picker |
| | | v-model="currentDate" |
| | | title="éæ©æ¥æ" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | /> |
| | | </van-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { getLedgerById, addLedger, editLedger } from '@/api/equipmentManagement/ledger'; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | calculateTaxIncludeTotalPrice, |
| | | calculateTaxExclusiveTotalPrice, |
| | | } from "@/utils/summarizeTable"; |
| | | import { showToast } from 'vant'; |
| | | |
| | | defineOptions({ |
| | | name: "设å¤å°è´¦è¡¨å", |
| | | }); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const operationType = ref(''); |
| | | const loading = ref(false); |
| | | const showTaxRate = ref(false); |
| | | const taxRatePickerValue = ref([]); |
| | | const showDate = ref(false); |
| | | const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]); |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | deviceName: [{ required: true, trigger: "blur", message: "请è¾å
¥" }], |
| | | deviceModel: [{ required: true, trigger: "blur", message: "请è¾å
¥" }], |
| | | supplierName: [{ required: true, trigger: "blur", message: "请è¾å
¥" }], |
| | | unit: [{ required: true, trigger: "blur", message: "请è¾å
¥" }], |
| | | number: [{ required: true, trigger: "blur", message: "请è¾å
¥" }], |
| | | taxIncludingPriceUnit: [{ required: true, trigger: "blur", message: "请è¾å
¥" }], |
| | | taxRate: [{ required: true, trigger: "change", message: "请è¾å
¥" }], |
| | | }; |
| | | |
| | | // ä½¿ç¨ ref 声æè¡¨åæ°æ® |
| | | const form = ref({ |
| | | deviceName: undefined, // 设å¤åç§° |
| | | deviceModel: undefined, // è§æ ¼åå· |
| | | supplierName: undefined, // ä¾åºå |
| | | unit: undefined, // åä½ |
| | | number: undefined, // æ°é |
| | | taxIncludingPriceUnit: undefined, // å«ç¨åä»· |
| | | taxIncludingPriceTotal: undefined, // å«ç¨æ»ä»· |
| | | taxRate: undefined, // ç¨ç |
| | | unTaxIncludingPriceTotal: undefined, // ä¸å«ç¨æ»ä»· |
| | | createTime: dayjs().format("YYYY-MM-DD"), // å½å
¥æ¥æ |
| | | }); |
| | | |
| | | // ç¨çé项 |
| | | const taxRateOptions = computed(() => { |
| | | return [ |
| | | { text: '1', value: 1 }, |
| | | { text: '6', value: 6 }, |
| | | { text: '13', value: 13 } |
| | | ] |
| | | }); |
| | | |
| | | // å è½½è¡¨åæ°æ® |
| | | const loadForm = async (id) => { |
| | | if (id) { |
| | | operationType.value = 'edit'; |
| | | } |
| | | try { |
| | | const { code, data } = await getLedgerById(id); |
| | | if (code == 200) { |
| | | form.value.deviceName = data.deviceName; |
| | | form.value.deviceModel = data.deviceModel; |
| | | form.value.supplierName = data.supplierName; |
| | | form.value.unit = data.unit; |
| | | form.value.number = data.number; |
| | | form.value.taxIncludingPriceUnit = data.taxIncludingPriceUnit; |
| | | form.value.taxIncludingPriceTotal = data.taxIncludingPriceTotal; |
| | | form.value.taxRate = data.taxRate; |
| | | form.value.unTaxIncludingPriceTotal = data.unTaxIncludingPriceTotal; |
| | | form.value.createTime = data.createTime; |
| | | } |
| | | } catch (e) { |
| | | showToast('è·å详æ
失败'); |
| | | } |
| | | }; |
| | | |
| | | // æ°å¦è®¡ç® |
| | | const mathNum = () => { |
| | | if (!form.value.taxIncludingPriceUnit) { |
| | | showToast("请è¾å
¥åä»·"); |
| | | return; |
| | | } |
| | | if (!form.value.number) { |
| | | showToast("请è¾å
¥æ°é"); |
| | | return; |
| | | } |
| | | form.value.taxIncludingPriceTotal = calculateTaxIncludeTotalPrice( |
| | | form.value.taxIncludingPriceUnit, |
| | | form.value.number |
| | | ); |
| | | if (form.value.taxRate) { |
| | | form.value.unTaxIncludingPriceTotal = calculateTaxExclusiveTotalPrice( |
| | | form.value.taxIncludingPriceTotal, |
| | | form.value.taxRate |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | // æ¸
é¤è¡¨åæ ¡éªç¶æ |
| | | const clearValidate = () => { |
| | | formRef.value?.clearValidate(); |
| | | }; |
| | | |
| | | // éç½®è¡¨åæ°æ®åæ ¡éªç¶æ |
| | | const resetForm = () => { |
| | | form.value = { |
| | | deviceName: undefined, |
| | | deviceModel: undefined, |
| | | supplierName: undefined, |
| | | unit: undefined, |
| | | number: undefined, |
| | | taxIncludingPriceUnit: undefined, |
| | | taxIncludingPriceTotal: undefined, |
| | | taxRate: undefined, |
| | | unTaxIncludingPriceTotal: undefined, |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }; |
| | | }; |
| | | |
| | | const resetFormAndValidate = () => { |
| | | resetForm(); |
| | | clearValidate(); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const sendForm = async () => { |
| | | try { |
| | | // æå¨éªè¯è¡¨å |
| | | await formRef.value?.validate(); |
| | | |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | |
| | | // åå¤æäº¤æ°æ®ï¼createTime å ä¸å½åæ¶åç§ |
| | | const submitData = { ...form.value }; |
| | | if (submitData.createTime && !submitData.createTime.includes(':')) { |
| | | // 妿 createTime åªå
嫿¥æï¼æ·»å å½åæ¶åç§ |
| | | submitData.createTime = submitData.createTime + ' ' + dayjs().format('HH:mm:ss'); |
| | | } |
| | | |
| | | const { code } = id |
| | | ? await editLedger({ id: id, ...submitData }) |
| | | : await addLedger(submitData); |
| | | |
| | | if (code == 200) { |
| | | showToast("æä½æå"); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast('表åéªè¯å¤±è´¥'); |
| | | } |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | |
| | | if (options.id) { |
| | | // ç¼è¾æ¨¡å¼ï¼è·å详æ
|
| | | loadForm(options.id); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | operationType.value = 'add'; |
| | | } |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | return options.id; |
| | | }; |
| | | |
| | | // æ¾ç¤ºç¨çéæ©å¨ |
| | | const showTaxRatePicker = () => { |
| | | showTaxRate.value = true; |
| | | }; |
| | | |
| | | // 确认ç¨çéæ© |
| | | const onTaxRateConfirm = ({ selectedValues, selectedOptions }) => { |
| | | form.value.taxRate = selectedOptions[0].value; |
| | | taxRatePickerValue.value = selectedValues; |
| | | showTaxRate.value = false; |
| | | mathNum(); // éæ°è®¡ç® |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = ({ selectedValues }) => { |
| | | // åªä¿åå¹´ææ¥ï¼ä¸å
嫿¶åç§ |
| | | form.value.createTime = selectedValues.join('-'); |
| | | currentDate.value = selectedValues; |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶è·ååæ° |
| | | getPageParams(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·ååæ° |
| | | getPageParams(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .ledger-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // ååºå¼è°æ´ |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="device-ledger"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="设å¤å°è´¦" @back="goBack" /> |
| | | |
| | | <!-- æç´¢åçéåºå --> |
| | | <view class="search-filter-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°" |
| | | v-model="searchKeyword" |
| | | confirm-type="search" |
| | | @confirm="getList" |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="getList"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 设å¤å°è´¦å表 --> |
| | | <view class="ledger-list" v-if="ledgerList.length > 0"> |
| | | <view v-for="(item, index) in ledgerList" :key="index"> |
| | | <view class="ledger-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">设å¤åç§°ï¼{{ item.deviceName }}</text> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | | <text class="detail-value">{{ item.deviceModel || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¾åºå</text> |
| | | <text class="detail-value">{{ item.supplierName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åä½</text> |
| | | <text class="detail-value">{{ item.unit || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ item.number || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å«ç¨åä»·</text> |
| | | <text class="detail-value highlight">{{ item.taxIncludingPriceUnit || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å«ç¨æ»ä»·</text> |
| | | <text class="detail-value highlight">{{ item.taxIncludingPriceTotal || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç¨ç</text> |
| | | <text class="detail-value">{{ item.taxRate || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¸å«ç¨æ»ä»·</text> |
| | | <text class="detail-value highlight">{{ item.unTaxIncludingPriceTotal || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥äºº</text> |
| | | <text class="detail-value">{{ item.createUser || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥æ¥æ</text> |
| | | <text class="detail-value">{{ item.createTime || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- æé®åºåï¼åè invoiceLedger çæ ·å¼ --> |
| | | <view class="action-buttons"> |
| | | <van-button |
| | | type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | @click="edit(item.id)" |
| | | > |
| | | ç¼è¾ |
| | | </van-button> |
| | | <van-button |
| | | type="danger" |
| | | size="small" |
| | | plain |
| | | class="action-btn" |
| | | @click="deleteRow(item.id)" |
| | | > |
| | | å é¤ |
| | | </van-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <text>ææ è®¾å¤å°è´¦æ°æ®</text> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" @click="add"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import PageHeader from '@/components/PageHeader.vue' |
| | | import { getLedgerPage, delLedger } from '@/api/equipmentManagement/ledger' |
| | | import useUserStore from "@/store/modules/user" |
| | | import { showToast } from 'vant'; |
| | | |
| | | const userStore = useUserStore() |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref('') |
| | | |
| | | // 设å¤å°è´¦æ°æ® |
| | | const ledgerList = ref([]) |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | // æ¥è¯¢å表ï¼current/size åºå®ä¼ -1ï¼ |
| | | const getList = () => { |
| | | const params = { |
| | | current: -1, |
| | | size: -1, |
| | | deviceName: searchKeyword.value || undefined, |
| | | } |
| | | getLedgerPage(params) |
| | | .then((res) => { |
| | | ledgerList.value = res.records || res.data?.records || [] |
| | | }) |
| | | .catch(() => { |
| | | showToast('è·åæ°æ®å¤±è´¥') |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢ - 跳转å°è¯¦æ
é¡µé¢ |
| | | const add = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/ledger/detail' |
| | | }) |
| | | } |
| | | |
| | | // ç¼è¾ - 跳转å°è¯¦æ
é¡µé¢ |
| | | const edit = (id) => { |
| | | if (!id) return |
| | | uni.navigateTo({ |
| | | url: `/pages/equipmentManagement/ledger/detail?id=${id}` |
| | | }) |
| | | } |
| | | |
| | | // å é¤ |
| | | const deleteRow = async (id) => { |
| | | if (!id) return |
| | | uni.showModal({ |
| | | title: 'æç¤º', |
| | | content: 'æ¤æä½å°æ°¸ä¹
å é¤è¯¥è®°å½, æ¯å¦ç»§ç»?', |
| | | success: async (res) => { |
| | | if (!res.confirm) return |
| | | try { |
| | | await delLedger(id) |
| | | showToast('å 餿å') |
| | | getList() |
| | | } catch (e) { |
| | | showToast('å é¤å¤±è´¥') |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | |
| | | onShow(() => { |
| | | getList() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .u-divider { |
| | | margin: 0 !important; |
| | | } |
| | | |
| | | .device-ledger { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | .search-filter-section { |
| | | padding: 10px 20px; |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .search-bar { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | background: #f5f5f5; |
| | | border-radius: 24px; |
| | | padding: 10px 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .search-text { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #333; |
| | | background: transparent; |
| | | border: none; |
| | | outline: none; |
| | | } |
| | | |
| | | .search-text::placeholder { |
| | | color: #999; |
| | | } |
| | | |
| | | .filter-button { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .ledger-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .ledger-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 16px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .document-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .item-id { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | |
| | | .detail-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .no-data { |
| | | padding: 40px 0; |
| | | text-align: center; |
| | | color: #999; |
| | | } |
| | | |
| | | // æé®æ ·å¼ï¼åè invoiceLedger |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 12px; |
| | | padding: 0 0 16px 0; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .action-btn { |
| | | flex: 1; |
| | | } |
| | | |
| | | .fab-button { |
| | | position: fixed; |
| | | bottom: calc(30px + env(safe-area-inset-bottom)); |
| | | right: 30px; |
| | | width: 56px; |
| | | height: 56px; |
| | | background: #2979ff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3); |
| | | z-index: 1000; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="repair-add"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="operationType === 'edit' ? 'ç¼è¾æ¥ä¿®' : 'æ°å¢æ¥ä¿®'" @back="goBack" /> |
| | | |
| | | <!-- 表åå
容 --> |
| | | <van-form @submit="sendForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <van-cell-group title="åºæ¬ä¿¡æ¯" inset> |
| | | <van-field |
| | | v-model="deviceNameText" |
| | | label="设å¤åç§°" |
| | | placeholder="è¯·éæ©è®¾å¤åç§°" |
| | | :rules="formRules.deviceLedgerId" |
| | | required |
| | | readonly |
| | | @click="showDevicePicker" |
| | | clearable |
| | | > |
| | | <template #right-icon> |
| | | <van-icon name="scan" @click.stop="startScan" class="scan-icon" /> |
| | | </template> |
| | | </van-field> |
| | | <van-field |
| | | v-model="form.deviceModel" |
| | | label="è§æ ¼åå·" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | readonly |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.repairTime" |
| | | label="æ¥ä¿®æ¥æ" |
| | | placeholder="è¯·éæ©æ¥ä¿®æ¥æ" |
| | | :rules="formRules.repairTime" |
| | | required |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.repairName" |
| | | label="æ¥ä¿®äºº" |
| | | placeholder="请è¾å
¥æ¥ä¿®äºº" |
| | | :rules="formRules.repairName" |
| | | required |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.remark" |
| | | label="æ
éç°è±¡" |
| | | type="textarea" |
| | | rows="3" |
| | | placeholder="请è¾å
¥æ
éç°è±¡" |
| | | :rules="formRules.remark" |
| | | required |
| | | clearable |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </van-cell-group> |
| | | |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <van-button class="cancel-btn" @click="goBack">åæ¶</van-button> |
| | | <van-button class="save-btn" native-type="submit" form-type="submit" :loading="loading">ä¿å</van-button> |
| | | </view> |
| | | </van-form> |
| | | |
| | | <!-- 设å¤éæ©å¨ --> |
| | | <van-popup v-model:show="showDevice" position="bottom"> |
| | | <van-picker |
| | | :model-value="devicePickerValue" |
| | | :columns="deviceColumns" |
| | | @confirm="onDeviceConfirm" |
| | | @cancel="showDevice = false" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <van-popup v-model:show="showDate" position="bottom"> |
| | | <van-date-picker |
| | | v-model="currentDate" |
| | | title="éæ©æ¥æ" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | /> |
| | | </van-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onUnmounted } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { getDeviceLedger } from '@/api/equipmentManagement/ledger'; |
| | | import { addRepair, editRepair, getRepairById } from '@/api/equipmentManagement/repair'; |
| | | import dayjs from "dayjs"; |
| | | import { showToast } from 'vant'; |
| | | |
| | | defineOptions({ |
| | | name: "è®¾å¤æ¥ä¿®è¡¨å", |
| | | }); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const operationType = ref('add'); |
| | | const loading = ref(false); |
| | | const showDevice = ref(false); |
| | | const devicePickerValue = ref([]); |
| | | const showDate = ref(false); |
| | | const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]); |
| | | |
| | | // 设å¤é项 |
| | | const deviceOptions = ref([]); |
| | | const deviceNameText = ref(''); |
| | | |
| | | // æ«ç ç¸å
³ç¶æ |
| | | const isScanning = ref(false); |
| | | const scanTimer = ref(null); |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | deviceLedgerId: [{ required: true, trigger: "change", message: "è¯·éæ©è®¾å¤åç§°" }], |
| | | repairTime: [{ required: true, trigger: "change", message: "è¯·éæ©æ¥ä¿®æ¥æ" }], |
| | | repairName: [{ required: true, trigger: "blur", message: "请è¾å
¥æ¥ä¿®äºº" }], |
| | | remark: [{ required: true, trigger: "blur", message: "请è¾å
¥æ
éç°è±¡" }], |
| | | }; |
| | | |
| | | // ä½¿ç¨ ref 声æè¡¨åæ°æ® |
| | | const form = ref({ |
| | | deviceLedgerId: undefined, // 设å¤ID |
| | | deviceModel: undefined, // è§æ ¼åå· |
| | | repairTime: dayjs().format("YYYY-MM-DD"), // æ¥ä¿®æ¥æ |
| | | repairName: undefined, // æ¥ä¿®äºº |
| | | remark: undefined, // æ
éç°è±¡ |
| | | }); |
| | | |
| | | // 设å¤éæ©å¨å |
| | | const deviceColumns = computed(() => { |
| | | return deviceOptions.value.map(item => ({ |
| | | text: item.deviceName, |
| | | value: item.id |
| | | })); |
| | | }); |
| | | |
| | | // å 载设å¤å表 |
| | | const loadDeviceName = async () => { |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (e) { |
| | | showToast('è·å设å¤å表失败'); |
| | | } |
| | | }; |
| | | |
| | | // 设置设å¤è§æ ¼åå· |
| | | const setDeviceModel = (id) => { |
| | | const option = deviceOptions.value.find((item) => item.id === id); |
| | | if (option) { |
| | | form.value.deviceModel = option.deviceModel; |
| | | deviceNameText.value = option.deviceName; |
| | | } |
| | | }; |
| | | |
| | | // å è½½è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const loadForm = async (id) => { |
| | | if (id) { |
| | | operationType.value = 'edit'; |
| | | try { |
| | | const { code, data } = await getRepairById(id); |
| | | if (code == 200) { |
| | | form.value.deviceLedgerId = data.deviceLedgerId; |
| | | form.value.deviceModel = data.deviceModel; |
| | | form.value.repairTime = data.repairTime; |
| | | form.value.repairName = data.repairName; |
| | | form.value.remark = data.remark; |
| | | // 设置设å¤åç§°æ¾ç¤º |
| | | const device = deviceOptions.value.find(item => item.id === data.deviceLedgerId); |
| | | if (device) { |
| | | deviceNameText.value = device.deviceName; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | showToast('è·å详æ
失败'); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | operationType.value = 'add'; |
| | | } |
| | | }; |
| | | |
| | | // æ¸
é¤è¡¨åæ ¡éªç¶æ |
| | | const clearValidate = () => { |
| | | formRef.value?.clearValidate(); |
| | | }; |
| | | |
| | | // éç½®è¡¨åæ°æ®åæ ¡éªç¶æ |
| | | const resetForm = () => { |
| | | form.value = { |
| | | deviceLedgerId: undefined, |
| | | deviceModel: undefined, |
| | | repairTime: dayjs().format("YYYY-MM-DD"), |
| | | repairName: undefined, |
| | | remark: undefined, |
| | | }; |
| | | deviceNameText.value = ''; |
| | | }; |
| | | |
| | | const resetFormAndValidate = () => { |
| | | resetForm(); |
| | | clearValidate(); |
| | | }; |
| | | |
| | | // æ«æäºç»´ç åè½ |
| | | const startScan = () => { |
| | | if (isScanning.value) { |
| | | showToast('æ£å¨æ«æä¸ï¼è¯·ç¨å...'); |
| | | return; |
| | | } |
| | | |
| | | // è°ç¨uni-appçæ«ç API |
| | | uni.scanCode({ |
| | | scanType: ['qrCode', 'barCode'], |
| | | success: (res) => { |
| | | handleScanResult(res.result); |
| | | }, |
| | | fail: (err) => { |
| | | console.error('æ«ç 失败:', err); |
| | | showToast('æ«ç 失败ï¼è¯·éè¯'); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å¤çæ«ç ç»æ |
| | | const handleScanResult = (scanResult) => { |
| | | if (!scanResult) { |
| | | showToast('æ«ç ç»æä¸ºç©º'); |
| | | return; |
| | | } |
| | | |
| | | isScanning.value = true; |
| | | showToast('æ«ç æåï¼3ç§åèªå¨å¡«å
设å¤ä¿¡æ¯'); |
| | | |
| | | // 3ç§åå¤çæ«ç ç»æ |
| | | scanTimer.value = setTimeout(() => { |
| | | processScanResult(scanResult); |
| | | isScanning.value = false; |
| | | }, 3000); |
| | | }; |
| | | |
| | | // å¤çæ«ç ç»æå¹¶å¹é
è®¾å¤ |
| | | const processScanResult = (scanResult) => { |
| | | // å¨è®¾å¤åè¡¨ä¸æ¥æ¾å¹é
çè®¾å¤ |
| | | // å设äºç»´ç å
容æ¯è®¾å¤åç§°æè®¾å¤ç¼å· |
| | | const matchedDevice = deviceOptions.value.find(device => |
| | | device.deviceName === scanResult || |
| | | device.deviceCode === scanResult || |
| | | device.id.toString() === scanResult |
| | | ); |
| | | |
| | | if (matchedDevice) { |
| | | // æ¾å°å¹é
ç设å¤ï¼èªå¨å¡«å
|
| | | form.value.deviceLedgerId = matchedDevice.id; |
| | | deviceNameText.value = matchedDevice.deviceName; |
| | | form.value.deviceModel = matchedDevice.deviceModel; |
| | | showToast('设å¤ä¿¡æ¯å·²èªå¨å¡«å
'); |
| | | } else { |
| | | // æªæ¾å°å¹é
çè®¾å¤ |
| | | showToast('æªæ¾å°å¹é
ç设å¤ï¼è¯·æå¨éæ©'); |
| | | } |
| | | }; |
| | | |
| | | // æ¾ç¤ºè®¾å¤éæ©å¨ |
| | | const showDevicePicker = () => { |
| | | showDevice.value = true; |
| | | }; |
| | | |
| | | // 确认设å¤éæ© |
| | | const onDeviceConfirm = ({ selectedValues, selectedOptions }) => { |
| | | form.value.deviceLedgerId = selectedOptions[0].value; |
| | | devicePickerValue.value = selectedValues; |
| | | showDevice.value = false; |
| | | setDeviceModel(selectedOptions[0].value); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = ({ selectedValues }) => { |
| | | form.value.repairTime = selectedValues.join('-'); |
| | | currentDate.value = selectedValues; |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶è·ååæ° |
| | | getPageParams(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·å设å¤å表ååæ° |
| | | loadDeviceName(); |
| | | getPageParams(); |
| | | }); |
| | | |
| | | // ç»ä»¶å¸è½½æ¶æ¸
ç宿¶å¨ |
| | | onUnmounted(() => { |
| | | if (scanTimer.value) { |
| | | clearTimeout(scanTimer.value); |
| | | } |
| | | }); |
| | | |
| | | // æäº¤è¡¨å |
| | | const sendForm = async () => { |
| | | try { |
| | | // æå¨éªè¯è¡¨å |
| | | await formRef.value?.validate(); |
| | | |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | |
| | | // åå¤æäº¤æ°æ® |
| | | const submitData = { ...form.value }; |
| | | |
| | | const { code } = id |
| | | ? await editRepair({ id: id, ...submitData }) |
| | | : await addRepair(submitData); |
| | | |
| | | if (code == 200) { |
| | | showToast(`${id ? "ç¼è¾" : "æ°å¢"}æ¥ä¿®æå`); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast('表åéªè¯å¤±è´¥'); |
| | | } |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | |
| | | // æ ¹æ®æ¯å¦æidåæ°æ¥å¤ææ¯æ°å¢è¿æ¯ç¼è¾ |
| | | if (options.id) { |
| | | // ç¼è¾æ¨¡å¼ï¼è·å详æ
|
| | | loadForm(options.id); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | loadForm(); |
| | | } |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | return options.id; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .repair-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // ååºå¼è°æ´ |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | |
| | | .scan-icon { |
| | | color: #1989fa; |
| | | font-size: 18px; |
| | | margin-left: 8px; |
| | | cursor: pointer; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="device-repair"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="è®¾å¤æ¥ä¿®" @back="goBack" /> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-filter-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°" |
| | | v-model="searchKeyword" |
| | | confirm-type="search" |
| | | @confirm="getList" |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="getList"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- è®¾å¤æ¥ä¿®å表 --> |
| | | <view class="repair-list" v-if="repairList.length > 0"> |
| | | <view v-for="(item, index) in repairList" :key="index"> |
| | | <view class="repair-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">设å¤åç§°ï¼{{ item.deviceName }}</text> |
| | | </view> |
| | | <view class="status-tag"> |
| | | <van-tag v-if="item.status === 1" type="success">å®ç»</van-tag> |
| | | <van-tag v-if="item.status === 0" type="danger">å¾
ç»´ä¿®</van-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | | <text class="detail-value">{{ item.deviceModel || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ¥ä¿®æ¥æ</text> |
| | | <text class="detail-value">{{ formatDate(item.repairTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ¥ä¿®äºº</text> |
| | | <text class="detail-value">{{ item.repairName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ
éç°è±¡</text> |
| | | <text class="detail-value">{{ item.remark || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">维修人</text> |
| | | <text class="detail-value">{{ item.maintenanceName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç»´ä¿®ç»æ</text> |
| | | <text class="detail-value">{{ item.maintenanceResult || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç»´ä¿®æ¥æ</text> |
| | | <text class="detail-value">{{ formatDate(item.maintenanceTime) || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- æé®åºå --> |
| | | <view class="action-buttons"> |
| | | <van-button |
| | | type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | @click="edit(item.id)" |
| | | > |
| | | ç¼è¾ |
| | | </van-button> |
| | | <van-button |
| | | type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.status === 1" |
| | | @click="addMaintain(item.id)" |
| | | > |
| | | æ°å¢ç»´ä¿® |
| | | </van-button> |
| | | <van-button |
| | | type="danger" |
| | | size="small" |
| | | plain |
| | | class="action-btn" |
| | | @click="delRepairByIds(item.id)" |
| | | > |
| | | å é¤ |
| | | </van-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <text>ææ è®¾å¤æ¥ä¿®æ°æ®</text> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°æ³¡æé® --> |
| | | <van-floating-bubble |
| | | axis="xy" |
| | | icon="plus" |
| | | @click="addRepair" |
| | | /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import PageHeader from '@/components/PageHeader.vue' |
| | | import { getRepairPage, delRepair } from '@/api/equipmentManagement/repair' |
| | | import useUserStore from "@/store/modules/user" |
| | | import { showToast } from 'vant'; |
| | | |
| | | const userStore = useUserStore() |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref('') |
| | | |
| | | // è®¾å¤æ¥ä¿®æ°æ® |
| | | const repairList = ref([]) |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | // æ ¼å¼åæ¥æ |
| | | const formatDate = (dateStr) => { |
| | | if (!dateStr) return '' |
| | | const date = new Date(dateStr) |
| | | const year = date.getFullYear() |
| | | const month = String(date.getMonth() + 1).padStart(2, '0') |
| | | const day = String(date.getDate()).padStart(2, '0') |
| | | return `${year}-${month}-${day}` |
| | | } |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | const params = { |
| | | current: -1, |
| | | size: -1, |
| | | deviceName: searchKeyword.value || undefined |
| | | } |
| | | getRepairPage(params) |
| | | .then((res) => { |
| | | repairList.value = res.records || res.data?.records || [] |
| | | }) |
| | | .catch(() => { |
| | | showToast('è·åæ°æ®å¤±è´¥') |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢ç»´ä¿® - 跳转å°ç»´ä¿®é¡µé¢ |
| | | const addMaintain = (id) => { |
| | | if (!id) { |
| | | showToast('åæ°é误') |
| | | return |
| | | } |
| | | uni.navigateTo({ |
| | | url: `/pages/equipmentManagement/repair/maintain?id=${id}` |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢æ¥ä¿® - è·³è½¬å°æ¥ä¿®é¡µé¢ |
| | | const addRepair = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/repair/add' |
| | | }) |
| | | } |
| | | |
| | | // ç¼è¾ - 跳转å°add页é¢ï¼éè¿idåºåæ°å¢è¿æ¯ç¼è¾ |
| | | const edit = (id) => { |
| | | if (!id) return |
| | | uni.navigateTo({ |
| | | url: `/pages/equipmentManagement/repair/add?id=${id}` |
| | | }) |
| | | } |
| | | |
| | | // å 餿¥ä¿®æ°æ® |
| | | const delRepairByIds = async (ids) => { |
| | | uni.showModal({ |
| | | title: 'è¦å', |
| | | content: '确认å 餿¥ä¿®æ°æ®, æ¤æä½ä¸å¯é?', |
| | | confirmText: 'ç¡®å®', |
| | | cancelText: 'åæ¶', |
| | | success: async (res) => { |
| | | if (!res.confirm) return |
| | | try { |
| | | const response = await delRepair(ids) |
| | | if (response.code === 200) { |
| | | showToast('å 餿å') |
| | | getList() |
| | | } else { |
| | | showToast('å é¤å¤±è´¥') |
| | | } |
| | | } catch (e) { |
| | | showToast('å é¤å¤±è´¥') |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | |
| | | onShow(() => { |
| | | getList() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .u-divider { |
| | | margin: 0 !important; |
| | | } |
| | | |
| | | .device-repair { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | .search-filter-section { |
| | | padding: 10px 20px; |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .search-bar { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | background: #f5f5f5; |
| | | border-radius: 24px; |
| | | padding: 10px 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .search-text { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #333; |
| | | background: transparent; |
| | | border: none; |
| | | outline: none; |
| | | } |
| | | |
| | | .search-text::placeholder { |
| | | color: #999; |
| | | } |
| | | |
| | | .filter-button { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .repair-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .repair-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 16px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .document-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .item-id { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .status-tag { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | |
| | | .detail-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .no-data { |
| | | padding: 40px 0; |
| | | text-align: center; |
| | | color: #999; |
| | | } |
| | | |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 8px; |
| | | padding: 0 0 16px 0; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .action-btn { |
| | | flex: 1; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="repair-maintain"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="æ°å¢ç»´ä¿®" @back="goBack" /> |
| | | |
| | | <!-- 表åå
容 --> |
| | | <van-form @submit="sendForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <van-cell-group title="维修信æ¯" inset> |
| | | <van-field |
| | | v-model="form.maintenanceName" |
| | | label="维修人" |
| | | placeholder="请è¾å
¥ç»´ä¿®äºº" |
| | | :rules="formRules.maintenanceName" |
| | | required |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.maintenanceResult" |
| | | label="ç»´ä¿®ç»æ" |
| | | type="textarea" |
| | | rows="3" |
| | | placeholder="请è¾å
¥ç»´ä¿®ç»æ" |
| | | :rules="formRules.maintenanceResult" |
| | | required |
| | | clearable |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | <van-field |
| | | v-model="form.maintenanceTime" |
| | | label="ç»´ä¿®æ¥æ" |
| | | placeholder="è¯·éæ©ç»´ä¿®æ¥æ" |
| | | :rules="formRules.maintenanceTime" |
| | | required |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable |
| | | /> |
| | | </van-cell-group> |
| | | |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <van-button class="cancel-btn" @click="goBack">åæ¶</van-button> |
| | | <van-button class="save-btn" native-type="submit" form-type="submit" :loading="loading">ä¿å</van-button> |
| | | </view> |
| | | </van-form> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <van-popup v-model:show="showDate" position="bottom"> |
| | | <van-date-picker |
| | | v-model="currentDate" |
| | | title="éæ©æ¥æ" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | /> |
| | | </van-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { addMaintain } from '@/api/equipmentManagement/repair'; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import dayjs from "dayjs"; |
| | | import { showToast } from 'vant'; |
| | | |
| | | defineOptions({ |
| | | name: "设å¤ç»´ä¿®è¡¨å", |
| | | }); |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const loading = ref(false); |
| | | const showDate = ref(false); |
| | | const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]); |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | maintenanceName: [{ required: true, trigger: "blur", message: "请è¾å
¥ç»´ä¿®äºº" }], |
| | | maintenanceResult: [{ required: true, trigger: "blur", message: "请è¾å
¥ç»´ä¿®ç»æ" }], |
| | | maintenanceTime: [{ required: true, trigger: "change", message: "è¯·éæ©ç»´ä¿®æ¥æ" }], |
| | | }; |
| | | |
| | | // ä½¿ç¨ ref 声æè¡¨åæ°æ® |
| | | const form = ref({ |
| | | maintenanceName: userStore.nickName || '', // é»è®¤ä½¿ç¨å½åç¨æ·æµç§° |
| | | maintenanceResult: undefined, // ç»´ä¿®ç»æ |
| | | maintenanceTime: dayjs().format("YYYY-MM-DD"), // ç»´ä¿®æ¥æï¼åªæ¾ç¤ºæ¥æï¼ |
| | | }); |
| | | |
| | | // æ¸
é¤è¡¨åæ ¡éªç¶æ |
| | | const clearValidate = () => { |
| | | // Vant4ä¸ä¸éè¦æå¨æ¸
é¤éªè¯ç¶æï¼éç½®è¡¨åæ¶ä¼èªå¨æ¸
é¤ |
| | | // formRef.value?.clearValidate(); // å é¤è¿è¡ |
| | | }; |
| | | |
| | | // éç½®è¡¨åæ°æ®åæ ¡éªç¶æ |
| | | const resetForm = () => { |
| | | form.value = { |
| | | maintenanceName: userStore.nickName || '', |
| | | maintenanceResult: undefined, |
| | | maintenanceTime: dayjs().format("YYYY-MM-DD"), |
| | | }; |
| | | }; |
| | | |
| | | const resetFormAndValidate = () => { |
| | | resetForm(); |
| | | // clearValidate(); // å é¤è¿è¡ï¼Vant4ä¼èªå¨å¤ç |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const sendForm = async () => { |
| | | try { |
| | | // 使ç¨Vant4çæ£ç¡®éªè¯æ¹å¼ |
| | | formRef.value?.validate().then(() => { |
| | | // éªè¯éè¿ |
| | | submitFormData(); |
| | | }).catch((errors) => { |
| | | // éªè¯å¤±è´¥ |
| | | showToast('请填å宿´ä¿¡æ¯'); |
| | | }); |
| | | } catch (e) { |
| | | showToast('表åéªè¯å¤±è´¥'); |
| | | } |
| | | }; |
| | | |
| | | // æäº¤è¡¨åæ°æ® |
| | | const submitFormData = async () => { |
| | | try { |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | |
| | | if (!id) { |
| | | showToast('åæ°é误'); |
| | | loading.value = false; |
| | | return; |
| | | } |
| | | |
| | | // åå¤æäº¤æ°æ®ï¼maintenanceTime å ä¸å½åæ¶åç§ |
| | | const submitData = { ...form.value }; |
| | | if (submitData.maintenanceTime && !submitData.maintenanceTime.includes(':')) { |
| | | // 妿 maintenanceTime åªå
嫿¥æï¼æ·»å å½åæ¶åç§ |
| | | submitData.maintenanceTime = submitData.maintenanceTime + ' ' + dayjs().format('HH:mm:ss'); |
| | | } |
| | | |
| | | const { code } = await addMaintain({ id: id, ...submitData }); |
| | | |
| | | if (code == 200) { |
| | | showToast('æ°å¢ç»´ä¿®æå'); |
| | | resetFormAndValidate(); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast('æä½å¤±è´¥'); |
| | | } |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | return options.id; |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = ({ selectedValues }) => { |
| | | // åªä¿åå¹´ææ¥ï¼ä¸å
嫿¶åç§ |
| | | form.value.maintenanceTime = selectedValues.join('-'); |
| | | currentDate.value = selectedValues; |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | // åå§åè¡¨åæ°æ® |
| | | const initForm = () => { |
| | | // 设置维修人为å½åç¨æ·æµç§° |
| | | form.value.maintenanceName = userStore.nickName || ''; |
| | | // 设置å½åæ¥æï¼åªå
å«å¹´ææ¥ï¼ |
| | | form.value.maintenanceTime = dayjs().format('YYYY-MM-DD'); |
| | | currentDate.value = [new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]; |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶åå§å表å |
| | | initForm(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶åå§å表å |
| | | initForm(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .repair-maintain { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // ååºå¼è°æ´ |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="upkeep-add"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="operationType === 'edit' ? 'ç¼è¾ä¿å
»è®¡å' : 'æ°å¢ä¿å
»è®¡å'" @back="goBack" /> |
| | | |
| | | <!-- 表åå
容 --> |
| | | <van-form @submit="sendForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <van-cell-group title="åºæ¬ä¿¡æ¯" inset> |
| | | <van-field |
| | | v-model="deviceNameText" |
| | | label="设å¤åç§°" |
| | | placeholder="è¯·éæ©è®¾å¤åç§°" |
| | | :rules="formRules.deviceLedgerId" |
| | | required |
| | | readonly |
| | | @click="showDevicePicker" |
| | | clearable |
| | | > |
| | | <template #right-icon> |
| | | <van-icon name="scan" @click.stop="startScan" class="scan-icon" /> |
| | | </template> |
| | | </van-field> |
| | | <van-field |
| | | v-model="form.deviceModel" |
| | | label="è§æ ¼åå·" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | readonly |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.maintenancePlanTime" |
| | | label="计åä¿å
»æ¥æ" |
| | | placeholder="è¯·éæ©è®¡åä¿å
»æ¥æ" |
| | | :rules="formRules.maintenancePlanTime" |
| | | required |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable |
| | | /> |
| | | </van-cell-group> |
| | | |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <van-button class="cancel-btn" @click="goBack">åæ¶</van-button> |
| | | <van-button class="save-btn" native-type="submit" form-type="submit" :loading="loading">ä¿å</van-button> |
| | | </view> |
| | | </van-form> |
| | | |
| | | <!-- 设å¤éæ©å¨ --> |
| | | <van-popup v-model:show="showDevice" position="bottom"> |
| | | <van-picker |
| | | :model-value="devicePickerValue" |
| | | :columns="deviceColumns" |
| | | @confirm="onDeviceConfirm" |
| | | @cancel="showDevice = false" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <van-popup v-model:show="showDate" position="bottom"> |
| | | <van-date-picker |
| | | v-model="currentDate" |
| | | title="éæ©æ¥æ" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | /> |
| | | </van-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onUnmounted } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { getDeviceLedger } from '@/api/equipmentManagement/ledger'; |
| | | import { addUpkeep, editUpkeep, getUpkeepById } from '@/api/equipmentManagement/upkeep'; |
| | | import dayjs from "dayjs"; |
| | | import { showToast } from 'vant'; |
| | | |
| | | defineOptions({ |
| | | name: "设å¤ä¿å
»è®¡å表å", |
| | | }); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const operationType = ref('add'); |
| | | const loading = ref(false); |
| | | const showDevice = ref(false); |
| | | const devicePickerValue = ref([]); |
| | | const showDate = ref(false); |
| | | const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]); |
| | | |
| | | // 设å¤é项 |
| | | const deviceOptions = ref([]); |
| | | const deviceNameText = ref(''); |
| | | |
| | | // æ«ç ç¸å
³ç¶æ |
| | | const isScanning = ref(false); |
| | | const scanTimer = ref(null); |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | deviceLedgerId: [{ required: true, trigger: "change", message: "è¯·éæ©è®¾å¤åç§°" }], |
| | | maintenancePlanTime: [{ required: true, trigger: "change", message: "è¯·éæ©è®¡åä¿å
»æ¥æ" }], |
| | | }; |
| | | |
| | | // ä½¿ç¨ ref 声æè¡¨åæ°æ® |
| | | const form = ref({ |
| | | deviceLedgerId: undefined, // 设å¤ID |
| | | deviceModel: undefined, // è§æ ¼åå· |
| | | maintenancePlanTime: dayjs().format("YYYY-MM-DD"), // 计åä¿å
»æ¥æ |
| | | }); |
| | | |
| | | // 设å¤éæ©å¨å |
| | | const deviceColumns = computed(() => { |
| | | return deviceOptions.value.map(item => ({ |
| | | text: item.deviceName, |
| | | value: item.id |
| | | })); |
| | | }); |
| | | |
| | | // å 载设å¤å表 |
| | | const loadDeviceName = async () => { |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (e) { |
| | | showToast('è·å设å¤å表失败'); |
| | | } |
| | | }; |
| | | |
| | | // 设置设å¤è§æ ¼åå· |
| | | const setDeviceModel = (id) => { |
| | | const option = deviceOptions.value.find((item) => item.id === id); |
| | | if (option) { |
| | | form.value.deviceModel = option.deviceModel; |
| | | deviceNameText.value = option.deviceName; |
| | | } |
| | | }; |
| | | |
| | | // å è½½è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const loadForm = async (id) => { |
| | | if (id) { |
| | | operationType.value = 'edit'; |
| | | try { |
| | | const { code, data } = await getUpkeepById(id); |
| | | if (code == 200) { |
| | | form.value.deviceLedgerId = data.deviceLedgerId; |
| | | form.value.deviceModel = data.deviceModel; |
| | | form.value.maintenancePlanTime = dayjs(data.maintenancePlanTime).format("YYYY-MM-DD"); |
| | | // 设置设å¤åç§°æ¾ç¤º |
| | | const device = deviceOptions.value.find(item => item.id === data.deviceLedgerId); |
| | | if (device) { |
| | | deviceNameText.value = device.deviceName; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | showToast('è·å详æ
失败'); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | operationType.value = 'add'; |
| | | } |
| | | }; |
| | | |
| | | // æ¸
é¤è¡¨åæ ¡éªç¶æ |
| | | const clearValidate = () => { |
| | | formRef.value?.clearValidate(); |
| | | }; |
| | | |
| | | // éç½®è¡¨åæ°æ®åæ ¡éªç¶æ |
| | | const resetForm = () => { |
| | | form.value = { |
| | | deviceLedgerId: undefined, |
| | | deviceModel: undefined, |
| | | maintenancePlanTime: dayjs().format("YYYY-MM-DD"), |
| | | }; |
| | | deviceNameText.value = ''; |
| | | }; |
| | | |
| | | const resetFormAndValidate = () => { |
| | | resetForm(); |
| | | clearValidate(); |
| | | }; |
| | | |
| | | // æ«æäºç»´ç åè½ |
| | | const startScan = () => { |
| | | if (isScanning.value) { |
| | | showToast('æ£å¨æ«æä¸ï¼è¯·ç¨å...'); |
| | | return; |
| | | } |
| | | |
| | | // è°ç¨uni-appçæ«ç API |
| | | uni.scanCode({ |
| | | scanType: ['qrCode', 'barCode'], |
| | | success: (res) => { |
| | | handleScanResult(res.result); |
| | | }, |
| | | fail: (err) => { |
| | | console.error('æ«ç 失败:', err); |
| | | showToast('æ«ç 失败ï¼è¯·éè¯'); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å¤çæ«ç ç»æ |
| | | const handleScanResult = (scanResult) => { |
| | | if (!scanResult) { |
| | | showToast('æ«ç ç»æä¸ºç©º'); |
| | | return; |
| | | } |
| | | |
| | | isScanning.value = true; |
| | | showToast('æ«ç æåï¼3ç§åèªå¨å¡«å
设å¤ä¿¡æ¯'); |
| | | |
| | | // 3ç§åå¤çæ«ç ç»æ |
| | | scanTimer.value = setTimeout(() => { |
| | | processScanResult(scanResult); |
| | | isScanning.value = false; |
| | | }, 3000); |
| | | }; |
| | | |
| | | // å¤çæ«ç ç»æå¹¶å¹é
è®¾å¤ |
| | | const processScanResult = (scanResult) => { |
| | | // å¨è®¾å¤åè¡¨ä¸æ¥æ¾å¹é
çè®¾å¤ |
| | | // å设äºç»´ç å
容æ¯è®¾å¤åç§°æè®¾å¤ç¼å· |
| | | const matchedDevice = deviceOptions.value.find(device => |
| | | device.deviceName === scanResult || |
| | | device.deviceCode === scanResult || |
| | | device.id.toString() === scanResult |
| | | ); |
| | | |
| | | if (matchedDevice) { |
| | | // æ¾å°å¹é
ç设å¤ï¼èªå¨å¡«å
|
| | | form.value.deviceLedgerId = matchedDevice.id; |
| | | deviceNameText.value = matchedDevice.deviceName; |
| | | form.value.deviceModel = matchedDevice.deviceModel; |
| | | showToast('设å¤ä¿¡æ¯å·²èªå¨å¡«å
'); |
| | | } else { |
| | | // æªæ¾å°å¹é
çè®¾å¤ |
| | | showToast('æªæ¾å°å¹é
ç设å¤ï¼è¯·æå¨éæ©'); |
| | | } |
| | | }; |
| | | |
| | | // æ¾ç¤ºè®¾å¤éæ©å¨ |
| | | const showDevicePicker = () => { |
| | | showDevice.value = true; |
| | | }; |
| | | |
| | | // 确认设å¤éæ© |
| | | const onDeviceConfirm = ({ selectedValues, selectedOptions }) => { |
| | | form.value.deviceLedgerId = selectedOptions[0].value; |
| | | devicePickerValue.value = selectedValues; |
| | | showDevice.value = false; |
| | | setDeviceModel(selectedOptions[0].value); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = ({ selectedValues }) => { |
| | | form.value.maintenancePlanTime = selectedValues.join('-'); |
| | | currentDate.value = selectedValues; |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶è·ååæ° |
| | | getPageParams(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·å设å¤å表ååæ° |
| | | loadDeviceName(); |
| | | getPageParams(); |
| | | }); |
| | | |
| | | // ç»ä»¶å¸è½½æ¶æ¸
ç宿¶å¨ |
| | | onUnmounted(() => { |
| | | if (scanTimer.value) { |
| | | clearTimeout(scanTimer.value); |
| | | } |
| | | }); |
| | | |
| | | // æäº¤è¡¨å |
| | | const sendForm = async () => { |
| | | try { |
| | | // æå¨éªè¯è¡¨å |
| | | await formRef.value?.validate(); |
| | | |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | |
| | | // åå¤æäº¤æ°æ® |
| | | const submitData = { ...form.value }; |
| | | // ç¡®ä¿æ¥ææ ¼å¼æ£ç¡® |
| | | if (submitData.maintenancePlanTime && !submitData.maintenancePlanTime.includes(':')) { |
| | | submitData.maintenancePlanTime = submitData.maintenancePlanTime + ' 00:00:00'; |
| | | } |
| | | |
| | | const { code } = id |
| | | ? await editUpkeep({ id: id, ...submitData }) |
| | | : await addUpkeep(submitData); |
| | | |
| | | if (code == 200) { |
| | | showToast(`${id ? "ç¼è¾" : "æ°å¢"}计åæå`); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast('表åéªè¯å¤±è´¥'); |
| | | } |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | |
| | | // æ ¹æ®æ¯å¦æidåæ°æ¥å¤ææ¯æ°å¢è¿æ¯ç¼è¾ |
| | | if (options.id) { |
| | | // ç¼è¾æ¨¡å¼ï¼è·å详æ
|
| | | loadForm(options.id); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | loadForm(); |
| | | } |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | return options.id; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .upkeep-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // ååºå¼è°æ´ |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | |
| | | .scan-icon { |
| | | color: #1989fa; |
| | | font-size: 18px; |
| | | margin-left: 8px; |
| | | cursor: pointer; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="device-upkeep"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="设å¤ä¿å
»" @back="goBack" /> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-filter-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°" |
| | | v-model="searchKeyword" |
| | | confirm-type="search" |
| | | @confirm="getList" |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="getList"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 设å¤ä¿å
»å表 --> |
| | | <view class="upkeep-list" v-if="upkeepList.length > 0"> |
| | | <view v-for="(item, index) in upkeepList" :key="index"> |
| | | <view class="upkeep-item" @click="toggleSelection(item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">设å¤åç§°ï¼{{ item.deviceName }}</text> |
| | | </view> |
| | | <view class="status-tag"> |
| | | <van-tag v-if="item.status === 1" type="success">å®ç»</van-tag> |
| | | <van-tag v-if="item.status === 0" type="danger">å¾
ä¿å
»</van-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | | <text class="detail-value">{{ item.deviceModel || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">计åä¿å
»æ¥æ</text> |
| | | <text class="detail-value">{{ formatDate(item.maintenancePlanTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥äºº</text> |
| | | <text class="detail-value">{{ item.createUserName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å½å
¥æ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(item.createTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å®é
ä¿å
»äºº</text> |
| | | <text class="detail-value">{{ item.maintenanceActuallyName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å®é
ä¿å
»æ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(item.maintenanceActuallyTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¿å
ȍȾ</text> |
| | | <view class="detail-value"> |
| | | <van-tag v-if="item.maintenanceResult === 1" type="success"> |
| | | å®å¥½ |
| | | </van-tag> |
| | | <van-tag v-if="item.maintenanceResult === 0" type="danger"> |
| | | ç»´ä¿® |
| | | </van-tag> |
| | | <text v-if="item.maintenanceResult === undefined || item.maintenanceResult === null">-</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- æé®åºå --> |
| | | <view class="action-buttons"> |
| | | <van-button |
| | | type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="edit(item.id)" |
| | | > |
| | | ç¼è¾ |
| | | </van-button> |
| | | <van-button |
| | | type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.status === 1" |
| | | @click.stop="addMaintain(item.id)" |
| | | > |
| | | ä¿å
» |
| | | </van-button> |
| | | <van-button |
| | | type="danger" |
| | | size="small" |
| | | plain |
| | | class="action-btn" |
| | | @click.stop="delUpkeepByIds(item.id)" |
| | | > |
| | | å é¤ |
| | | </van-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <text>ææ è®¾å¤ä¿å
»æ°æ®</text> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°æ³¡æé® --> |
| | | <van-floating-bubble |
| | | axis="xy" |
| | | icon="plus" |
| | | @click="addPlan" |
| | | /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import PageHeader from '@/components/PageHeader.vue' |
| | | import { getUpkeepPage, delUpkeep } from '@/api/equipmentManagement/upkeep' |
| | | import useUserStore from "@/store/modules/user" |
| | | import { showToast } from 'vant'; |
| | | import dayjs from "dayjs" |
| | | |
| | | const userStore = useUserStore() |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref('') |
| | | |
| | | // 设å¤ä¿å
»æ°æ® |
| | | const upkeepList = ref([]) |
| | | |
| | | // å¤éå表 |
| | | const multipleList = ref([]) |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | // æ ¼å¼åæ¥æ |
| | | const formatDate = (dateStr) => { |
| | | if (!dateStr) return '' |
| | | return dayjs(dateStr).format("YYYY-MM-DD") |
| | | } |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = (dateStr) => { |
| | | if (!dateStr) return '' |
| | | return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss") |
| | | } |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | const params = { |
| | | current: -1, |
| | | size: -1, |
| | | deviceName: searchKeyword.value || undefined |
| | | } |
| | | getUpkeepPage(params) |
| | | .then((res) => { |
| | | // 妿res.data䏿¯æ°ç»ï¼è®¾ç½®ä¸ºç©ºæ°ç» |
| | | upkeepList.value = res.records || res.data?.records || [] |
| | | }) |
| | | .catch(() => { |
| | | showToast('è·åæ°æ®å¤±è´¥') |
| | | }) |
| | | } |
| | | |
| | | // åæ¢éæ©ç¶æ |
| | | const toggleSelection = (item) => { |
| | | const index = multipleList.value.findIndex(selected => selected.id === item.id) |
| | | if (index > -1) { |
| | | multipleList.value.splice(index, 1) |
| | | } else { |
| | | multipleList.value.push(item) |
| | | } |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦å·²éæ© |
| | | const isSelected = (item) => { |
| | | return multipleList.value.some(selected => selected.id === item.id) |
| | | } |
| | | |
| | | // æ°å¢ä¿å
» - 跳转å°ä¿å
»é¡µé¢ |
| | | const addMaintain = (id) => { |
| | | if (!id && multipleList.value.length !== 1) { |
| | | showToast('è¯·éæ©ä¸æ¡è®°å½') |
| | | return |
| | | } |
| | | const targetId = id || multipleList.value[0].id |
| | | uni.navigateTo({ |
| | | url: `/pages/equipmentManagement/upkeep/maintain?id=${targetId}` |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢è®¡å - è·³è½¬å°æ°å¢é¡µé¢ |
| | | const addPlan = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/upkeep/add' |
| | | }) |
| | | } |
| | | |
| | | // ç¼è¾ - 跳转å°add页é¢ï¼éè¿idåºåæ°å¢è¿æ¯ç¼è¾ |
| | | const edit = (id) => { |
| | | if (!id) return |
| | | uni.navigateTo({ |
| | | url: `/pages/equipmentManagement/upkeep/add?id=${id}` |
| | | }) |
| | | } |
| | | |
| | | // å é¤ä¿å
»æ°æ® |
| | | const delUpkeepByIds = async (ids) => { |
| | | const deleteIds = Array.isArray(ids) ? ids : [ids] |
| | | if (deleteIds.length === 0) { |
| | | showToast('è¯·éæ©è¦å é¤çè®°å½') |
| | | return |
| | | } |
| | | |
| | | uni.showModal({ |
| | | title: 'è¦å', |
| | | content: '确认å é¤ä¿å
»æ°æ®, æ¤æä½ä¸å¯é?', |
| | | confirmText: 'ç¡®å®', |
| | | cancelText: 'åæ¶', |
| | | success: async (res) => { |
| | | if (!res.confirm) return |
| | | try { |
| | | // é个å é¤ |
| | | for (const id of deleteIds) { |
| | | const response = await delUpkeep(id) |
| | | if (response.code !== 200) { |
| | | showToast('å é¤å¤±è´¥') |
| | | return |
| | | } |
| | | } |
| | | showToast('å 餿å') |
| | | multipleList.value = [] |
| | | getList() |
| | | } catch (e) { |
| | | showToast('å é¤å¤±è´¥') |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | |
| | | onShow(() => { |
| | | getList() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .u-divider { |
| | | margin: 0 !important; |
| | | } |
| | | |
| | | .device-upkeep { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | .search-filter-section { |
| | | padding: 10px 20px; |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .search-bar { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | background: #f5f5f5; |
| | | border-radius: 24px; |
| | | padding: 10px 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .search-text { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #333; |
| | | background: transparent; |
| | | border: none; |
| | | outline: none; |
| | | } |
| | | |
| | | .search-text::placeholder { |
| | | color: #999; |
| | | } |
| | | |
| | | .filter-button { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .action-section { |
| | | padding: 10px 20px; |
| | | background: #ffffff; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 8px; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .upkeep-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .upkeep-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 16px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .checkbox-wrapper { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .document-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .item-id { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .status-tag { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | } |
| | | |
| | | .detail-value.highlight { |
| | | color: #2979ff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .no-data { |
| | | padding: 40px 0; |
| | | text-align: center; |
| | | color: #999; |
| | | } |
| | | |
| | | .upkeep-item .action-buttons { |
| | | display: flex; |
| | | gap: 8px; |
| | | padding: 0 0 16px 0; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .action-btn { |
| | | flex: 1; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="upkeep-maintain"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="æ°å¢ä¿å
»" @back="goBack" /> |
| | | |
| | | <!-- 表åå
容 --> |
| | | <van-form @submit="sendForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <van-cell-group title="ä¿å
»ä¿¡æ¯" inset> |
| | | <van-field |
| | | v-model="form.maintenanceActuallyName" |
| | | label="å®é
ä¿å
»äºº" |
| | | placeholder="请è¾å
¥å®é
ä¿å
»äºº" |
| | | :rules="formRules.maintenanceActuallyName" |
| | | required |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="form.maintenanceActuallyTime" |
| | | label="å®é
ä¿å
»æ¥æ" |
| | | placeholder="è¯·éæ©å®é
ä¿å
»æ¥æ" |
| | | :rules="formRules.maintenanceActuallyTime" |
| | | required |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable |
| | | /> |
| | | <van-field |
| | | v-model="maintenanceResultText" |
| | | label="ä¿å
ȍȾ" |
| | | placeholder="è¯·éæ©ä¿å
ȍȾ" |
| | | :rules="formRules.maintenanceResult" |
| | | required |
| | | readonly |
| | | @click="showResultPicker" |
| | | clearable |
| | | /> |
| | | </van-cell-group> |
| | | |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <van-button class="cancel-btn" @click="goBack">åæ¶</van-button> |
| | | <van-button class="save-btn" native-type="submit" form-type="submit" :loading="loading">ä¿å</van-button> |
| | | </view> |
| | | </van-form> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <van-popup v-model:show="showDate" position="bottom"> |
| | | <van-date-picker |
| | | v-model="currentDate" |
| | | title="éæ©æ¥æ" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | /> |
| | | </van-popup> |
| | | |
| | | <!-- ä¿å
»ç»æéæ©å¨ --> |
| | | <van-popup v-model:show="showResult" position="bottom"> |
| | | <van-picker |
| | | :model-value="resultPickerValue" |
| | | :columns="resultColumns" |
| | | @confirm="onResultConfirm" |
| | | @cancel="showResult = false" |
| | | /> |
| | | </van-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { addMaintenance } from '@/api/equipmentManagement/upkeep'; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import dayjs from "dayjs"; |
| | | import { showToast } from 'vant'; |
| | | |
| | | defineOptions({ |
| | | name: "设å¤ä¿å
»è¡¨å", |
| | | }); |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const loading = ref(false); |
| | | const showDate = ref(false); |
| | | const showResult = ref(false); |
| | | const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]); |
| | | const resultPickerValue = ref([]); |
| | | const maintenanceResultText = ref(''); |
| | | |
| | | // ä¿å
»ç»æé项 |
| | | const resultColumns = [ |
| | | { text: 'å®å¥½', value: 1 }, |
| | | { text: 'ç»´ä¿®', value: 0 } |
| | | ]; |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | maintenanceActuallyName: [{ required: true, trigger: "blur", message: "请è¾å
¥å®é
ä¿å
»äºº" }], |
| | | maintenanceActuallyTime: [{ required: true, trigger: "change", message: "è¯·éæ©å®é
ä¿å
»æ¥æ" }], |
| | | maintenanceResult: [{ required: true, trigger: "change", message: "è¯·éæ©ä¿å
ȍȾ" }], |
| | | }; |
| | | |
| | | // ä½¿ç¨ ref 声æè¡¨åæ°æ® |
| | | const form = ref({ |
| | | maintenanceActuallyName: userStore.nickName || '', // é»è®¤ä½¿ç¨å½åç¨æ·æµç§° |
| | | maintenanceResult: undefined, // ä¿å
ȍȾ |
| | | maintenanceActuallyTime: dayjs().format("YYYY-MM-DD"), // å®é
ä¿å
»æ¥æï¼åªæ¾ç¤ºæ¥æï¼ |
| | | }); |
| | | |
| | | // æ¸
é¤è¡¨åæ ¡éªç¶æ |
| | | const clearValidate = () => { |
| | | // Vant4ä¸ä¸éè¦æå¨æ¸
é¤éªè¯ç¶æï¼éç½®è¡¨åæ¶ä¼èªå¨æ¸
é¤ |
| | | // formRef.value?.clearValidate(); // å é¤è¿è¡ |
| | | }; |
| | | |
| | | // éç½®è¡¨åæ°æ®åæ ¡éªç¶æ |
| | | const resetForm = () => { |
| | | form.value = { |
| | | maintenanceActuallyName: userStore.nickName || '', |
| | | maintenanceResult: undefined, |
| | | maintenanceActuallyTime: dayjs().format("YYYY-MM-DD"), |
| | | }; |
| | | maintenanceResultText.value = ''; |
| | | }; |
| | | |
| | | const resetFormAndValidate = () => { |
| | | resetForm(); |
| | | // clearValidate(); // å é¤è¿è¡ï¼Vant4ä¼èªå¨å¤ç |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const sendForm = async () => { |
| | | try { |
| | | // 使ç¨Vant4çæ£ç¡®éªè¯æ¹å¼ |
| | | formRef.value?.validate().then(() => { |
| | | // éªè¯éè¿ |
| | | submitFormData(); |
| | | }).catch((errors) => { |
| | | // éªè¯å¤±è´¥ |
| | | showToast('请填å宿´ä¿¡æ¯'); |
| | | }); |
| | | } catch (e) { |
| | | showToast('表åéªè¯å¤±è´¥'); |
| | | } |
| | | }; |
| | | |
| | | // æäº¤è¡¨åæ°æ® |
| | | const submitFormData = async () => { |
| | | try { |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | |
| | | if (!id) { |
| | | showToast('åæ°é误'); |
| | | loading.value = false; |
| | | return; |
| | | } |
| | | |
| | | // åå¤æäº¤æ°æ®ï¼maintenanceActuallyTime å ä¸å½åæ¶åç§ |
| | | const submitData = { ...form.value }; |
| | | if (submitData.maintenanceActuallyTime && !submitData.maintenanceActuallyTime.includes(':')) { |
| | | // 妿 maintenanceActuallyTime åªå
嫿¥æï¼æ·»å å½åæ¶åç§ |
| | | submitData.maintenanceActuallyTime = submitData.maintenanceActuallyTime + ' ' + dayjs().format('HH:mm:ss'); |
| | | } |
| | | |
| | | const { code } = await addMaintenance({ id: id, ...submitData }); |
| | | |
| | | if (code == 200) { |
| | | showToast('æ°å¢ä¿å
»æå'); |
| | | resetFormAndValidate(); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast('æä½å¤±è´¥'); |
| | | } |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | return options.id; |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = ({ selectedValues }) => { |
| | | // åªä¿åå¹´ææ¥ï¼ä¸å
嫿¶åç§ |
| | | form.value.maintenanceActuallyTime = selectedValues.join('-'); |
| | | currentDate.value = selectedValues; |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | // æ¾ç¤ºä¿å
»ç»æéæ©å¨ |
| | | const showResultPicker = () => { |
| | | showResult.value = true; |
| | | }; |
| | | |
| | | // 确认ä¿å
»ç»æéæ© |
| | | const onResultConfirm = ({ selectedValues, selectedOptions }) => { |
| | | form.value.maintenanceResult = selectedOptions[0].value; |
| | | maintenanceResultText.value = selectedOptions[0].text; |
| | | resultPickerValue.value = selectedValues; |
| | | showResult.value = false; |
| | | }; |
| | | |
| | | // åå§åè¡¨åæ°æ® |
| | | const initForm = () => { |
| | | // 设置ä¿å
»äººä¸ºå½åç¨æ·æµç§° |
| | | form.value.maintenanceActuallyName = userStore.nickName || ''; |
| | | // 设置å½åæ¥æï¼åªå
å«å¹´ææ¥ï¼ |
| | | form.value.maintenanceActuallyTime = dayjs().format('YYYY-MM-DD'); |
| | | currentDate.value = [new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]; |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶åå§å表å |
| | | initForm(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶åå§å表å |
| | | initForm(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .upkeep-maintain { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // ååºå¼è°æ´ |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | </style> |
| | |
| | | <view class="hero-section"> |
| | | <view class="bg-img"> |
| | | <view class="hero-content"> |
| | | <text class="hero-title">产ååºå管çç³»ç»</text> |
| | | <text class="hero-subtitle">髿ã便æ·çä¸å¡ç®¡çå
¥å£</text> |
| | | <text class="hero-title"></text> |
| | | <text class="hero-subtitle"></text> |
| | | </view> |
| | | <view class="hero-wave"></view> |
| | | </view> |
| | |
| | | url: '/pages/cooperativeOffice/collaborativeApproval/index' |
| | | }); |
| | | break; |
| | | case 'å®¢æ·æè®¿': |
| | | case 'å®¢æ·æè®¿': |
| | | uni.navigateTo({ |
| | | url: '/pages/cooperativeOffice/clientVisit/index' |
| | | }); |
| | | break; |
| | | case '设å¤å°è´¦': |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/ledger/index' |
| | | }); |
| | | break; |
| | | case 'è®¾å¤æ¥ä¿®': |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/repair/index' |
| | | }); |
| | | break; |
| | | case '设å¤ä¿å
»': |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/upkeep/index' |
| | | }); |
| | | break; |
| | | default: |
| | |
| | | .bg-img { |
| | | width: 100%; |
| | | height: 8.75rem; |
| | | background: linear-gradient(135deg, #2979ff 0%, #1565c0 100%); |
| | | background-image: url("../static/images/banner/view-background.png"); |
| | | background-size: cover; |
| | | border-radius: 0.75rem; |
| | | position: relative; |
| | | overflow: hidden; |