已添加11个文件
已修改17个文件
已删除2个文件
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | export function approveProcessListPage(query) { |
| | | return request({ |
| | | url: '/approveProcess/list', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | return request({ |
| | | url: "/approveProcess/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | export function getDept(query) { |
| | | return request({ |
| | | url: '/approveProcess/getDept', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | return request({ |
| | | url: "/approveProcess/getDept", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | export function approveProcessGetInfo(query) { |
| | | return request({ |
| | | url: '/approveProcess/get', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | return request({ |
| | | url: "/approveProcess/get", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ°å¢å®¡æ¹æµç¨ |
| | | export function approveProcessAdd(query) { |
| | | return request({ |
| | | url: '/approveProcess/add', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | return request({ |
| | | url: "/approveProcess/add", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹å®¡æ¹æµç¨ |
| | | export function approveProcessUpdate(query) { |
| | | return request({ |
| | | url: '/approveProcess/update', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | return request({ |
| | | url: "/approveProcess/update", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // æäº¤å®¡æ¹ |
| | | export function updateApproveNode(query) { |
| | | return request({ |
| | | url: '/approveNode/updateApproveNode', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | return request({ |
| | | url: "/approveNode/updateApproveNode", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // å é¤å®¡æ¹æµç¨ |
| | | export function approveProcessDelete(query) { |
| | | return request({ |
| | | url: '/approveProcess/deleteIds', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | return request({ |
| | | url: "/approveProcess/deleteIds", |
| | | method: "delete", |
| | | data: query, |
| | | }); |
| | | } |
| | | // æ¥è¯¢å®¡æ¹æµç¨ |
| | | export function approveProcessDetails(query) { |
| | | return request({ |
| | | url: '/approveNode/details/' + query, |
| | | method: 'get', |
| | | }) |
| | | } |
| | | return request({ |
| | | url: "/approveNode/details/" + query, |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | // 审æ¹è¯¦æ
|
| | | export function getDeliveryDetailByShippingNo(query) { |
| | | return request({ |
| | | url: "/shippingInfo/getDateilByShippingNo", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | |
| | | }); |
| | | }; |
| | | |
| | | // å页æ¥è¯¢èååºåè®°å½å表ï¼å
å«ååä¿¡æ¯ï¼ |
| | | export const getStockInventoryListPageCombined = (params) => { |
| | | return request({ |
| | | url: "/stockInventory/pageListCombinedStockInventory", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // å建åºåè®°å½ |
| | | export const createStockInventory = (params) => { |
| | | return request({ |
| | |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | // æ¥è¯¢éè´è¯¦æ
|
| | | export function getPurchaseByCode(query) { |
| | | return request({ |
| | | url: "/purchase/ledger/getPurchaseByCode", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function approveProcessGetInfo(query) { |
| | | return request({ |
| | | url: '/approveProcess/get', |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="common-upload"> |
| | | <u-upload |
| | | :fileList="internalFileList" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | :name="name" |
| | | :multiple="multiple" |
| | | :maxCount="maxCount" |
| | | :accept="accept" |
| | | :disabled="disabled" |
| | | ></u-upload> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, watch, onMounted } from 'vue'; |
| | | import { getToken } from "@/utils/auth"; |
| | | import config from "@/config"; |
| | | |
| | | const props = defineProps({ |
| | | // ç¶ç»ä»¶ä¼ å
¥çæä»¶å表ï¼å¯¹åºå端åå¨ç对象åè¡¨ï¼ |
| | | modelValue: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // æå¤§ä¸ä¼ æ°é |
| | | maxCount: { |
| | | type: Number, |
| | | default: 9 |
| | | }, |
| | | // æ¯å¦æ¯æå¤é |
| | | multiple: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // æ¥åçæä»¶ç±»å |
| | | accept: { |
| | | type: String, |
| | | default: 'image' |
| | | }, |
| | | // ä¸ä¼ æ¥å£å¯¹åºçåæ°å |
| | | name: { |
| | | type: String, |
| | | default: 'file' |
| | | }, |
| | | // æ¯å¦ç¦ç¨ |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }); |
| | | |
| | | const emit = defineEmits(['update:modelValue']); |
| | | |
| | | // ç¨äº u-upload æ¾ç¤ºçå
é¨å表 |
| | | const internalFileList = ref([]); |
| | | |
| | | // çå¬å¤é¨ modelValue ååï¼åæ¥å°å
鍿¾ç¤ºå表 |
| | | watch(() => props.modelValue, (newVal) => { |
| | | if (newVal) { |
| | | internalFileList.value = newVal.map(item => ({ |
| | | ...item, |
| | | url: item.url || item.previewURL, |
| | | status: 'success', |
| | | message: '' |
| | | })); |
| | | } |
| | | }, { immediate: true, deep: true }); |
| | | |
| | | const showToast = (message) => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // ä¸ä¼ é»è¾ |
| | | const uploadFilePromise = (url) => { |
| | | return new Promise((resolve, reject) => { |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + "/common/upload", |
| | | filePath: url, |
| | | name: "files", // 注æï¼è¿éæ ¹æ®åä»£ç æ¯ "files" |
| | | header: { |
| | | Authorization: "Bearer " + getToken(), |
| | | }, |
| | | success: (res) => { |
| | | try { |
| | | const data = JSON.parse(res.data); |
| | | if (data.code === 200) { |
| | | // 妿è¿åçæ¯æ°ç»ï¼å第ä¸ä¸ªå
ç´ |
| | | const resultData = Array.isArray(data.data) ? data.data[0] : data.data; |
| | | |
| | | // å¤ç url èµå¼ |
| | | if (!resultData.url && resultData.previewURL) { |
| | | resultData.url = resultData.previewURL; |
| | | } |
| | | // å
¼å®¹å代ç ä¸ç name èµå¼ |
| | | if (!resultData.name && resultData.originalFilename) { |
| | | resultData.name = resultData.originalFilename; |
| | | } |
| | | |
| | | resolve(resultData); |
| | | } else { |
| | | reject(data.msg || "ä¸ä¼ 失败"); |
| | | } |
| | | } catch (e) { |
| | | reject("è§£æååºå¤±è´¥"); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | reject(err); |
| | | }, |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | // ä¸ä¼ åçå¤ç |
| | | const afterRead = async (event) => { |
| | | let lists = [].concat(event.file); |
| | | let currentModelValue = [...props.modelValue]; |
| | | |
| | | // å
å¨å
é¨åè¡¨ä¸æ·»å å ä½ï¼ä¸ä¼ ä¸ç¶æï¼ |
| | | lists.forEach(item => { |
| | | internalFileList.value.push({ |
| | | ...item, |
| | | status: 'uploading', |
| | | message: 'ä¸ä¼ ä¸' |
| | | }); |
| | | }); |
| | | |
| | | for (let i = 0; i < lists.length; i++) { |
| | | try { |
| | | const result = await uploadFilePromise(lists[i].url); |
| | | |
| | | // æ´æ° modelValue |
| | | currentModelValue.push(result); |
| | | emit('update:modelValue', currentModelValue); |
| | | |
| | | } catch (e) { |
| | | // 妿ä¸ä¼ 失败ï¼ä»å
é¨å表ä¸ç§»é¤åææ·»å ç项 |
| | | const errorIndex = internalFileList.value.findIndex(item => item.status === 'uploading'); |
| | | if (errorIndex > -1) { |
| | | internalFileList.value.splice(errorIndex, 1); |
| | | } |
| | | showToast(typeof e === "string" ? e : "ä¸ä¼ 失败"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // å é¤å¤ç |
| | | const deleteFile = (event) => { |
| | | const newList = [...props.modelValue]; |
| | | newList.splice(event.index, 1); |
| | | emit('update:modelValue', newList); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .common-upload { |
| | | width: 100%; |
| | | } |
| | | </style> |
| | |
| | | // åºç¨å
¨å±é
ç½® |
| | | const config = { |
| | | baseUrl: "http://1.15.17.182:9003", |
| | | fileUrl: "http://1.15.17.182:9002", |
| | | baseUrl: "http://1.15.17.182:9048", |
| | | fileUrl: "http://1.15.17.182:9049", |
| | | // åºç¨ä¿¡æ¯ |
| | | appInfo: { |
| | | // åºç¨åç§° |
| | |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/inspectionUpload/upload", |
| | | "style": { |
| | | "navigationBarTitleText": "ä¸ä¼ å·¡æ£è®°å½", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/inspectionUpload/attachment", |
| | | "style": { |
| | | "navigationBarTitleText": "æ¥çéä»¶", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/faultAnalysis/index", |
| | | "style": { |
| | | "navigationBarTitleText": "æ
éåæè¿½æº¯", |
| | |
| | | <template> |
| | | <view class="approve-page"> |
| | | |
| | | <PageHeader title="å®¡æ ¸" @back="goBack" /> |
| | | |
| | | <PageHeader title="å®¡æ ¸" |
| | | @back="goBack" /> |
| | | <!-- ç³è¯·ä¿¡æ¯ --> |
| | | <view class="application-info"> |
| | | <view class="info-header"> |
| | |
| | | <text class="info-label">ç³è¯·æ¥æ</text> |
| | | <text class="info-value">{{ approvalData.approveTime }}</text> |
| | | </view> |
| | | |
| | | <!-- approveType=2 请åç¸å
³å段 --> |
| | | <template v-if="approvalData.approveType === 2"> |
| | | <view class="info-row"> |
| | |
| | | <text class="info-value">{{ approvalData.endDate || '-' }}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- approveType=3 åºå·®ç¸å
³å段 --> |
| | | <view v-if="approvalData.approveType === 3" class="info-row"> |
| | | <view v-if="approvalData.approveType === 3" |
| | | class="info-row"> |
| | | <text class="info-label">åºå·®å°ç¹</text> |
| | | <text class="info-value">{{ approvalData.location || '-' }}</text> |
| | | </view> |
| | | |
| | | <!-- approveType=4 æ¥éç¸å
³å段 --> |
| | | <view v-if="approvalData.approveType === 4" class="info-row"> |
| | | <view v-if="approvalData.approveType === 4" |
| | | class="info-row"> |
| | | <text class="info-label">æ¥ééé¢</text> |
| | | <text class="info-value">{{ approvalData.price ? `Â¥${approvalData.price}` : '-' }}</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="{ |
| | | <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> |
| | | <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 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> |
| | | <text v-if="step.approveTime" |
| | | class="step-time">{{ step.approveTime }}</text> |
| | | </view> |
| | | |
| | | <view v-if="step.opinion" class="step-opinion"> |
| | | <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;"> |
| | | <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;" /> |
| | | <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 v-if="canApprove" |
| | | class="approval-input"> |
| | | <view class="input-header"> |
| | | <text class="input-title">å®¡æ ¸æè§</text> |
| | | </view> |
| | | |
| | | <view class="input-content"> |
| | | <u-textarea |
| | | v-model="approvalOpinion" |
| | | rows="4" |
| | | placeholder="请è¾å
¥å®¡æ ¸æè§" |
| | | maxlength="200" |
| | | count |
| | | /> |
| | | <u-textarea v-model="approvalOpinion" |
| | | rows="4" |
| | | placeholder="请è¾å
¥å®¡æ ¸æè§" |
| | | maxlength="200" |
| | | count /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åºé¨æä½æé® --> |
| | | <view v-if="canApprove" class="footer-actions"> |
| | | <u-button class="reject-btn" @click="handleReject">驳å</u-button> |
| | | <u-button class="approve-btn" @click="handleApprove">éè¿</u-button> |
| | | <view v-if="canApprove" |
| | | class="footer-actions"> |
| | | <u-button class="reject-btn" |
| | | @click="handleReject">驳å</u-button> |
| | | <u-button class="approve-btn" |
| | | @click="handleApprove">éè¿</u-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' |
| | | const showToast = (message) => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: 'none' |
| | | }) |
| | | } |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { ref, onMounted, computed } from "vue"; |
| | | import { |
| | | approveProcessGetInfo, |
| | | approveProcessDetails, |
| | | updateApproveNode, |
| | | } from "@/api/collaborativeApproval/approvalProcess"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | const userStore = useUserStore() |
| | | const approvalData = ref({}) |
| | | const approvalSteps = ref([]) |
| | | const approvalOpinion = ref('') |
| | | const approveId = ref('') |
| | | 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) |
| | | }) |
| | | // ä»è¯¦æ
æ¥å£åæ®µå¯¹é½ canApproveï¼ä»
彿 isShen çèç¹æ¶å¯å®¡æ¹ |
| | | const canApprove = computed(() => { |
| | | return approvalSteps.value.some(step => step.isShen === true); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | approveId.value = uni.getStorageSync('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.removeStorageSync('approveId'); |
| | | 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) |
| | | onMounted(() => { |
| | | approveId.value = uni.getStorageSync("approveId"); |
| | | if (approveId.value) { |
| | | loadApprovalData(); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | |
| | | const handleReject = () => { |
| | | uni.showModal({ |
| | | title: '确认æä½', |
| | | content: 'ç¡®å®è¦é©³åæ¤å®¡æ¹åï¼', |
| | | success: (res) => { |
| | | if (res.confirm) submitForm(2) |
| | | 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.removeStorageSync("approveId"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const submitForm = status => { |
| | | // å¯éï¼æ ¡éªå®¡æ ¸æè§ |
| | | if (!approvalOpinion.value?.trim()) { |
| | | showToast("请è¾å
¥å®¡æ ¸æè§"); |
| | | return; |
| | | } |
| | | }) |
| | | } |
| | | // åå§èç¹æ°æ®ï¼ç¨äºæäº¤é»è¾ï¼ |
| | | const activities = ref([]) |
| | | // æ¾å°å½åå¯å®¡æ¹èç¹ |
| | | 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; |
| | | .approve-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 80px; |
| | | } |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | width: 80px; |
| | | flex-shrink: 0; |
| | | } |
| | | .header { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #fff; |
| | | padding: 16px 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 100; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | flex: 1; |
| | | } |
| | | .title { |
| | | flex: 1; |
| | | text-align: center; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .approval-process { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | .application-info { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .process-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #f8f9fa; |
| | | } |
| | | .info-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .process-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | .info-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .process-steps { |
| | | padding: 20px; |
| | | } |
| | | .info-content { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .process-step { |
| | | display: flex; |
| | | position: relative; |
| | | margin-bottom: 24px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | |
| | | .step-line { |
| | | display: none; |
| | | .info-row { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .step-indicator { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | margin-right: 16px; |
| | | } |
| | | .info-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | width: 80px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | flex: 1; |
| | | } |
| | | |
| | | .process-step.completed .step-dot { |
| | | background: #52c41a; |
| | | color: #fff; |
| | | } |
| | | .approval-process { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .process-step.current .step-dot { |
| | | background: #1890ff; |
| | | color: #fff; |
| | | animation: pulse 2s infinite; |
| | | } |
| | | .process-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .process-step.pending .step-dot { |
| | | background: #d9d9d9; |
| | | color: #999; |
| | | } |
| | | .process-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .step-line { |
| | | width: 2px; |
| | | height: 40px; |
| | | background: #d9d9d9; |
| | | margin-top: 8px; |
| | | } |
| | | .process-steps { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .process-step.completed .step-line { |
| | | background: #52c41a; |
| | | } |
| | | .process-step { |
| | | display: flex; |
| | | position: relative; |
| | | margin-bottom: 24px; |
| | | |
| | | .process-step.rejected .step-dot { |
| | | background: #ff4d4f; |
| | | color: #fff; |
| | | } |
| | | .process-step.rejected .step-line { |
| | | background: #ff4d4f; |
| | | } |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | |
| | | .step-content { |
| | | flex: 1; |
| | | padding-top: 4px; |
| | | } |
| | | .step-line { |
| | | display: none; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .step-info { |
| | | margin-bottom: 8px; |
| | | } |
| | | .step-indicator { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | margin-right: 16px; |
| | | } |
| | | |
| | | .step-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | .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; |
| | | } |
| | | |
| | | .step-approver { |
| | | font-size: 14px; |
| | | color: #666; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | .process-step.completed .step-dot { |
| | | background: #52c41a; |
| | | color: #fff; |
| | | } |
| | | |
| | | .step-time { |
| | | font-size: 12px; |
| | | color: #999; |
| | | display: block; |
| | | } |
| | | .process-step.current .step-dot { |
| | | background: #1890ff; |
| | | color: #fff; |
| | | animation: pulse 2s infinite; |
| | | } |
| | | |
| | | .step-opinion { |
| | | background: #f8f9fa; |
| | | padding: 12px; |
| | | border-radius: 8px; |
| | | border-left: 4px solid #52c41a; |
| | | } |
| | | .process-step.pending .step-dot { |
| | | background: #d9d9d9; |
| | | color: #999; |
| | | } |
| | | |
| | | .opinion-label { |
| | | font-size: 12px; |
| | | color: #666; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | .step-line { |
| | | width: 2px; |
| | | height: 40px; |
| | | background: #d9d9d9; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .opinion-content { |
| | | font-size: 14px; |
| | | color: #333; |
| | | line-height: 1.5; |
| | | } |
| | | .process-step.completed .step-line { |
| | | background: #52c41a; |
| | | } |
| | | |
| | | .approval-input { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | .process-step.rejected .step-dot { |
| | | background: #ff4d4f; |
| | | color: #fff; |
| | | } |
| | | .process-step.rejected .step-line { |
| | | background: #ff4d4f; |
| | | } |
| | | |
| | | .input-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #f8f9fa; |
| | | } |
| | | .step-content { |
| | | flex: 1; |
| | | padding-top: 4px; |
| | | } |
| | | |
| | | .input-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | .step-info { |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .input-content { |
| | | padding: 16px; |
| | | } |
| | | .step-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | .step-approver { |
| | | font-size: 14px; |
| | | color: #666; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .reject-btn { |
| | | .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; |
| | |
| | | background: #52c41a; |
| | | color: #fff; |
| | | } |
| | | |
| | | |
| | | /* éé
u-buttonæ ·å¼ */ |
| | | :deep(.u-button) { |
| | | border-radius: 6px; |
| | | } |
| | | |
| | | @keyframes pulse { |
| | | 0% { |
| | | box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7); |
| | | @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); |
| | | } |
| | | } |
| | | 70% { |
| | | box-shadow: 0 0 0 10px rgba(24, 144, 255, 0); |
| | | .signature-section { |
| | | background: #fff; |
| | | padding: 12px 16px 16px; |
| | | border-top: 1px solid #f0f0f0; |
| | | } |
| | | 100% { |
| | | box-shadow: 0 0 0 0 rgba(24, 144, 255, 0); |
| | | .signature-header { |
| | | margin-bottom: 8px; |
| | | } |
| | | } |
| | | .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; |
| | | } |
| | | .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="account-detail"> |
| | | <PageHeader title="å®¡æ¹æµç¨" |
| | | <PageHeader :title="operationType === 'detail' ? '详æ
' : 'å®¡æ¹æµç¨'" |
| | | @back="goBack" /> |
| | | <!-- 表ååºå --> |
| | | <u-form ref="formRef" |
| | |
| | | :rules="rules" |
| | | :model="form" |
| | | label-width="140rpx"> |
| | | <u-form-item prop="approveReason" |
| | | label="æµç¨ç¼å·"> |
| | | <u-input v-model="form.approveId" |
| | | disabled |
| | | placeholder="èªå¨ç¼å·" /> |
| | | </u-form-item> |
| | | <u-form-item prop="approveReason" |
| | | :label="approveType === 5 ? 'éè´äºç±' : 'ç³è¯·äºç±'" |
| | | required> |
| | | <u-input v-model="form.approveReason" |
| | | type="textarea" |
| | | rows="2" |
| | | auto-height |
| | | maxlength="200" |
| | | :placeholder="approveType === 5 ? '请è¾å
¥éè´äºç±' : '请è¾å
¥ç³è¯·äºç±'" |
| | | show-word-limit /> |
| | | </u-form-item> |
| | | <u-form-item prop="approveDeptName" |
| | | label="ç³è¯·é¨é¨" |
| | | required> |
| | | <!-- <u-input v-model="form.approveDeptName" |
| | | <template v-if="operationType !== 'detail'"> |
| | | <u-form-item prop="approveReason" |
| | | label="æµç¨ç¼å·"> |
| | | <u-input v-model="form.approveId" |
| | | disabled |
| | | placeholder="èªå¨ç¼å·" /> |
| | | </u-form-item> |
| | | <u-form-item prop="approveReason" |
| | | :label="approveType === 5 ? 'éè´äºç±' : 'ç³è¯·äºç±'" |
| | | required> |
| | | <u-input v-model="form.approveReason" |
| | | type="textarea" |
| | | rows="2" |
| | | auto-height |
| | | maxlength="200" |
| | | :placeholder="approveType === 5 ? '请è¾å
¥éè´äºç±' : '请è¾å
¥ç³è¯·äºç±'" |
| | | show-word-limit /> |
| | | </u-form-item> |
| | | <u-form-item prop="approveDeptName" |
| | | label="ç³è¯·é¨é¨" |
| | | required> |
| | | <!-- <u-input v-model="form.approveDeptName" |
| | | placeholder="è¯·éæ©ç³è¯·é¨é¨" /> --> |
| | | <u-input v-model="form.approveDeptName" |
| | | readonly |
| | | placeholder="è¯·éæ©ç³è¯·é¨é¨" |
| | | @click="showPicker = true" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showPicker = true"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item prop="approveUser" |
| | | <u-input v-model="form.approveDeptName" |
| | | readonly |
| | | placeholder="è¯·éæ©ç³è¯·é¨é¨" |
| | | @click="showPicker = true" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showPicker = true"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <!-- <u-form-item prop="approveUser" |
| | | label="ç³è¯·äºº" |
| | | required> |
| | | <u-input v-model="form.approveUserName" |
| | |
| | | <up-icon name="arrow-right" |
| | | @click="showDatePicker"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <!-- approveType=2 请åç¸å
³å段 --> |
| | | <template v-if="approveType === 2"> |
| | | <u-form-item prop="startDate" |
| | | label="å¼å§æ¶é´" |
| | | </u-form-item> --> |
| | | <!-- approveType=2 请åç¸å
³å段 --> |
| | | <template v-if="approveType === 2"> |
| | | <u-form-item prop="startDate" |
| | | label="å¼å§æ¶é´" |
| | | required> |
| | | <u-input v-model="form.startDate" |
| | | readonly |
| | | placeholder="请åå¼å§æ¶é´" |
| | | @click="showStartDatePicker" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showStartDatePicker"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item prop="endDate" |
| | | label="ç»ææ¶é´" |
| | | required> |
| | | <u-input v-model="form.endDate" |
| | | readonly |
| | | placeholder="请åç»ææ¶é´" |
| | | @click="showEndDatePicker" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showEndDatePicker"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | </template> |
| | | <!-- approveType=3 åºå·®ç¸å
³å段 --> |
| | | <u-form-item v-if="approveType === 3" |
| | | prop="location" |
| | | label="åºå·®å°ç¹" |
| | | required> |
| | | <u-input v-model="form.startDate" |
| | | readonly |
| | | placeholder="请åå¼å§æ¶é´" |
| | | @click="showStartDatePicker" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showStartDatePicker"></up-icon> |
| | | </template> |
| | | <u-input v-model="form.location" |
| | | placeholder="请è¾å
¥åºå·®å°ç¹" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item prop="endDate" |
| | | label="ç»ææ¶é´" |
| | | <!-- approveType=4 æ¥éç¸å
³å段 --> |
| | | <u-form-item v-if="approveType === 4" |
| | | prop="price" |
| | | label="æ¥ééé¢" |
| | | required> |
| | | <u-input v-model="form.endDate" |
| | | readonly |
| | | placeholder="请åç»ææ¶é´" |
| | | @click="showEndDatePicker" /> |
| | | <template #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showEndDatePicker"></up-icon> |
| | | </template> |
| | | <u-input v-model="form.price" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ¥ééé¢" |
| | | clearable /> |
| | | </u-form-item> |
| | | </template> |
| | | <!-- approveType=3 åºå·®ç¸å
³å段 --> |
| | | <u-form-item v-if="approveType === 3" |
| | | prop="location" |
| | | label="åºå·®å°ç¹" |
| | | required> |
| | | <u-input v-model="form.location" |
| | | placeholder="请è¾å
¥åºå·®å°ç¹" |
| | | clearable /> |
| | | </u-form-item> |
| | | <!-- approveType=4 æ¥éç¸å
³å段 --> |
| | | <u-form-item v-if="approveType === 4" |
| | | prop="price" |
| | | label="æ¥ééé¢" |
| | | required> |
| | | <u-input v-model="form.price" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ¥ééé¢" |
| | | clearable /> |
| | | <!-- æ¥ä»·å®¡æ¹è¯¦æ
--> |
| | | <view v-if="isQuotationApproval" |
| | | style="margin: 20rpx 0;"> |
| | | <u-divider text="æ¥ä»·è¯¦æ
" |
| | | text-size="28rpx" |
| | | color="#2979ff"></u-divider> |
| | | <u-skeleton :loading="quotationLoading" |
| | | rows="3" |
| | | animated> |
| | | <view v-if="!currentQuotation || !currentQuotation.quotationNo" |
| | | style="padding: 40rpx; text-align: center; color: #999;"> |
| | | æªæ¥è¯¢å°å¯¹åºæ¥ä»·è¯¦æ
|
| | | </view> |
| | | <view v-else> |
| | | <u-cell-group :border="false"> |
| | | <u-cell title="æ¥ä»·åå·" |
| | | :value="currentQuotation.quotationNo"></u-cell> |
| | | <u-cell title="客æ·åç§°" |
| | | :value="currentQuotation.customer"></u-cell> |
| | | <u-cell title="ä¸å¡å" |
| | | :value="currentQuotation.salesperson"></u-cell> |
| | | <u-cell title="æ¥ä»·æ¥æ" |
| | | :value="currentQuotation.quotationDate"></u-cell> |
| | | <u-cell title="æææè³" |
| | | :value="currentQuotation.validDate"></u-cell> |
| | | <u-cell title="仿¬¾æ¹å¼" |
| | | :value="currentQuotation.paymentMethod"></u-cell> |
| | | <u-cell title="æ¥ä»·æ»é¢"> |
| | | <template #value> |
| | | <text style="font-size: 32rpx; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }} |
| | | </text> |
| | | </template> |
| | | </u-cell> |
| | | </u-cell-group> |
| | | <view style="margin-top: 20rpx; padding: 0 30rpx;"> |
| | | <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">产åæç»</view> |
| | | <view v-for="(item, index) in (currentQuotation.products || [])" |
| | | :key="index" |
| | | style="background: #f8f8f8; border-radius: 8rpx; padding: 20rpx; margin-bottom: 10rpx;"> |
| | | <view style="display: flex; justify-content: space-between;"> |
| | | <text style="font-weight: bold;">{{ item.product }}</text> |
| | | <text style="color: #e6a23c;">Â¥{{ Number(item.unitPrice ?? 0).toFixed(2) }}</text> |
| | | </view> |
| | | <view style="font-size: 24rpx; color: #666; margin-top: 10rpx;"> |
| | | è§æ ¼: {{ item.specification }} | åä½: {{ item.unit }} |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="currentQuotation.remark" |
| | | style="margin-top: 20rpx; padding: 0 30rpx;"> |
| | | <view style="font-size: 28rpx; font-weight: bold;">夿³¨</view> |
| | | <view style="font-size: 26rpx; color: #666; margin-top: 10rpx;">{{ currentQuotation.remark }}</view> |
| | | </view> |
| | | </view> |
| | | </u-skeleton> |
| | | </view> |
| | | <!-- éè´å®¡æ¹è¯¦æ
--> |
| | | <view v-if="isPurchaseApproval" |
| | | style="margin: 20rpx 0;"> |
| | | <u-divider text="éè´è¯¦æ
" |
| | | text-size="28rpx" |
| | | color="#2979ff"></u-divider> |
| | | <u-skeleton :loading="purchaseLoading" |
| | | rows="3" |
| | | animated> |
| | | <view v-if="!currentPurchase || !currentPurchase.purchaseContractNumber" |
| | | style="padding: 40rpx; text-align: center; color: #999;"> |
| | | æªæ¥è¯¢å°å¯¹åºéè´è¯¦æ
|
| | | </view> |
| | | <view v-else> |
| | | <u-cell-group :border="false"> |
| | | <u-cell title="éè´ååå·" |
| | | :value="currentPurchase.purchaseContractNumber"></u-cell> |
| | | <u-cell title="ä¾åºååç§°" |
| | | :value="currentPurchase.supplierName"></u-cell> |
| | | <u-cell title="项ç®åç§°" |
| | | :value="currentPurchase.projectName"></u-cell> |
| | | <u-cell title="éå®ååå·" |
| | | :value="currentPurchase.salesContractNo"></u-cell> |
| | | <u-cell title="ç¾è®¢æ¥æ" |
| | | :value="currentPurchase.executionDate"></u-cell> |
| | | <u-cell title="å½å
¥æ¥æ" |
| | | :value="currentPurchase.entryDate"></u-cell> |
| | | <u-cell title="仿¬¾æ¹å¼" |
| | | :value="currentPurchase.paymentMethod"></u-cell> |
| | | <u-cell title="ååéé¢"> |
| | | <template #value> |
| | | <text style="font-size: 32rpx; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }} |
| | | </text> |
| | | </template> |
| | | </u-cell> |
| | | </u-cell-group> |
| | | <view style="margin-top: 20rpx; padding: 0 30rpx;"> |
| | | <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">产åæç»</view> |
| | | <view v-for="(item, index) in (currentPurchase.productData || [])" |
| | | :key="index" |
| | | style="background: #f8f8f8; border-radius: 8rpx; padding: 20rpx; margin-bottom: 10rpx;"> |
| | | <view style="display: flex; justify-content: space-between;"> |
| | | <text style="font-weight: bold;">{{ item.productCategory }}</text> |
| | | <text style="color: #e6a23c;">Â¥{{ Number(item.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</text> |
| | | </view> |
| | | <view style="font-size: 24rpx; color: #666; margin-top: 10rpx;"> |
| | | è§æ ¼: {{ item.specificationModel }} | æ°é: {{ item.quantity }} {{ item.unit }} |
| | | </view> |
| | | <view style="font-size: 24rpx; color: #999; margin-top: 4rpx;"> |
| | | å«ç¨åä»·: Â¥{{ Number(item.taxInclusiveUnitPrice ?? 0).toFixed(2) }} |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </u-skeleton> |
| | | </view> |
| | | <!-- å货审æ¹è¯¦æ
--> |
| | | <view v-if="isDeliveryApproval" |
| | | style="margin: 20rpx 0;"> |
| | | <u-divider text="å货详æ
" |
| | | text-size="28rpx" |
| | | color="#2979ff"></u-divider> |
| | | <u-skeleton :loading="deliveryLoading" |
| | | rows="3" |
| | | animated> |
| | | <view v-if="!currentDelivery || !currentDelivery.shippingInfo" |
| | | style="padding: 40rpx; text-align: center; color: #999;"> |
| | | æªæ¥è¯¢å°å¯¹åºå货详æ
|
| | | </view> |
| | | <view v-else> |
| | | <u-cell-group :border="false"> |
| | | <u-cell title="éå®è®¢å" |
| | | :value="currentDelivery.shippingInfo.salesContractNo || '--'"></u-cell> |
| | | <u-cell title="å货订åå·" |
| | | :value="currentDelivery.shippingInfo.shippingNo || '--'"></u-cell> |
| | | <u-cell title="客æ·åç§°" |
| | | :value="currentDelivery.shippingInfo.customerName || '--'"></u-cell> |
| | | <u-cell title="åè´§ç±»å" |
| | | :value="currentDelivery.shippingInfo.type || '--'"></u-cell> |
| | | <u-cell title="åè´§æ¥æ" |
| | | :value="currentDelivery.shippingInfo.shippingDate || '--'"></u-cell> |
| | | <u-cell title="å®¡æ ¸ç¶æ" |
| | | :value="currentDelivery.shippingInfo.status || '--'"></u-cell> |
| | | <u-cell title="å货车çå·" |
| | | :value="currentDelivery.shippingInfo.shippingCarNumber || '--'"></u-cell> |
| | | <u-cell title="å¿«éå
¬å¸" |
| | | :value="currentDelivery.shippingInfo.expressCompany || '--'"></u-cell> |
| | | <u-cell title="å¿«éåå·" |
| | | :value="currentDelivery.shippingInfo.expressNumber || '--'"></u-cell> |
| | | </u-cell-group> |
| | | <view style="margin-top: 20rpx; padding: 0 30rpx;"> |
| | | <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">产åæç»</view> |
| | | <view v-for="(item, index) in deliveryProductList" |
| | | :key="index" |
| | | style="background: #f8f8f8; border-radius: 8rpx; padding: 20rpx; margin-bottom: 10rpx;"> |
| | | <view style="display: flex; justify-content: space-between;"> |
| | | <text style="font-weight: bold;">{{ item.productName }}</text> |
| | | <text style="color: #2979ff;">æ°é: {{ item.deliveryQuantity }}</text> |
| | | </view> |
| | | <view style="font-size: 24rpx; color: #666; margin-top: 10rpx;"> |
| | | è§æ ¼: {{ item.specificationModel }} |
| | | </view> |
| | | <view v-if="item.batchNo" |
| | | style="font-size: 24rpx; color: #999; margin-top: 4rpx;"> |
| | | æ¹å·: {{ item.batchNo }} |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="currentDelivery.shippingInfo.storageBlobVOs && currentDelivery.shippingInfo.storageBlobVOs.length" |
| | | style="margin-top: 20rpx; padding: 0 30rpx;"> |
| | | <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">åè´§å¾ç</view> |
| | | <CommonUpload :model-value="currentDelivery.shippingInfo.storageBlobVOs" |
| | | disabled /> |
| | | </view> |
| | | </view> |
| | | </u-skeleton> |
| | | </view> |
| | | <u-form-item v-if="operationType !== 'detail'" |
| | | label="å¾çéä»¶" |
| | | prop="storageBlobDTOS" |
| | | border-bottom> |
| | | <CommonUpload v-model="form.storageBlobDTOS" /> |
| | | </u-form-item> |
| | | </u-form> |
| | | <!-- éæ©å¨å¼¹çª --> |
| | | <up-action-sheet :show="showPicker" |
| | | :actions="productOptions" |
| | | title="éæ©é¨é¨" |
| | | @select="onConfirm" |
| | | @close="showPicker = false" /> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup :show="showDate" |
| | | mode="bottom" |
| | | @close="showDate = false"> |
| | | <up-datetime-picker :show="true" |
| | | v-model="currentDate" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" /> |
| | | </up-popup> |
| | | <!-- 请åå¼å§æ¶é´éæ©å¨ --> |
| | | <up-popup :show="showStartDate" |
| | | mode="bottom" |
| | | @close="showStartDate = false"> |
| | | <up-datetime-picker :show="true" |
| | | v-model="startDateValue" |
| | | @confirm="onStartDateConfirm" |
| | | @cancel="showStartDate = false" |
| | | mode="date" /> |
| | | </up-popup> |
| | | <!-- 请åç»ææ¶é´éæ©å¨ --> |
| | | <up-popup :show="showEndDate" |
| | | mode="bottom" |
| | | @close="showEndDate = false"> |
| | | <up-datetime-picker :show="true" |
| | | v-model="endDateValue" |
| | | @confirm="onEndDateConfirm" |
| | | @cancel="showEndDate = false" |
| | | mode="date" /> |
| | | </up-popup> |
| | | <!-- å®¡æ ¸æµç¨åºå --> |
| | | <view class="approval-process"> |
| | | <view class="approval-header"> |
| | | <text class="approval-title">å®¡æ ¸æµç¨</text> |
| | | <text class="approval-desc">æ¯ä¸ªæ¥éª¤åªè½éæ©ä¸ä¸ªå®¡æ¹äºº</text> |
| | | </view> |
| | | <view class="approval-steps"> |
| | | <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="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 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 < 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"> |
| | | <u-button icon="plus" |
| | | plain |
| | | type="primary" |
| | | style="width: 100%" |
| | | @click="addApprovalStep">æ°å¢èç¹</u-button> |
| | | </view> |
| | | </view> |
| | | <template v-if="operationType !== 'detail'"> |
| | | <up-action-sheet :show="showPicker" |
| | | :actions="productOptions" |
| | | title="éæ©é¨é¨" |
| | | @select="onConfirm" |
| | | @close="showPicker = false" /> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup :show="showDate" |
| | | mode="bottom" |
| | | @close="showDate = false"> |
| | | <up-datetime-picker :show="true" |
| | | v-model="currentDate" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" /> |
| | | </up-popup> |
| | | <!-- 请åå¼å§æ¶é´éæ©å¨ --> |
| | | <up-popup :show="showStartDate" |
| | | mode="bottom" |
| | | @close="showStartDate = false"> |
| | | <up-datetime-picker :show="true" |
| | | v-model="startDateValue" |
| | | @confirm="onStartDateConfirm" |
| | | @cancel="showStartDate = false" |
| | | mode="date" /> |
| | | </up-popup> |
| | | <!-- 请åç»ææ¶é´éæ©å¨ --> |
| | | <up-popup :show="showEndDate" |
| | | mode="bottom" |
| | | @close="showEndDate = false"> |
| | | <up-datetime-picker :show="true" |
| | | v-model="endDateValue" |
| | | @confirm="onEndDateConfirm" |
| | | @cancel="showEndDate = false" |
| | | mode="date" /> |
| | | </up-popup> |
| | | </template> |
| | | <!-- åºé¨æé® --> |
| | | <view class="footer-btns"> |
| | | <view class="footer-btns" |
| | | v-if="operationType !== 'detail'"> |
| | | <u-button class="cancel-btn" |
| | | @click="goBack">åæ¶</u-button> |
| | | <u-button class="save-btn" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue"; |
| | | import { |
| | | ref, |
| | | onMounted, |
| | | onUnmounted, |
| | | reactive, |
| | | toRefs, |
| | | computed, |
| | | watch, |
| | | } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import CommonUpload from "@/components/CommonUpload.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { formatDateToYMD } from "@/utils/ruoyi"; |
| | | import { |
| | |
| | | approveProcessGetInfo, |
| | | approveProcessAdd, |
| | | approveProcessUpdate, |
| | | getDeliveryDetailByShippingNo, |
| | | } from "@/api/collaborativeApproval/approvalProcess"; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation"; |
| | | import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger"; |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | import { userListNoPageByTenantId } from "@/api/system/user"; |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | |
| | | approveDeptId: "", |
| | | approveReason: "", |
| | | checkResult: "", |
| | | tempFileIds: [], |
| | | approverList: [], // æ°å¢å段ï¼å卿æèç¹ç审æ¹äººid |
| | | storageBlobDTOS: [], |
| | | startDate: "", |
| | | endDate: "", |
| | | location: "", |
| | |
| | | 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 endDateValue = ref(Date.now()); |
| | | const userStore = useUserStore(); |
| | | const approveType = ref(0); |
| | | const isInitialLoading = ref(false); |
| | | |
| | | const quotationLoading = ref(false); |
| | | const currentQuotation = ref({}); |
| | | const purchaseLoading = ref(false); |
| | | const currentPurchase = ref({}); |
| | | const deliveryLoading = ref(false); |
| | | const currentDelivery = ref({}); |
| | | const deliveryProductList = ref([]); |
| | | |
| | | const isQuotationApproval = computed(() => Number(approveType.value) === 6); |
| | | const isPurchaseApproval = computed(() => Number(approveType.value) === 5); |
| | | const isDeliveryApproval = computed(() => Number(approveType.value) === 7); |
| | | |
| | | const getProductOptions = () => { |
| | | getDept().then(res => { |
| | |
| | | })); |
| | | }); |
| | | }; |
| | | const fileList = ref([]); |
| | | let nextApproverId = 2; |
| | | const getCurrentinfo = () => { |
| | | userStore.getInfo().then(res => { |
| | | form.value.approveDeptId = res.user.tenantId; |
| | | console.log(res.user.tenantId, "res.user.tenantId"); |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = e => { |
| | | form.value.approveTime = formatDateToYMD(e.value); |
| | | currentDate.value = formatDateToYMD(e.value); |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | // æ¾ç¤ºè¯·åå¼å§æ¶é´éæ©å¨ |
| | | const showStartDatePicker = () => { |
| | | showStartDate.value = true; |
| | | }; |
| | | |
| | | // 确认请åå¼å§æ¶é´éæ© |
| | | const onStartDateConfirm = e => { |
| | | form.value.startDate = formatDateToYMD(e.value); |
| | | showStartDate.value = false; |
| | | }; |
| | | |
| | | const showEndDatePicker = () => { |
| | | showEndDate.value = true; |
| | | }; |
| | | |
| | | // 确认请åç»ææ¶é´éæ© |
| | | const onEndDateConfirm = e => { |
| | | form.value.endDate = formatDateToYMD(e.value); |
| | | showEndDate.value = false; |
| | | }; |
| | | |
| | | const fetchDetailData = async row => { |
| | | // æ¥ä»·å®¡æ¹ |
| | | if (isQuotationApproval.value) { |
| | | const quotationNo = row?.approveReason; |
| | | if (quotationNo) { |
| | | quotationLoading.value = true; |
| | | getQuotationList({ quotationNo }) |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | | currentQuotation.value = records[0] || {}; |
| | | }) |
| | | .finally(() => { |
| | | quotationLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // éè´å®¡æ¹ |
| | | if (isPurchaseApproval.value) { |
| | | const purchaseContractNumber = row?.approveReason; |
| | | if (purchaseContractNumber) { |
| | | purchaseLoading.value = true; |
| | | getPurchaseByCode({ purchaseContractNumber }) |
| | | .then(res => { |
| | | currentPurchase.value = res; |
| | | }) |
| | | .catch(err => { |
| | | console.error("æ¥è¯¢éè´è¯¦æ
失败:", err); |
| | | }) |
| | | .finally(() => { |
| | | purchaseLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // åè´§å®¡æ¹ |
| | | if (isDeliveryApproval.value) { |
| | | const deliveryNo = row?.approveReason; |
| | | if (deliveryNo) { |
| | | deliveryLoading.value = true; |
| | | currentDelivery.value = {}; |
| | | deliveryProductList.value = []; |
| | | getDeliveryDetailByShippingNo({ shippingNo: deliveryNo }) |
| | | .then(res => { |
| | | const detailData = res?.data || res || {}; |
| | | currentDelivery.value = detailData; |
| | | deliveryProductList.value = |
| | | detailData.shippingProductDetailDtoList || []; |
| | | }) |
| | | .catch(err => { |
| | | console.error("æ¥è¯¢å货详æ
失败:", err); |
| | | }) |
| | | .finally(() => { |
| | | deliveryLoading.value = false; |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // çå¬å®¡æ¹äºç±ååï¼å¦ææ¯ç¹å®å®¡æ¹ç±»ååå°è¯è·å详æ
|
| | | watch( |
| | | () => form.value.approveReason, |
| | | newVal => { |
| | | if (isInitialLoading.value) return; |
| | | if ( |
| | | newVal && |
| | | (isQuotationApproval.value || |
| | | isPurchaseApproval.value || |
| | | isDeliveryApproval.value) |
| | | ) { |
| | | // å»¶è¿ä¸ä¼å请æ±ï¼é¿å
è¾å
¥è¿ç¨ä¸é¢ç¹è§¦å |
| | | debounceFetchDetail(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | let timer = null; |
| | | const debounceFetchDetail = () => { |
| | | if (timer) clearTimeout(timer); |
| | | timer = setTimeout(() => { |
| | | fetchDetailData(form.value); |
| | | }, 800); |
| | | }; |
| | | |
| | | 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(); |
| | |
| | | approveType.value = uni.getStorageSync("approveType") || 0; |
| | | |
| | | // 妿æ¯ç¼è¾æ¨¡å¼ï¼ä»æ¬å°åå¨è·åæ°æ® |
| | | if (operationType.value === "edit") { |
| | | if (operationType.value === "edit" || operationType.value === "detail") { |
| | | 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 => { |
| | | isInitialLoading.value = true; |
| | | 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; |
| | | // 设置å¾çå表æ¾ç¤º |
| | | const fileData = |
| | | res.data.storageBlobVOS || res.data.commonFileList || []; |
| | | if (fileData.length > 0) { |
| | | form.value.storageBlobDTOS = fileData; |
| | | } |
| | | } |
| | | ); |
| | | // è·åé¢å¤è¯¦æ
|
| | | fetchDetailData(res.data); |
| | | }) |
| | | .finally(() => { |
| | | // å»¶è¿ä¸ä¼éç½®ï¼ç¡®ä¿ watch ä¸ä¼è¢«è§¦å |
| | | setTimeout(() => { |
| | | isInitialLoading.value = false; |
| | | }, 100); |
| | | }); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼åå§åä¸ä¸ªç©ºç审æ¹èç¹ |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | } |
| | | |
| | | // çå¬èç³»äººéæ©äºä»¶ |
| | | uni.$on("selectContact", handleSelectContact); |
| | | } catch (error) { |
| | | console.error("è·åé¨é¨æ°æ®å¤±è´¥:", error); |
| | | console.error("è·åæ°æ®å¤±è´¥:", error); |
| | | } |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | // ç§»é¤äºä»¶çå¬ |
| | | uni.$off("selectContact", handleSelectContact); |
| | | }); |
| | | onUnmounted(() => {}); |
| | | |
| | | const onConfirm = item => { |
| | | // 设置éä¸çé¨é¨ |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | // æ£æ¥æ¯ä¸ªå®¡æ¹æ¥éª¤æ¯å¦é½æå®¡æ¹äºº |
| | | const hasEmptyStep = approverNodes.value.some(step => !step.nickName); |
| | | if (hasEmptyStep) { |
| | | showToast("请为æ¯ä¸ªå®¡æ¹æ¥éª¤éæ©å®¡æ¹äºº"); |
| | | return; |
| | | } |
| | | |
| | | // æå¨æ£æ¥å¿
å¡«åæ®µï¼é²æ¢å æ°æ®ç±»åé®é¢å¯¼è´çæ ¡éªå¤±è´¥ |
| | | if (!form.value.approveReason || !form.value.approveReason.trim()) { |
| | | showToast("请è¾å
¥ç³è¯·äºç±"); |
| | |
| | | .then(valid => { |
| | | if (valid) { |
| | | // è¡¨åæ ¡éªéè¿ï¼å¯ä»¥æäº¤æ°æ® |
| | | // æ¶éææèç¹ç审æ¹äººid |
| | | console.log("approverNodes---", approverNodes.value); |
| | | form.value.approveUserIds = approverNodes.value |
| | | .map(node => node.userId) |
| | | .join(","); |
| | | form.value.approveType = approveType.value; |
| | | form.value.approveDeptId = Number(form.value.approveDeptId); |
| | | // const submitForm = { |
| | | // approveDeptId: form.value.approveDeptId, |
| | | // approveDeptName: form.value.approveDeptName, |
| | | // approveReason: form.value.approveReason, |
| | | // approveTime: form.value.approveTime, |
| | | // approveType: form.value.approveType, |
| | | // approveUser: form.value.approveUser, |
| | | // approveUserIds: form.value.approveUserIds, |
| | | // endDate: form.value.endDate, |
| | | // startDate: form.value.startDate, |
| | | // }; |
| | | // console.log("form.value---", form.value); |
| | | // console.log("submitForm", submitForm); |
| | | |
| | | if (operationType.value === "add" || currentApproveStatus.value == 3) { |
| | | approveProcessAdd(form.value).then(res => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | // å¤çèç³»äººéæ©ç»æ |
| | | const handleSelectContact = data => { |
| | | const { stepIndex, contact } = data; |
| | | // å°éä¸çè系人设置为对åºå®¡æ¹æ¥éª¤ç审æ¹äºº |
| | | approverNodes.value[stepIndex].userId = contact.userId; |
| | | approverNodes.value[stepIndex].nickName = contact.nickName; |
| | | }; |
| | | |
| | | const addApprover = stepIndex => { |
| | | // 跳转å°èç³»äººéæ©é¡µé¢ |
| | | uni.setStorageSync("stepIndex", stepIndex); |
| | | uni.navigateTo({ |
| | | url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect", |
| | | }); |
| | | }; |
| | | |
| | | 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 = e => { |
| | | form.value.approveTime = formatDateToYMD(e.value); |
| | | currentDate.value = formatDateToYMD(e.value); |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | // æ¾ç¤ºè¯·åå¼å§æ¶é´éæ©å¨ |
| | | const showStartDatePicker = () => { |
| | | showStartDate.value = true; |
| | | }; |
| | | |
| | | // 确认请åå¼å§æ¶é´éæ© |
| | | const onStartDateConfirm = e => { |
| | | form.value.startDate = formatDateToYMD(e.value); |
| | | showStartDate.value = false; |
| | | }; |
| | | |
| | | const showEndDatePicker = () => { |
| | | showEndDate.value = true; |
| | | }; |
| | | |
| | | // 确认请åç»ææ¶é´éæ© |
| | | const onEndDateConfirm = e => { |
| | | form.value.endDate = formatDateToYMD(e.value); |
| | | showEndDate.value = false; |
| | | }; |
| | | |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | | const today = new Date(); |
| | |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .approval-process { |
| | | background: #fff; |
| | | margin: 16px; |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .approval-header { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .approval-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .approval-desc { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | /* æ ·å¼å¢å¼ºä¸ºâç®æ´å°åå飿 ¼â */ |
| | | .approval-steps { |
| | | 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: 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: 4px 12px; |
| | | border-radius: 12px; |
| | | position: relative; |
| | | line-height: 1.4; // ç¡®ä¿æåè¡é«ä¸è´ |
| | | } |
| | | |
| | | .approver-item { |
| | | display: flex; |
| | | align-items: center; |
| | | 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: 48px; |
| | | height: 48px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); |
| | | } |
| | | |
| | | .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 { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #333; |
| | | 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: 16px; |
| | | color: #ff4d4f; |
| | | 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; |
| | | 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 { |
| | | color: #ff4d4f; |
| | | font-size: 12px; |
| | | 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%; |
| | | } |
| | | } |
| | | |
| | | .step-line { |
| | | display: none; // éè忥ç线æ¡ï¼ä½¿ç¨ä¼ªå
ç´ ä»£æ¿ |
| | | } |
| | | |
| | | .add-step-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | .account-detail { |
| | | background-color: #fff; |
| | | } |
| | | .footer-btns { |
| | | position: fixed; |
| | |
| | | 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> |
| | | <view class="detail-row"> |
| | | <view class="actions"> |
| | | <!-- <u-button type="primary" |
| | | <u-button type="primary" |
| | | size="small" |
| | | class="action-btn edit" |
| | | :disabled="item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4 || item.approveStatus == 8" |
| | | v-if="!(item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4 || item.approveStatus == 8 || item.approveType == 5 || item.approveType == 6 || item.approveType == 7)" |
| | | @click="handleItemClick(item)"> |
| | | ç¼è¾ |
| | | </u-button> --> |
| | | </u-button> |
| | | <u-button type="info" |
| | | v-if="item.approveType == 5 || item.approveType == 6 || item.approveType == 7" |
| | | size="small" |
| | | class="action-btn detail" |
| | | @click="handleDetailClick(item)"> |
| | | 详æ
|
| | | </u-button> |
| | | <u-button type="success" |
| | | size="small" |
| | | class="action-btn approve" |
| | |
| | | <text>ææ å®¡æ¹æ°æ®</text> |
| | | </view> |
| | | <!-- æµ®å¨æä½æé® --> |
| | | <!-- <view class="fab-button" |
| | | <view class="fab-button" |
| | | v-if="props.approveType != 5 && props.approveType != 6 && props.approveType != 7" |
| | | @click="handleAdd"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> --> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | }); |
| | | }; |
| | | |
| | | // æ¥ç详æ
|
| | | const handleDetailClick = item => { |
| | | uni.setStorageSync("invoiceLedgerEditRow", JSON.stringify(item)); |
| | | uni.setStorageSync("operationType", "detail"); |
| | | uni.setStorageSync("approveId", item.approveId); |
| | | uni.setStorageSync("approveType", props.approveType); |
| | | uni.navigateTo({ |
| | | url: "/pages/cooperativeOffice/collaborativeApproval/detail", |
| | | }); |
| | | }; |
| | | |
| | | // æ·»å æ°è®°å½ |
| | | const handleAdd = () => { |
| | | uni.setStorageSync("operationType", "add"); |
| | |
| | | placeholder="请è¾å
¥æ¥ä¿®äºº" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="维修人" |
| | | prop="maintenanceName" |
| | | border-bottom> |
| | | <u-input v-model="form.maintenanceName" |
| | | placeholder="请è¾å
¥ç»´ä¿®äºº" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="维修项ç®" |
| | | prop="machineryCategory" |
| | | border-bottom> |
| | | <u-input v-model="form.machineryCategory" |
| | | placeholder="请è¾å
¥ç»´ä¿®é¡¹ç®" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="æ
éç°è±¡" |
| | | prop="remark" |
| | | required |
| | |
| | | clearable |
| | | count |
| | | maxlength="200" /> |
| | | </u-form-item> |
| | | <u-form-item label="å¾çéä»¶" |
| | | prop="storageBlobDTOs" |
| | | border-bottom> |
| | | <CommonUpload v-model="form.storageBlobDTOs" /> |
| | | </u-form-item> |
| | | </u-cell-group> |
| | | <!-- æäº¤æé® --> |
| | |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onUnmounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import { onShow, onLoad } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import CommonUpload from "@/components/CommonUpload.vue"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import { |
| | | addRepair, |
| | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const operationType = ref("add"); |
| | | const repairId = ref(""); |
| | | const loading = ref(false); |
| | | const showDevice = ref(false); |
| | | const showDate = ref(false); |
| | | const pickerDateValue = ref(Date.now()); |
| | | |
| | | onLoad(options => { |
| | | if (options.id) { |
| | | repairId.value = options.id; |
| | | } |
| | | getPageParams(); |
| | | }); |
| | | |
| | | // 设å¤é项 |
| | | const deviceOptions = ref([]); |
| | |
| | | deviceModel: undefined, // è§æ ¼åå· |
| | | repairTime: dayjs().format("YYYY-MM-DD"), // æ¥ä¿®æ¥æ |
| | | repairName: undefined, // æ¥ä¿®äºº |
| | | maintenanceName: undefined, // 维修人 |
| | | machineryCategory: undefined, // ç»´ä¿®é¡¹ç® |
| | | remark: undefined, // æ
éç°è±¡ |
| | | storageBlobDTOs: [], // å¾çéä»¶ |
| | | }); |
| | | |
| | | // æ¥ä¿®ç¶æé项 |
| | |
| | | form.value.deviceModel = data.deviceModel; |
| | | form.value.repairTime = dayjs(data.repairTime).format("YYYY-MM-DD"); |
| | | form.value.repairName = data.repairName; |
| | | form.value.maintenanceName = data.maintenanceName; |
| | | form.value.machineryCategory = data.machineryCategory; |
| | | form.value.remark = data.remark; |
| | | form.value.storageBlobDTOs = data.storageBlobVOs || []; |
| | | repairStatusText.value = |
| | | repairStatusOptions.value.find(item => item.value == data.status) |
| | | ?.name || ""; |
| | |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶è·ååæ° |
| | | getPageParams(); |
| | | // 页颿¾ç¤ºæ¶é»è¾ |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·å设å¤å表ååæ° |
| | | // 页é¢å è½½æ¶è·å设å¤å表 |
| | | loadDeviceName(); |
| | | getPageParams(); |
| | | }); |
| | | |
| | | // ç»ä»¶å¸è½½æ¶æ¸
ç宿¶å¨ |
| | |
| | | |
| | | // åå¤æäº¤æ°æ® |
| | | const submitData = { ...form.value }; |
| | | |
| | | const { code } = id |
| | | ? await editRepair({ id: id, ...submitData }) |
| | | : await addRepair(submitData); |
| | |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.removeStorageSync("repairId"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | // 使ç¨uni.getStorageSyncè·åid |
| | | const id = uni.getStorageSync("repairId"); |
| | | |
| | | // æ ¹æ®æ¯å¦æidåæ°æ¥å¤ææ¯æ°å¢è¿æ¯ç¼è¾ |
| | | if (id) { |
| | | if (repairId.value) { |
| | | // ç¼è¾æ¨¡å¼ï¼è·å详æ
|
| | | loadForm(id); |
| | | // å¯éï¼è·å忏
é¤åå¨çidï¼é¿å
å½±ååç»æä½ |
| | | uni.removeStorageSync("repairId"); |
| | | loadForm(repairId.value); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | loadForm(); |
| | |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | // 使ç¨uni.getStorageSyncè·åid |
| | | const id = uni.getStorageSync("repairId"); |
| | | return id; |
| | | return repairId.value; |
| | | }; |
| | | </script> |
| | | |
| | |
| | | <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.machineryCategory || '-' }}</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> |
| | |
| | | const edit = id => { |
| | | if (!id) return; |
| | | // 使ç¨uni.setStorageSyncåå¨id |
| | | uni.setStorageSync("repairId", id); |
| | | // uni.setStorageSync("repairId", id); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/repair/add", |
| | | url: "/pages/equipmentManagement/repair/add?id=" + id, |
| | | }); |
| | | }; |
| | | |
| | |
| | | <template> |
| | | <view class="upkeep-add"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="operationType === 'edit' ? 'ç¼è¾ä¿å
»è®¡å' : 'æ°å¢ä¿å
»è®¡å'" @back="goBack" /> |
| | | |
| | | <!-- 表åå
容 --> |
| | | <u-form ref="formRef" :model="form" :rules="formRules" label-width="110px"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <u-form-item label="设å¤åç§°" prop="deviceNameText" required border-bottom> |
| | | <u-input |
| | | v-model="form.deviceNameText" |
| | | placeholder="è¯·éæ©è®¾å¤åç§°" |
| | | readonly |
| | | @click="showDevicePicker" |
| | | clearable |
| | | /> |
| | | <template #right> |
| | | <u-icon name="scan" @click="startScan" class="scan-icon" /> |
| | | </template> |
| | | </u-form-item> |
| | | |
| | | <u-form-item label="è§æ ¼åå·" prop="deviceModel" border-bottom> |
| | | <u-input |
| | | v-model="form.deviceModel" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | readonly |
| | | clearable |
| | | /> |
| | | </u-form-item> |
| | | |
| | | <u-form-item label="计åä¿å
»æ¥æ" prop="maintenancePlanTime" required border-bottom> |
| | | <u-input |
| | | v-model="form.maintenancePlanTime" |
| | | placeholder="è¯·éæ©è®¡åä¿å
»æ¥æ" |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable |
| | | /> |
| | | <template #right> |
| | | <u-icon name="arrow-right" @click="showDatePicker" /> |
| | | </template> |
| | | </u-form-item> |
| | | |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <u-button class="cancel-btn" @click="goBack">åæ¶</u-button> |
| | | <u-button class="save-btn" @click="sendForm" :loading="loading">ä¿å</u-button> |
| | | </view> |
| | | </u-form> |
| | | |
| | | <!-- 设å¤éæ©å¨ --> |
| | | <up-action-sheet |
| | | :show="showDevice" |
| | | :actions="deviceActions" |
| | | title="éæ©è®¾å¤" |
| | | @select="onDeviceConfirm" |
| | | @close="showDevice = false" |
| | | /> |
| | | <up-datetime-picker |
| | | :show="showDate" |
| | | v-model="pickerDateValue" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" |
| | | /> |
| | | |
| | | </view> |
| | | <view class="upkeep-add"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="operationType === 'edit' ? 'ç¼è¾ä¿å
»è®¡å' : 'æ°å¢ä¿å
»è®¡å'" |
| | | @back="goBack" /> |
| | | <!-- 表åå
容 --> |
| | | <u-form ref="formRef" |
| | | :model="form" |
| | | :rules="formRules" |
| | | label-width="110px"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <u-form-item label="设å¤åç§°" |
| | | prop="deviceNameText" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.deviceNameText" |
| | | placeholder="è¯·éæ©è®¾å¤åç§°" |
| | | readonly |
| | | @click="showDevicePicker" |
| | | clearable /> |
| | | <template #right> |
| | | <u-icon name="scan" |
| | | @click="startScan" |
| | | class="scan-icon" /> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="è§æ ¼åå·" |
| | | prop="deviceModel" |
| | | border-bottom> |
| | | <u-input v-model="form.deviceModel" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | readonly |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="计åä¿å
»æ¥æ" |
| | | prop="maintenancePlanTime" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.maintenancePlanTime" |
| | | placeholder="è¯·éæ©è®¡åä¿å
»æ¥æ" |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable /> |
| | | <template #right> |
| | | <u-icon name="arrow-right" |
| | | @click="showDatePicker" /> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="ä¿å
»äºº" |
| | | prop="maintenancePerson" |
| | | border-bottom> |
| | | <u-input v-model="form.maintenancePerson" |
| | | placeholder="请è¾å
¥ä¿å
»äºº" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="ä¿å
»é¡¹ç®" |
| | | prop="machineryCategory" |
| | | border-bottom> |
| | | <u-input v-model="form.machineryCategory" |
| | | placeholder="请è¾å
¥ä¿å
»é¡¹ç®" |
| | | clearable /> |
| | | </u-form-item> |
| | | <!-- æäº¤æé® --> |
| | | <view class="footer-btns"> |
| | | <u-button class="cancel-btn" |
| | | @click="goBack">åæ¶</u-button> |
| | | <u-button class="save-btn" |
| | | @click="sendForm" |
| | | :loading="loading">ä¿å</u-button> |
| | | </view> |
| | | </u-form> |
| | | <!-- 设å¤éæ©å¨ --> |
| | | <up-action-sheet :show="showDevice" |
| | | :actions="deviceActions" |
| | | title="éæ©è®¾å¤" |
| | | @select="onDeviceConfirm" |
| | | @close="showDevice = false" /> |
| | | <up-datetime-picker :show="showDate" |
| | | v-model="pickerDateValue" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" /> |
| | | </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 { formatDateToYMD } from '@/utils/ruoyi'; |
| | | 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 { formatDateToYMD } from "@/utils/ruoyi"; |
| | | |
| | | defineOptions({ |
| | | name: "设å¤ä¿å
»è®¡å表å", |
| | | }); |
| | | const showToast = (message) => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: 'none' |
| | | }) |
| | | } |
| | | defineOptions({ |
| | | name: "设å¤ä¿å
»è®¡å表å", |
| | | }); |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const operationType = ref('add'); |
| | | const loading = ref(false); |
| | | const showDevice = ref(false); |
| | | const showDate = ref(false); |
| | | const pickerDateValue = ref(Date.now()); |
| | | const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]); |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | const operationType = ref("add"); |
| | | const loading = ref(false); |
| | | const showDevice = ref(false); |
| | | const showDate = ref(false); |
| | | const pickerDateValue = ref(Date.now()); |
| | | const currentDate = ref([ |
| | | new Date().getFullYear(), |
| | | new Date().getMonth() + 1, |
| | | new Date().getDate(), |
| | | ]); |
| | | |
| | | // 设å¤é项 |
| | | const deviceOptions = ref([]); |
| | | const deviceNameText = ref(''); |
| | | // 转æ¢ä¸º action-sheet éè¦çæ ¼å¼ |
| | | const deviceActions = computed(() => { |
| | | return deviceOptions.value.map(item => ({ |
| | | text: item.deviceName, |
| | | value: item.id, |
| | | data: item |
| | | })); |
| | | }); |
| | | // 设å¤é项 |
| | | const deviceOptions = ref([]); |
| | | const deviceNameText = ref(""); |
| | | // 转æ¢ä¸º action-sheet éè¦çæ ¼å¼ |
| | | const deviceActions = computed(() => { |
| | | return deviceOptions.value.map(item => ({ |
| | | text: item.deviceName, |
| | | value: item.id, |
| | | data: item, |
| | | })); |
| | | }); |
| | | |
| | | // æ«ç ç¸å
³ç¶æ |
| | | const isScanning = ref(false); |
| | | const scanTimer = ref(null); |
| | | // æ«ç ç¸å
³ç¶æ |
| | | const isScanning = ref(false); |
| | | const scanTimer = ref(null); |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | deviceLedgerId: [{ required: true, trigger: "change", message: "è¯·éæ©è®¾å¤åç§°" }], |
| | | maintenancePlanTime: [{ required: true, trigger: "change", message: "è¯·éæ©è®¡åä¿å
»æ¥æ" }], |
| | | }; |
| | | // 表åéªè¯è§å |
| | | 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"), // 计åä¿å
»æ¥æ |
| | | }); |
| | | // ä½¿ç¨ ref 声æè¡¨åæ°æ® |
| | | const form = ref({ |
| | | deviceLedgerId: undefined, // 设å¤ID |
| | | deviceModel: undefined, // è§æ ¼åå· |
| | | maintenancePlanTime: dayjs().format("YYYY-MM-DD"), // 计åä¿å
»æ¥æ |
| | | maintenancePerson: undefined, // ä¿å
»äºº |
| | | machineryCategory: undefined, // ä¿å
»é¡¹ç® |
| | | }); |
| | | |
| | | // å 载设å¤å表 |
| | | const loadDeviceName = async () => { |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (e) { |
| | | showToast('è·å设å¤å表失败'); |
| | | } |
| | | }; |
| | | // å 载设å¤å表 |
| | | const loadDeviceName = async () => { |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (e) { |
| | | showToast("è·å设å¤å表失败"); |
| | | } |
| | | }; |
| | | |
| | | // å è½½è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | 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) { |
| | | form.value.deviceNameText = device.deviceName; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | showToast('è·å详æ
失败'); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | operationType.value = 'add'; |
| | | } |
| | | }; |
| | | // å è½½è¡¨åæ°æ®ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | 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" |
| | | ); |
| | | form.value.maintenancePerson = data.maintenancePerson; |
| | | form.value.machineryCategory = data.machineryCategory; |
| | | // 设置设å¤åç§°æ¾ç¤º |
| | | const device = deviceOptions.value.find( |
| | | item => item.id === data.deviceLedgerId |
| | | ); |
| | | if (device) { |
| | | form.value.deviceNameText = device.deviceName; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | showToast("è·å详æ
失败"); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | operationType.value = "add"; |
| | | } |
| | | }; |
| | | |
| | | // æ«æäºç»´ç åè½ |
| | | 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 startScan = () => { |
| | | if (isScanning.value) { |
| | | showToast("æ£å¨æ«æä¸ï¼è¯·ç¨å..."); |
| | | return; |
| | | } |
| | | |
| | | // å¤çæ«ç ç»æ |
| | | const handleScanResult = (scanResult) => { |
| | | if (!scanResult) { |
| | | showToast('æ«ç ç»æä¸ºç©º'); |
| | | return; |
| | | } |
| | | |
| | | isScanning.value = true; |
| | | showToast('æ«ç æå'); |
| | | |
| | | // 3ç§åå¤çæ«ç ç»æ |
| | | scanTimer.value = setTimeout(() => { |
| | | processScanResult(scanResult); |
| | | isScanning.value = false; |
| | | }, 1000); |
| | | }; |
| | | function getDeviceIdByRegExp(url) { |
| | | // å¹é
deviceId=åé¢çæ°å |
| | | const reg = /deviceId=(\d+)/; |
| | | const match = url.match(reg); |
| | | // 妿å¹é
å°ç»æï¼è¿åæ°åç±»åï¼å¦åè¿ånull |
| | | return match ? Number(match[1]) : null; |
| | | } |
| | | // å¤çæ«ç ç»æå¹¶å¹é
è®¾å¤ |
| | | const processScanResult = (scanResult) => { |
| | | const deviceId = getDeviceIdByRegExp(scanResult); |
| | | const matchedDevice = deviceOptions.value.find(item => item.id == deviceId); |
| | | |
| | | if (matchedDevice) { |
| | | // æ¾å°å¹é
ç设å¤ï¼èªå¨å¡«å
|
| | | form.value.deviceLedgerId = matchedDevice.id; |
| | | form.value.deviceNameText = matchedDevice.deviceName; |
| | | form.value.deviceModel = matchedDevice.deviceModel; |
| | | showToast('设å¤ä¿¡æ¯å·²èªå¨å¡«å
'); |
| | | } else { |
| | | // æªæ¾å°å¹é
çè®¾å¤ |
| | | showToast('æªæ¾å°å¹é
ç设å¤ï¼è¯·æå¨éæ©'); |
| | | } |
| | | }; |
| | | // è°ç¨uni-appçæ«ç API |
| | | uni.scanCode({ |
| | | scanType: ["qrCode", "barCode"], |
| | | success: res => { |
| | | handleScanResult(res.result); |
| | | }, |
| | | fail: err => { |
| | | console.error("æ«ç 失败:", err); |
| | | showToast("æ«ç 失败ï¼è¯·éè¯"); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºè®¾å¤éæ©å¨ |
| | | const showDevicePicker = () => { |
| | | showDevice.value = true; |
| | | }; |
| | | // å¤çæ«ç ç»æ |
| | | const handleScanResult = scanResult => { |
| | | if (!scanResult) { |
| | | showToast("æ«ç ç»æä¸ºç©º"); |
| | | return; |
| | | } |
| | | |
| | | // 确认设å¤éæ© |
| | | const onDeviceConfirm = (selected) => { |
| | | // selected è¿åçæ¯éä¸é¡¹ |
| | | form.value.deviceLedgerId = selected.value; |
| | | form.value.deviceNameText = selected.name; |
| | | const selectedDevice = deviceOptions.value.find(item => item.id === selected.value); |
| | | if (selectedDevice) { |
| | | form.value.deviceModel = selectedDevice.deviceModel; |
| | | } |
| | | showDevice.value = false; |
| | | }; |
| | | isScanning.value = true; |
| | | showToast("æ«ç æå"); |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | // 3ç§åå¤çæ«ç ç»æ |
| | | scanTimer.value = setTimeout(() => { |
| | | processScanResult(scanResult); |
| | | isScanning.value = false; |
| | | }, 1000); |
| | | }; |
| | | function getDeviceIdByRegExp(url) { |
| | | // å¹é
deviceId=åé¢çæ°å |
| | | const reg = /deviceId=(\d+)/; |
| | | const match = url.match(reg); |
| | | // 妿å¹é
å°ç»æï¼è¿åæ°åç±»åï¼å¦åè¿ånull |
| | | return match ? Number(match[1]) : null; |
| | | } |
| | | // å¤çæ«ç ç»æå¹¶å¹é
è®¾å¤ |
| | | const processScanResult = scanResult => { |
| | | const deviceId = getDeviceIdByRegExp(scanResult); |
| | | const matchedDevice = deviceOptions.value.find(item => item.id == deviceId); |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const onDateConfirm = (e) => { |
| | | form.value.maintenancePlanTime = formatDateToYMD(e.value); |
| | | showDate.value = false; |
| | | }; |
| | | if (matchedDevice) { |
| | | // æ¾å°å¹é
ç设å¤ï¼èªå¨å¡«å
|
| | | form.value.deviceLedgerId = matchedDevice.id; |
| | | form.value.deviceNameText = matchedDevice.deviceName; |
| | | form.value.deviceModel = matchedDevice.deviceModel; |
| | | showToast("设å¤ä¿¡æ¯å·²èªå¨å¡«å
"); |
| | | } else { |
| | | // æªæ¾å°å¹é
çè®¾å¤ |
| | | showToast("æªæ¾å°å¹é
ç设å¤ï¼è¯·æå¨éæ©"); |
| | | } |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶è·ååæ° |
| | | getPageParams(); |
| | | }); |
| | | // æ¾ç¤ºè®¾å¤éæ©å¨ |
| | | const showDevicePicker = () => { |
| | | showDevice.value = true; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·å设å¤å表ååæ° |
| | | loadDeviceName(); |
| | | getPageParams(); |
| | | }); |
| | | // 确认设å¤éæ© |
| | | const onDeviceConfirm = selected => { |
| | | // selected è¿åçæ¯éä¸é¡¹ |
| | | form.value.deviceLedgerId = selected.value; |
| | | form.value.deviceNameText = selected.name; |
| | | const selectedDevice = deviceOptions.value.find( |
| | | item => item.id === selected.value |
| | | ); |
| | | if (selectedDevice) { |
| | | form.value.deviceModel = selectedDevice.deviceModel; |
| | | } |
| | | showDevice.value = false; |
| | | }; |
| | | |
| | | // ç»ä»¶å¸è½½æ¶æ¸
ç宿¶å¨ |
| | | onUnmounted(() => { |
| | | if (scanTimer.value) { |
| | | clearTimeout(scanTimer.value); |
| | | } |
| | | }); |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const sendForm = async () => { |
| | | try { |
| | | // æå¨éªè¯è¡¨å |
| | | const valid = await formRef.value.validate(); |
| | | if (!valid) return; |
| | | |
| | | 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 onDateConfirm = e => { |
| | | form.value.maintenancePlanTime = formatDateToYMD(e.value); |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | // æ¸
é¤åå¨çid |
| | | uni.removeStorageSync('repairId'); |
| | | uni.navigateBack(); |
| | | }; |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶è·ååæ° |
| | | getPageParams(); |
| | | }); |
| | | |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | // 仿¬å°åå¨è·åid |
| | | const id = uni.getStorageSync('repairId'); |
| | | |
| | | // æ ¹æ®æ¯å¦æidåæ°æ¥å¤ææ¯æ°å¢è¿æ¯ç¼è¾ |
| | | if (id) { |
| | | // ç¼è¾æ¨¡å¼ï¼è·å详æ
|
| | | loadForm(id); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | loadForm(); |
| | | } |
| | | }; |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·å设å¤å表ååæ° |
| | | loadDeviceName(); |
| | | getPageParams(); |
| | | }); |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | // 仿¬å°åå¨è·åid |
| | | return uni.getStorageSync('repairId'); |
| | | }; |
| | | // ç»ä»¶å¸è½½æ¶æ¸
ç宿¶å¨ |
| | | onUnmounted(() => { |
| | | if (scanTimer.value) { |
| | | clearTimeout(scanTimer.value); |
| | | } |
| | | }); |
| | | |
| | | // æäº¤è¡¨å |
| | | const sendForm = async () => { |
| | | try { |
| | | // æå¨éªè¯è¡¨å |
| | | const valid = await formRef.value.validate(); |
| | | if (!valid) return; |
| | | |
| | | 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 = () => { |
| | | // æ¸
é¤åå¨çid |
| | | uni.removeStorageSync("repairId"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è·å页é¢åæ° |
| | | const getPageParams = () => { |
| | | // 仿¬å°åå¨è·åid |
| | | const id = uni.getStorageSync("repairId"); |
| | | |
| | | // æ ¹æ®æ¯å¦æidåæ°æ¥å¤ææ¯æ°å¢è¿æ¯ç¼è¾ |
| | | if (id) { |
| | | // ç¼è¾æ¨¡å¼ï¼è·å详æ
|
| | | loadForm(id); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | loadForm(); |
| | | } |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | // 仿¬å°åå¨è·åid |
| | | return uni.getStorageSync("repairId"); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/static/scss/form-common.scss'; |
| | | .upkeep-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | @import "@/static/scss/form-common.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; |
| | | } |
| | | .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; |
| | | } |
| | | .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; |
| | | } |
| | | .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; |
| | | } |
| | | } |
| | | // ååºå¼è°æ´ |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | |
| | | .scan-icon { |
| | | color: #1989fa; |
| | | font-size: 18px; |
| | | margin-left: 8px; |
| | | cursor: pointer; |
| | | } |
| | | .scan-icon { |
| | | color: #1989fa; |
| | | font-size: 18px; |
| | | margin-left: 8px; |
| | | cursor: pointer; |
| | | } |
| | | </style> |
| | |
| | | <text class="detail-value">{{ formatDateTime(item.createTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¿å
»äºº</text> |
| | | <text class="detail-value">{{ item.maintenancePerson || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¿å
»é¡¹ç®</text> |
| | | <text class="detail-value">{{ item.machineryCategory || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å®é
ä¿å
»äºº</text> |
| | | <text class="detail-value">{{ item.maintenanceActuallyName || '-' }}</text> |
| | | </view> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="attachment-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader :title="`æ¥çéä»¶ - ${taskInfo?.taskName || ''}`" @back="goBack" /> |
| | | |
| | | <!-- 页é¢å
容 --> |
| | | <view class="attachment-content"> |
| | | <!-- åç±»æ ç¾é¡µ --> |
| | | <view class="attachment-tabs"> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentViewType === 'before' }" |
| | | @click="switchViewType('before')" |
| | | > |
| | | ç产å ({{ getAttachmentsByType(0).length }}) |
| | | </view> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentViewType === 'after' }" |
| | | @click="switchViewType('after')" |
| | | > |
| | | çäº§ä¸ ({{ getAttachmentsByType(1).length }}) |
| | | </view> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentViewType === 'issue' }" |
| | | @click="switchViewType('issue')" |
| | | > |
| | | ç产å ({{ getAttachmentsByType(2).length }}) |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å½ååç±»çéä»¶å表 --> |
| | | <view class="attachment-list-container"> |
| | | <view v-if="getCurrentViewAttachments().length > 0" class="attachment-list"> |
| | | <view |
| | | v-for="(file, index) in getCurrentViewAttachments()" |
| | | :key="index" |
| | | class="attachment-item" |
| | | @click="previewAttachment(file)" |
| | | > |
| | | <view class="attachment-preview-container"> |
| | | <image |
| | | v-if="isImageFile(file)" |
| | | :src="file.url || file.downloadUrl" |
| | | class="attachment-preview" |
| | | mode="aspectFill" |
| | | /> |
| | | <view v-else class="attachment-video-preview"> |
| | | <u-icon name="video" size="40" color="#409eff"></u-icon> |
| | | <text class="video-text">è§é¢</text> |
| | | </view> |
| | | </view> |
| | | <view class="attachment-info"> |
| | | <text class="attachment-name">{{ file.originalFilename || file.bucketFilename || file.name || 'éä»¶' }}</text> |
| | | <text class="attachment-size">{{ formatFileSize(file.byteSize || file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="attachment-empty"> |
| | | <u-icon name="folder-open" size="60" color="#ccc"></u-icon> |
| | | <text class="empty-text">该åç±»ææ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- è§é¢é¢è§å¼¹çª --> |
| | | <view v-if="showVideoDialog" class="video-modal-overlay" @click="closeVideoPreview"> |
| | | <view class="video-modal-container" @click.stop> |
| | | <view class="video-modal-header"> |
| | | <text class="video-modal-title">{{ currentVideoFile?.originalFilename || 'è§é¢é¢è§' }}</text> |
| | | <view class="close-btn-video" @click="closeVideoPreview"> |
| | | <u-icon name="close" size="20" color="#fff"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="video-modal-body"> |
| | | <video |
| | | v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | @error="handleVideoError" |
| | | ></video> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import { onLoad } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import config from '@/config'; |
| | | |
| | | // ä»»å¡ä¿¡æ¯ |
| | | const taskInfo = ref(null); |
| | | |
| | | // éä»¶å表 |
| | | const attachmentList = ref([]); |
| | | |
| | | // å½åæ¥çç±»å |
| | | const currentViewType = ref('before'); // 'before', 'after', 'issue' |
| | | |
| | | // è§é¢é¢è§ç¸å
³ç¶æ |
| | | const showVideoDialog = ref(false); |
| | | const currentVideoFile = ref(null); |
| | | |
| | | // æä»¶è®¿é®åºç¡å |
| | | const filePreviewBase = config.fileUrl; |
| | | |
| | | // 页é¢å è½½ |
| | | onLoad((options) => { |
| | | if (options.taskInfo) { |
| | | try { |
| | | taskInfo.value = JSON.parse(decodeURIComponent(options.taskInfo)); |
| | | loadAttachments(); |
| | | } catch (e) { |
| | | console.error('è§£æä»»å¡ä¿¡æ¯å¤±è´¥:', e); |
| | | uni.showToast({ |
| | | title: 'å 载失败', |
| | | icon: 'error' |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | // å è½½éä»¶æ°æ® |
| | | const loadAttachments = () => { |
| | | const task = taskInfo.value; |
| | | if (!task) return; |
| | | |
| | | attachmentList.value = []; |
| | | |
| | | // åç«¯åæ¾åæ®µ |
| | | const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : []; |
| | | const beforeList = Array.isArray(task?.commonFileListBefore) |
| | | ? task.commonFileListBefore |
| | | : allList.filter((f) => f?.type === 10); |
| | | const afterList = Array.isArray(task?.commonFileListAfter) |
| | | ? task.commonFileListAfter |
| | | : allList.filter((f) => f?.type === 11); |
| | | const issueList = Array.isArray(task?.commonFileListIssue) |
| | | ? task.commonFileListIssue |
| | | : allList.filter((f) => f?.type === 12); |
| | | |
| | | const mapToViewFile = (file, viewType) => { |
| | | const u = normalizeFileUrl(file?.url || file?.downloadUrl || ''); |
| | | return { |
| | | ...file, |
| | | type: viewType, |
| | | name: file?.name || file?.originalFilename || file?.bucketFilename, |
| | | bucketFilename: file?.bucketFilename || file?.name, |
| | | originalFilename: file?.originalFilename || file?.name, |
| | | url: u, |
| | | downloadUrl: u, |
| | | size: file?.size || file?.byteSize, |
| | | }; |
| | | }; |
| | | |
| | | attachmentList.value.push(...beforeList.map((f) => mapToViewFile(f, 0))); |
| | | attachmentList.value.push(...afterList.map((f) => mapToViewFile(f, 1))); |
| | | attachmentList.value.push(...issueList.map((f) => mapToViewFile(f, 2))); |
| | | }; |
| | | |
| | | // å°å端è¿åçæä»¶å°åè§èæå¯è®¿é®URL |
| | | const normalizeFileUrl = (rawUrl) => { |
| | | try { |
| | | if (!rawUrl || typeof rawUrl !== 'string') return ''; |
| | | const url = rawUrl.trim(); |
| | | if (!url) return ''; |
| | | if (/^https?:\/\//i.test(url)) return url; |
| | | if (url.startsWith('/')) return `${filePreviewBase}${url}`; |
| | | |
| | | // Windows path -> web path |
| | | if (/^[a-zA-Z]:\\/.test(url)) { |
| | | const normalized = url.replace(/\\/g, '/'); |
| | | const idx = normalized.indexOf('/prod/'); |
| | | if (idx >= 0) { |
| | | const relative = normalized.slice(idx + '/prod/'.length); |
| | | return `${filePreviewBase}/${relative}`; |
| | | } |
| | | return normalized; |
| | | } |
| | | |
| | | return `${filePreviewBase}/${url.replace(/^\//, '')}`; |
| | | } catch (e) { |
| | | return rawUrl || ''; |
| | | } |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // 忢æ¥çç±»å |
| | | const switchViewType = (type) => { |
| | | currentViewType.value = type; |
| | | }; |
| | | |
| | | // æ ¹æ®typeè·å对åºåç±»çéä»¶ |
| | | const getAttachmentsByType = (typeValue) => { |
| | | return attachmentList.value.filter((file) => file.type === typeValue) || []; |
| | | }; |
| | | |
| | | // è·åå½åæ¥çç±»åçéä»¶ |
| | | const getCurrentViewAttachments = () => { |
| | | switch (currentViewType.value) { |
| | | case 'before': |
| | | return getAttachmentsByType(0); |
| | | case 'after': |
| | | return getAttachmentsByType(1); |
| | | case 'issue': |
| | | return getAttachmentsByType(2); |
| | | default: |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºå¾çæä»¶ |
| | | const isImageFile = (file) => { |
| | | if (file.contentType && file.contentType.startsWith('image/')) { |
| | | return true; |
| | | } |
| | | if (file.type === 'image') return true; |
| | | |
| | | const name = file.bucketFilename || file.originalFilename || file.name || ''; |
| | | const ext = name.split('.').pop()?.toLowerCase(); |
| | | return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext); |
| | | }; |
| | | |
| | | // é¢è§éä»¶ |
| | | const previewAttachment = (file) => { |
| | | if (isImageFile(file)) { |
| | | const imageUrls = getCurrentViewAttachments() |
| | | .filter((f) => isImageFile(f)) |
| | | .map((f) => f.url || f.downloadUrl); |
| | | |
| | | uni.previewImage({ |
| | | urls: imageUrls, |
| | | current: file.url || file.downloadUrl, |
| | | }); |
| | | } else { |
| | | showVideoPreview(file); |
| | | } |
| | | }; |
| | | |
| | | // æ¾ç¤ºè§é¢é¢è§ |
| | | const showVideoPreview = (file) => { |
| | | currentVideoFile.value = file; |
| | | showVideoDialog.value = true; |
| | | }; |
| | | |
| | | // å
³éè§é¢é¢è§ |
| | | const closeVideoPreview = () => { |
| | | showVideoDialog.value = false; |
| | | currentVideoFile.value = null; |
| | | }; |
| | | |
| | | // è§é¢ææ¾é误å¤ç |
| | | const handleVideoError = () => { |
| | | uni.showToast({ |
| | | title: 'è§é¢ææ¾å¤±è´¥', |
| | | icon: 'error', |
| | | }); |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = (size) => { |
| | | if (!size) return ''; |
| | | if (size < 1024) return size + 'B'; |
| | | if (size < 1024 * 1024) return (size / 1024).toFixed(1) + 'KB'; |
| | | return (size / (1024 * 1024)).toFixed(1) + 'MB'; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .attachment-page { |
| | | min-height: 100vh; |
| | | background-color: #f5f5f5; |
| | | } |
| | | |
| | | .attachment-content { |
| | | padding: 15px; |
| | | } |
| | | |
| | | /* æ ç¾é¡µæ ·å¼ */ |
| | | .attachment-tabs { |
| | | display: flex; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | margin-bottom: 15px; |
| | | padding: 4px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .tab-item { |
| | | flex: 1; |
| | | text-align: center; |
| | | padding: 12px 8px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .tab-item.active { |
| | | background: #409eff; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | /* éä»¶åè¡¨æ ·å¼ */ |
| | | .attachment-list-container { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | min-height: 400px; |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 15px; |
| | | } |
| | | |
| | | .attachment-item { |
| | | width: calc(33.33% - 10px); |
| | | background: #f8f9fa; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .attachment-item:active { |
| | | transform: scale(0.98); |
| | | } |
| | | |
| | | .attachment-preview-container { |
| | | width: 100%; |
| | | height: 120px; |
| | | background: #e9ecef; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .attachment-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .attachment-video-preview { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .attachment-info { |
| | | padding: 10px; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .attachment-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | } |
| | | |
| | | /* ç©ºç¶ææ ·å¼ */ |
| | | .attachment-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 80px 20px; |
| | | color: #999; |
| | | } |
| | | |
| | | .empty-text { |
| | | margin-top: 15px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | /* è§é¢å¼¹çªæ ·å¼ */ |
| | | .video-modal-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.9); |
| | | z-index: 10000; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .video-modal-container { |
| | | width: 100%; |
| | | max-width: 800px; |
| | | background: #000; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .video-modal-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | background: #1a1a1a; |
| | | } |
| | | |
| | | .video-modal-title { |
| | | font-size: 16px; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .close-btn-video { |
| | | width: 32px; |
| | | height: 32px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: rgba(255, 255, 255, 0.1); |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .video-modal-body { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .video-player { |
| | | width: 100%; |
| | | height: 400px; |
| | | border-radius: 8px; |
| | | } |
| | | </style> |
| | |
| | | > |
| | | <view class="popup-content"> |
| | | <view class="popup-header"> |
| | | <text class="popup-title">ä¸ä¼ </text> |
| | | <text class="popup-title">å·¡æ£è®°å½ä¸ä¼ </text> |
| | | </view> |
| | | |
| | | <view class="upload-container"> |
| | | <!-- å¼å¸¸ç¶æéæ© --> |
| | | <view class="form-container"> |
| | | <view class="title">ç产å</view> |
| | | <u-upload |
| | | :fileList="beforeModelValue" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | name="before" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | <view class="title">å·¡æ£ç¶æ</view> |
| | | <view class="exception-section"> |
| | | <view class="exception-options"> |
| | | <view |
| | | class="exception-option" |
| | | :class="{ active: hasException === false }" |
| | | @click="setExceptionStatus(false)" |
| | | > |
| | | <u-icon name="checkmark-circle" size="20" color="#52c41a"></u-icon> |
| | | <text class="option-text">æ£å¸¸</text> |
| | | </view> |
| | | <view |
| | | class="exception-option" |
| | | :class="{ active: hasException === true }" |
| | | @click="setExceptionStatus(true)" |
| | | > |
| | | <u-icon name="close-circle" size="20" color="#ff4d4f"></u-icon> |
| | | <text class="option-text">åå¨å¼å¸¸</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å¼å¸¸æè¿°ï¼ä»
å¨å¼å¸¸æ¶æ¾ç¤ºï¼ --> |
| | | <view class="form-container" v-if="hasException === true"> |
| | | <view class="title">å¼å¸¸æè¿°</view> |
| | | <u-input |
| | | v-model="exceptionDescription" |
| | | type="textarea" |
| | | :maxlength="500" |
| | | placeholder="请æè¿°å¼å¸¸æ
åµ..." |
| | | :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }" |
| | | /> |
| | | </view> |
| | | |
| | | <view class="form-container"> |
| | | <view class="title">ç产å</view> |
| | | <u-upload |
| | | :fileList="afterModelValue" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | name="after" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | </view> |
| | | |
| | | <view class="form-container"> |
| | | <view class="title">ç产é®é¢</view> |
| | | <u-upload |
| | | :fileList="issueModelValue" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | name="issue" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | <!-- ä¸ä¼ åºåï¼ä»
å¨å¼å¸¸æ¶æ¾ç¤ºï¼ --> |
| | | <template v-if="hasException === true"> |
| | | <view class="form-container"> |
| | | <view class="title">ç产å</view> |
| | | <u-upload |
| | | :fileList="beforeModelValue" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | name="before" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | </view> |
| | | |
| | | <view class="form-container"> |
| | | <view class="title">ç产å</view> |
| | | <u-upload |
| | | :fileList="afterModelValue" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | name="after" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | </view> |
| | | |
| | | <view class="form-container"> |
| | | <view class="title">ç产é®é¢</view> |
| | | <u-upload |
| | | :fileList="issueModelValue" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | name="issue" |
| | | multiple |
| | | :maxCount="10" |
| | | :maxSize="5 * 1024 * 1024" |
| | | accept="image/*" |
| | | :previewFullImage="true" |
| | | ></u-upload> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- æ£å¸¸ç¶ææç¤º --> |
| | | <view class="form-container normal-tip" v-if="hasException === false"> |
| | | <u-icon name="info-circle" size="40" color="#52c41a"></u-icon> |
| | | <text class="tip-text">设å¤è¿è¡æ£å¸¸ï¼æ éä¸ä¼ ç
§ç</text> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | const afterModelValue = ref([]) |
| | | const issueModelValue = ref([]) |
| | | const infoData = ref(null) |
| | | |
| | | // å¼å¸¸ç¶æï¼null=æªéæ©, false=æ£å¸¸, true=å¼å¸¸ |
| | | const hasException = ref(null) |
| | | // å¼å¸¸æè¿° |
| | | const exceptionDescription = ref('') |
| | | |
| | | // 计ç®ä¸ä¼ URL |
| | | const uploadFileUrl = computed(() => { |
| | |
| | | } |
| | | } |
| | | |
| | | // 设置å¼å¸¸ç¶æ |
| | | const setExceptionStatus = (status) => { |
| | | hasException.value = status |
| | | } |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | | // æ£æ¥æ¯å¦éæ©äºå·¡æ£ç¶æ |
| | | if (hasException.value === null) { |
| | | uni.showToast({ |
| | | title: 'è¯·éæ©å·¡æ£ç¶æ', |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | } |
| | | |
| | | // 妿æ¯å¼å¸¸ç¶æï¼æ£æ¥æ¯å¦æä¸ä¼ æä»¶ |
| | | if (hasException.value === true) { |
| | | const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length |
| | | if (totalFiles === 0) { |
| | | uni.showToast({ |
| | | title: '请ä¸ä¼ å¼å¸¸ç
§ç', |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | } |
| | | // æ£æ¥æ¯å¦å¡«åäºå¼å¸¸æè¿° |
| | | if (!exceptionDescription.value.trim()) { |
| | | uni.showToast({ |
| | | title: '请填åå¼å¸¸æè¿°', |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | } |
| | | } |
| | | |
| | | let arr = [] |
| | | if (beforeModelValue.value.length > 0) { |
| | | arr.push(...beforeModelValue.value.map(item => ({ ...item, statusType: 0 }))) |
| | |
| | | |
| | | // æäº¤æ°æ® |
| | | infoData.value.storageBlobDTO = arr |
| | | infoData.value.hasException = hasException.value |
| | | infoData.value.exceptionDescription = exceptionDescription.value |
| | | await submitInspectionRecord({ ...infoData.value }) |
| | | |
| | | uni.showToast({ |
| | |
| | | beforeModelValue.value = [] |
| | | afterModelValue.value = [] |
| | | issueModelValue.value = [] |
| | | hasException.value = null |
| | | exceptionDescription.value = '' |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | |
| | | border-top: 1px solid #f0f0f0; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | // å¼å¸¸ç¶æéæ©æ ·å¼ |
| | | .exception-section { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .exception-options { |
| | | display: flex; |
| | | gap: 15px; |
| | | } |
| | | |
| | | .exception-option { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | padding: 15px 20px; |
| | | border: 2px solid #e0e0e0; |
| | | border-radius: 8px; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | background-color: #fff; |
| | | |
| | | &.active { |
| | | border-color: #1890ff; |
| | | background-color: #e6f7ff; |
| | | } |
| | | |
| | | &:active { |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | |
| | | .option-text { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | // æ£å¸¸ç¶ææç¤ºæ ·å¼ |
| | | .normal-tip { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 40px 20px; |
| | | background-color: #f6ffed; |
| | | border: 1px dashed #b7eb8f; |
| | | border-radius: 8px; |
| | | |
| | | .tip-text { |
| | | margin-top: 15px; |
| | | font-size: 14px; |
| | | color: #52c41a; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <text class="detail-value">{{ item.taskId || item.id }}</text> |
| | | </view> |
| | | <view class="detail-item"> |
| | | <text class="detail-label">å·¡æ£é¡¹ç®</text> |
| | | <text class="detail-value">{{ item.inspectionProject || 'æ ' }}</text> |
| | | </view> |
| | | <view class="detail-item"> |
| | | <text class="detail-label">夿³¨</text> |
| | | <text class="detail-value">{{ item.remarks || 'æ ' }}</text> |
| | | </view> |
| | |
| | | <view v-if="taskTableData?.length === 0" |
| | | class="no-data"> |
| | | <text>ææ æ°æ®</text> |
| | | </view> |
| | | </view> |
| | | <!-- å¾çä¸ä¼ å¼¹çª - åçå®ç° --> |
| | | <view v-if="showUploadDialog" |
| | | class="custom-modal-overlay" |
| | | @click="closeUploadDialog"> |
| | | <view class="custom-modal-container" |
| | | @click.stop> |
| | | <view class="upload-popup-content"> |
| | | <view class="upload-popup-header"> |
| | | <text class="upload-popup-title">ä¸ä¼ å·¡æ£è®°å½</text> |
| | | </view> |
| | | <view class="upload-popup-body"> |
| | | <!-- åç±»æ ç¾é¡µ --> |
| | | <view class="upload-tabs"> |
| | | <view class="tab-item" |
| | | :class="{ active: currentUploadType === 'before' }" |
| | | @click="switchUploadType('before')"> |
| | | ç产å |
| | | </view> |
| | | <view class="tab-item" |
| | | :class="{ active: currentUploadType === 'after' }" |
| | | @click="switchUploadType('after')"> |
| | | çäº§ä¸ |
| | | </view> |
| | | <view class="tab-item" |
| | | :class="{ active: currentUploadType === 'issue' }" |
| | | @click="switchUploadType('issue')"> |
| | | ç产å |
| | | </view> |
| | | </view> |
| | | <!-- å¼å¸¸ç¶æéæ© --> |
| | | <view class="exception-section"> |
| | | <text class="section-title">æ¯å¦åå¨å¼å¸¸ï¼</text> |
| | | <view class="exception-options"> |
| | | <view class="exception-option" |
| | | :class="{ active: hasException === false }" |
| | | @click="setExceptionStatus(false)"> |
| | | <u-icon name="checkmark-circle" |
| | | size="20" |
| | | color="#52c41a"></u-icon> |
| | | <text>æ£å¸¸</text> |
| | | </view> |
| | | <view class="exception-option" |
| | | :class="{ active: hasException === true }" |
| | | @click="setExceptionStatus(true)"> |
| | | <u-icon name="close-circle" |
| | | size="20" |
| | | color="#ff4d4f"></u-icon> |
| | | <text>åå¨å¼å¸¸</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- å½ååç±»çä¸ä¼ åºå --> |
| | | <view class="simple-upload-area"> |
| | | <view class="upload-buttons"> |
| | | <u-button type="primary" |
| | | @click="chooseMedia('image')" |
| | | :loading="uploading" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }"> |
| | | <u-icon name="camera" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px;"></u-icon> |
| | | {{ uploading ? 'ä¸ä¼ ä¸...' : 'æç
§' }} |
| | | </u-button> |
| | | <u-button type="success" |
| | | @click="chooseMedia('video')" |
| | | :loading="uploading" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit" |
| | | :customStyle="{ flex: 1 }"> |
| | | <uni-icons type="videocam" |
| | | name="videocam" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px;"></uni-icons> |
| | | {{ uploading ? 'ä¸ä¼ ä¸...' : 'æè§é¢' }} |
| | | </u-button> |
| | | </view> |
| | | <!-- ä¸ä¼ è¿åº¦ --> |
| | | <view v-if="uploading" |
| | | class="upload-progress"> |
| | | <u-line-progress :percentage="uploadProgress" |
| | | :showText="true" |
| | | activeColor="#409eff"></u-line-progress> |
| | | </view> |
| | | <!-- å½ååç±»çæä»¶å表 --> |
| | | <view v-if="getCurrentFiles().length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in getCurrentFiles()" |
| | | :key="index" |
| | | class="file-item"> |
| | | <view class="file-preview-container"> |
| | | <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | class="file-preview" |
| | | mode="aspectFill" /> |
| | | <view v-else-if="file.type === 'video'" |
| | | class="video-preview"> |
| | | <uni-icons type="videocam" |
| | | name="videocam" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px;"></uni-icons> |
| | | <text class="video-text">è§é¢</text> |
| | | </view> |
| | | <!-- å é¤æé® --> |
| | | <view class="delete-btn" |
| | | @click="removeFile(index)"> |
| | | <u-icon name="close" |
| | | size="12" |
| | | color="#fff"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? 'å¾ç' : 'è§é¢') |
| | | }}</text> |
| | | <text class="file-size">{{ formatFileSize(file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="getCurrentFiles().length === 0" |
| | | class="empty-state"> |
| | | <text>è¯·éæ©è¦ä¸ä¼ ç{{ getUploadTypeText() }}å¾çæè§é¢</text> |
| | | </view> |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <view class="upload-summary"> |
| | | <text class="summary-text"> |
| | | ç产å: {{ beforeModelValue.length }}个æä»¶ | |
| | | ç产ä¸: {{ afterModelValue.length }}个æä»¶ | |
| | | ç产å: {{ issueModelValue.length }}个æä»¶ |
| | | </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="upload-popup-footer"> |
| | | <u-button @click="closeUploadDialog" |
| | | :customStyle="{ marginRight: '10px' }">åæ¶</u-button> |
| | | <u-button v-if="hasException === true" |
| | | type="warning" |
| | | @click="goToRepair" |
| | | :customStyle="{ marginRight: '10px' }"> |
| | | æ°å¢æ¥ä¿® |
| | | </u-button> |
| | | <u-button type="primary" |
| | | @click="submitUpload">æäº¤</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- æ¥çéä»¶å¼¹çª --> |
| | | <view v-if="showAttachmentDialog" |
| | | class="custom-modal-overlay" |
| | | @click="closeAttachmentDialog"> |
| | | <view class="custom-modal-container" |
| | | @click.stop> |
| | | <view class="attachment-popup-content"> |
| | | <view class="attachment-popup-header"> |
| | | <text class="attachment-popup-title">æ¥çéä»¶ - {{ currentViewTask?.taskName }}</text> |
| | | <view class="close-btn-attachment" |
| | | @click="closeAttachmentDialog"> |
| | | <u-icon name="close" |
| | | size="16" |
| | | color="#666"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="attachment-popup-body"> |
| | | <!-- åç±»æ ç¾é¡µ --> |
| | | <view class="attachment-tabs"> |
| | | <view class="tab-item" |
| | | :class="{ active: currentViewType === 'before' }" |
| | | @click="switchViewType('before')"> |
| | | ç产å ({{ getAttachmentsByType(0).length }}) |
| | | </view> |
| | | <view class="tab-item" |
| | | :class="{ active: currentViewType === 'after' }" |
| | | @click="switchViewType('after')"> |
| | | çäº§ä¸ ({{ getAttachmentsByType(1).length }}) |
| | | </view> |
| | | <view class="tab-item" |
| | | :class="{ active: currentViewType === 'issue' }" |
| | | @click="switchViewType('issue')"> |
| | | ç产å ({{ getAttachmentsByType(2).length }}) |
| | | </view> |
| | | </view> |
| | | <!-- å½ååç±»çéä»¶å表 --> |
| | | <view class="attachment-content"> |
| | | <view v-if="getCurrentViewAttachments().length > 0" |
| | | class="attachment-list"> |
| | | <view v-for="(file, index) in getCurrentViewAttachments()" |
| | | :key="index" |
| | | class="attachment-item" |
| | | @click="previewAttachment(file)"> |
| | | <view class="attachment-preview-container"> |
| | | <image v-if="file.type === 'image' || isImageFile(file)" |
| | | :src="file.url || file.downloadUrl" |
| | | class="attachment-preview" |
| | | mode="aspectFill" /> |
| | | <view v-else |
| | | class="attachment-video-preview"> |
| | | <u-icon name="video" |
| | | size="24" |
| | | color="#409eff"></u-icon> |
| | | <text class="video-text">è§é¢</text> |
| | | </view> |
| | | </view> |
| | | <view class="attachment-info"> |
| | | <text class="attachment-name">{{ file.originalFilename || file.bucketFilename || file.name || 'éä»¶' |
| | | }}</text> |
| | | <text class="attachment-size">{{ formatFileSize(file.byteSize || file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="attachment-empty"> |
| | | <text>该åç±»ææ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- è§é¢é¢è§å¼¹çª --> |
| | |
| | | const currentScanningTask = ref(null); |
| | | const infoData = ref(null); |
| | | |
| | | // ä¸ä¼ ç¸å
³ç¶æ |
| | | const showUploadDialog = ref(false); |
| | | const uploadFiles = ref([]); // ä¿çç¨äºå
¼å®¹æ§ |
| | | const uploadStatusType = ref(0); |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | const number = ref(0); |
| | | const uploadList = ref([]); |
| | | |
| | | // ä¸ä¸ªåç±»çä¸ä¼ ç¶æ |
| | | const beforeModelValue = ref([]); // ç产å |
| | | const afterModelValue = ref([]); // çäº§ä¸ |
| | | const issueModelValue = ref([]); // ç产å |
| | | |
| | | // å½åæ¿æ´»çä¸ä¼ ç±»å |
| | | const currentUploadType = ref("before"); // 'before', 'after', 'issue' |
| | | |
| | | // æ¥çéä»¶ç¸å
³ç¶æ |
| | | const showAttachmentDialog = ref(false); |
| | | const currentViewTask = ref(null); |
| | | const currentViewType = ref("before"); // 'before', 'after', 'issue' |
| | | const attachmentList = ref([]); // å½åæ¥çä»»å¡çéä»¶å表 |
| | | |
| | | // è§é¢é¢è§ç¸å
³ç¶æ |
| | | const showVideoDialog = ref(false); |
| | | const currentVideoFile = ref(null); |
| | | |
| | | // å¼å¸¸ç¶æ |
| | | const hasException = ref(null); // null: æªéæ©, true: åå¨å¼å¸¸, false: æ£å¸¸ |
| | | |
| | | // ä¸ä¼ é
ç½® |
| | | const uploadConfig = { |
| | | action: "/file/upload", |
| | | limit: 10, |
| | | fileSize: 50, // MB |
| | | fileType: ["jpg", "jpeg", "png", "mp4", "mov"], |
| | | maxVideoDuration: 60, // ç§ |
| | | }; |
| | | |
| | | // 计ç®ä¸ä¼ URL |
| | | const uploadFileUrl = computed(() => { |
| | | const baseUrl = config.baseUrl; |
| | | |
| | | return baseUrl + uploadConfig.action; |
| | | }); |
| | | |
| | | // 计ç®è¯·æ±å¤´ |
| | | const headers = computed(() => { |
| | | const token = getToken(); |
| | | return token ? { Authorization: "Bearer " + token } : {}; |
| | | }); |
| | | |
| | | // 请æ±åæ¶æ å¿ï¼ç¨äºåæ¶æ£å¨è¿è¡çè¯·æ± |
| | | let isRequestCancelled = false; |
| | |
| | | onUnmounted(() => { |
| | | // è®¾ç½®åæ¶æ å¿ï¼é»æ¢åç»ç弿¥æä½ |
| | | isRequestCancelled = true; |
| | | |
| | | // å
³éä¸ä¼ å¼¹çª |
| | | if (showUploadDialog.value) { |
| | | showUploadDialog.value = false; |
| | | } |
| | | }); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | |
| | | } |
| | | }; |
| | | |
| | | // æå¼ä¸ä¼ å¼¹çª |
| | | // æå¼ä¸ä¼ é¡µé¢ |
| | | const openUploadDialog = task => { |
| | | // 设置任å¡ä¿¡æ¯å°infoData |
| | | if (task) { |
| | | infoData.value = { |
| | | ...task, |
| | | taskId: task.taskId || task.id, |
| | | storageBlobDTO: [], // åå§åæä»¶å表 |
| | | }; |
| | | } |
| | | |
| | | // 设置ä¸ä¼ ç¶æç±»åï¼å¯ä»¥æ ¹æ®ä»»å¡ç±»å设置ä¸åçç¶æï¼ |
| | | uploadStatusType.value = 0; // é»è®¤ç¶æ |
| | | |
| | | // æ¸
空ä¹åçæä»¶ |
| | | uploadFiles.value = []; |
| | | |
| | | // æ¾ç¤ºä¸ä¼ å¼¹çª |
| | | showUploadDialog.value = true; |
| | | }; |
| | | |
| | | // å
³éä¸ä¼ å¼¹çª |
| | | const closeUploadDialog = () => { |
| | | showUploadDialog.value = false; |
| | | uploadFiles.value = []; |
| | | // æ¸
çä¸ä¸ªåç±»çæ°æ® |
| | | beforeModelValue.value = []; |
| | | afterModelValue.value = []; |
| | | issueModelValue.value = []; |
| | | currentUploadType.value = "before"; |
| | | hasException.value = null; // éç½®å¼å¸¸ç¶æ |
| | | infoData.value = null; // æ¸
ç任塿°æ® |
| | | }; |
| | | |
| | | // 忢ä¸ä¼ ç±»å |
| | | const switchUploadType = type => { |
| | | currentUploadType.value = type; |
| | | }; |
| | | |
| | | // è·åå½ååç±»çæä»¶å表 |
| | | const getCurrentFiles = () => { |
| | | switch (currentUploadType.value) { |
| | | case "before": |
| | | return beforeModelValue.value || []; |
| | | case "after": |
| | | return afterModelValue.value || []; |
| | | case "issue": |
| | | return issueModelValue.value || []; |
| | | default: |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | // è·åä¸ä¼ ç±»åææ¬ |
| | | const getUploadTypeText = () => { |
| | | switch (currentUploadType.value) { |
| | | case "before": |
| | | return "ç产å"; |
| | | case "after": |
| | | return "ç产ä¸"; |
| | | case "issue": |
| | | return "ç产å"; |
| | | default: |
| | | return ""; |
| | | } |
| | | }; |
| | | |
| | | // å¤çä¸ä¼ æä»¶æ´æ° |
| | | const handleUploadUpdate = files => { |
| | | uploadFiles.value = files; |
| | | }; |
| | | |
| | | // 设置å¼å¸¸ç¶æ |
| | | const setExceptionStatus = status => { |
| | | hasException.value = status; |
| | | }; |
| | | |
| | | // è·³è½¬å°æ°å¢æ¥ä¿®é¡µé¢ |
| | | const goToRepair = () => { |
| | | try { |
| | | // åå¨å½åä»»å¡ä¿¡æ¯å°æ¬å°åå¨ï¼ä¾æ¥ä¿®é¡µé¢ä½¿ç¨ |
| | | const taskInfo = { |
| | | taskId: infoData.value?.taskId || infoData.value?.id, |
| | | taskName: infoData.value?.taskName, |
| | | inspectionLocation: infoData.value?.inspectionLocation, |
| | | inspector: infoData.value?.inspector, |
| | | // ä¼ éå½åä¸ä¼ çæä»¶ä¿¡æ¯ |
| | | uploadedFiles: { |
| | | before: beforeModelValue.value, |
| | | after: afterModelValue.value, |
| | | issue: issueModelValue.value, |
| | | }, |
| | | }; |
| | | |
| | | uni.setStorageSync("repairTaskInfo", JSON.stringify(taskInfo)); |
| | | |
| | | // è·³è½¬å°æ°å¢æ¥ä¿®é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/repair/add", |
| | | }); |
| | | |
| | | // å
³éä¸ä¼ å¼¹çª |
| | | closeUploadDialog(); |
| | | } catch (error) { |
| | | console.error("跳转æ¥ä¿®é¡µé¢å¤±è´¥:", error); |
| | | uni.showToast({ |
| | | title: "跳转失败ï¼è¯·éè¯", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // æäº¤ä¸ä¼ |
| | | const submitUpload = async () => { |
| | | try { |
| | | // æ£æ¥æ¯å¦éæ©äºå¼å¸¸ç¶æ |
| | | if (hasException.value === null) { |
| | | uni.showToast({ |
| | | title: "è¯·éæ©æ¯å¦åå¨å¼å¸¸", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦æä»»ä½æä»¶ä¸ä¼ |
| | | const totalFiles = |
| | | beforeModelValue.value.length + |
| | | afterModelValue.value.length + |
| | | issueModelValue.value.length; |
| | | if (totalFiles === 0) { |
| | | uni.showToast({ |
| | | title: "请å
ä¸ä¼ æä»¶", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ¾ç¤ºæäº¤ä¸çå è½½æç¤º |
| | | showLoadingToast("æäº¤ä¸..."); |
| | | |
| | | // æç
§æ¨çé»è¾åå¹¶ææåç±»çæä»¶ |
| | | let arr = []; |
| | | if (beforeModelValue.value.length > 0) { |
| | | arr.push(...beforeModelValue.value); |
| | | } |
| | | if (afterModelValue.value.length > 0) { |
| | | arr.push(...afterModelValue.value); |
| | | } |
| | | if (issueModelValue.value.length > 0) { |
| | | arr.push(...issueModelValue.value); |
| | | } |
| | | |
| | | // ä¼ ç»å端çä¸´æ¶æä»¶IDå表ï¼tempFileIdsï¼ |
| | | // å
¼å®¹ï¼æäºæ¥å£å¯è½è¿å tempId / tempFileId / id |
| | | let tempFileIds = []; |
| | | if (arr !== null && arr.length > 0) { |
| | | tempFileIds = arr |
| | | .map(item => item?.tempId ?? item?.tempFileId ?? item?.id) |
| | | .filter(v => v !== undefined && v !== null && v !== ""); |
| | | } |
| | | |
| | | // æäº¤æ°æ® |
| | | infoData.value.storageBlobDTO = arr; |
| | | // æ·»å å¼å¸¸ç¶æä¿¡æ¯ |
| | | infoData.value.hasException = hasException.value; |
| | | infoData.value.tempFileIds = tempFileIds; |
| | | const result = await uploadInspectionTask({ ...infoData.value }); |
| | | |
| | | // æ£æ¥æäº¤ç»æ |
| | | if (result && (result.code === 200 || result.success)) { |
| | | // æäº¤æå |
| | | closeToast(); // å
³éå è½½æç¤º |
| | | |
| | | uni.showToast({ |
| | | title: "æäº¤æå", |
| | | icon: "success", |
| | | }); |
| | | |
| | | // å
³éå¼¹çª |
| | | closeUploadDialog(); |
| | | |
| | | // å·æ°å表 |
| | | setTimeout(() => { |
| | | reloadPage(); |
| | | }, 500); |
| | | } else { |
| | | // æäº¤å¤±è´¥ |
| | | closeToast(); |
| | | uni.showToast({ |
| | | title: result?.msg || result?.message || "æäº¤å¤±è´¥", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("æäº¤ä¸ä¼ 失败:", error); |
| | | closeToast(); // å
³éå è½½æç¤º |
| | | |
| | | let errorMessage = "æäº¤å¤±è´¥"; |
| | | if (error.message) { |
| | | errorMessage = error.message; |
| | | } else if (error.msg) { |
| | | errorMessage = error.msg; |
| | | } else if (typeof error === "string") { |
| | | errorMessage = error; |
| | | } |
| | | |
| | | uni.showToast({ |
| | | title: errorMessage, |
| | | icon: "error", |
| | | }); |
| | | } |
| | | // å°ä»»å¡ä¿¡æ¯ä¼ éå°ä¸ä¼ é¡µé¢ |
| | | const taskData = encodeURIComponent(JSON.stringify(task)); |
| | | uni.navigateTo({ |
| | | url: `/pages/inspectionUpload/upload?taskInfo=${taskData}`, |
| | | }); |
| | | }; |
| | | |
| | | // å¾çä¸ä¼ (å¯éæ©å¾çä¸ä¼ æè
æ¯ç¸æºæç
§) |
| | | const startUploadForTask = async (task, type) => { |
| | | // ç´æ¥æå¼ä¸ä¼ å¼¹çª |
| | | // æå¼ä¸ä¼ é¡µé¢ |
| | | openUploadDialog(task); |
| | | }; |
| | | |
| | | // æ¥çéä»¶ |
| | | // æ¥çéä»¶ - 跳转å°éä»¶é¡µé¢ |
| | | const viewAttachments = async task => { |
| | | try { |
| | | currentViewTask.value = task; |
| | | currentViewType.value = "before"; |
| | | |
| | | // è§£ææ°çæ°æ®ç»æ |
| | | attachmentList.value = []; |
| | | |
| | | // åç«¯åæ¾åæ®µï¼ä½ æä¾çæ°æ®ç»æï¼ï¼ |
| | | // - commonFileListBeforeï¼ç产åï¼é常 type=10ï¼ |
| | | // - commonFileListAfterï¼ç产ä¸ï¼é常 type=11ï¼ |
| | | // - commonFileListï¼å¯è½æ¯å
¨é¨/å
åºï¼è¥å
å«ç产åï¼ä¸è¬ type=12ï¼ |
| | | const allList = Array.isArray(task?.commonFileList) |
| | | ? task.commonFileList |
| | | : []; |
| | | const beforeList = Array.isArray(task?.commonFileListBefore) |
| | | ? task.commonFileListBefore |
| | | : allList.filter(f => f?.type === 10); |
| | | const afterList = Array.isArray(task?.commonFileListAfter) |
| | | ? task.commonFileListAfter |
| | | : allList.filter(f => f?.type === 11); |
| | | // 妿å端åç»è¡¥äº commonFileListIssueï¼åä¼å
ç¨ï¼å¦åä» commonFileList éæ type=12 å
åº |
| | | const issueList = Array.isArray(task?.commonFileListIssue) |
| | | ? task.commonFileListIssue |
| | | : allList.filter(f => f?.type === 12); |
| | | |
| | | const mapToViewFile = (file, viewType) => { |
| | | const u = normalizeFileUrl(file?.url || file?.downloadUrl || ""); |
| | | return { |
| | | ...file, |
| | | // ç¨äºä¸æ ç¾é¡µåç»ï¼0=ç产å 1=çäº§ä¸ 2=ç产å |
| | | type: viewType, |
| | | name: file?.name || file?.originalFilename || file?.bucketFilename, |
| | | bucketFilename: file?.bucketFilename || file?.name, |
| | | originalFilename: file?.originalFilename || file?.name, |
| | | url: u, |
| | | downloadUrl: u, |
| | | size: file?.size || file?.byteSize, |
| | | }; |
| | | }; |
| | | |
| | | attachmentList.value.push(...beforeList.map(f => mapToViewFile(f, 0))); |
| | | attachmentList.value.push(...afterList.map(f => mapToViewFile(f, 1))); |
| | | attachmentList.value.push(...issueList.map(f => mapToViewFile(f, 2))); |
| | | |
| | | showAttachmentDialog.value = true; |
| | | } catch (error) { |
| | | uni.showToast({ |
| | | title: "è·åé件失败", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // å
³ééä»¶æ¥çå¼¹çª |
| | | const closeAttachmentDialog = () => { |
| | | showAttachmentDialog.value = false; |
| | | currentViewTask.value = null; |
| | | attachmentList.value = []; |
| | | currentViewType.value = "before"; |
| | | }; |
| | | |
| | | // 忢æ¥çç±»å |
| | | const switchViewType = type => { |
| | | currentViewType.value = type; |
| | | }; |
| | | |
| | | // æ ¹æ®typeè·å对åºåç±»çéä»¶ |
| | | const getAttachmentsByType = typeValue => { |
| | | return attachmentList.value.filter(file => file.type === typeValue) || []; |
| | | }; |
| | | // è·åtypeå¼ |
| | | const getTabType = () => { |
| | | switch (currentUploadType.value) { |
| | | case "before": |
| | | return 10; |
| | | case "after": |
| | | return 11; |
| | | case "issue": |
| | | return 12; |
| | | default: |
| | | return 10; |
| | | } |
| | | }; |
| | | // è·åå½åæ¥çç±»åçéä»¶ |
| | | const getCurrentViewAttachments = () => { |
| | | switch (currentViewType.value) { |
| | | case "before": |
| | | return getAttachmentsByType(0); |
| | | case "after": |
| | | return getAttachmentsByType(1); |
| | | case "issue": |
| | | return getAttachmentsByType(2); |
| | | default: |
| | | return []; |
| | | } |
| | | const taskData = encodeURIComponent(JSON.stringify(task)); |
| | | uni.navigateTo({ |
| | | url: `/pages/inspectionUpload/attachment?taskInfo=${taskData}`, |
| | | }); |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºå¾çæä»¶ |
| | |
| | | }); |
| | | }; |
| | | |
| | | // æç
§/æè§é¢ï¼çæºä¼å
ç¨ chooseMediaï¼ä¸æ¯æåéçº§ï¼ |
| | | const chooseMedia = type => { |
| | | if (getCurrentFiles().length >= uploadConfig.limit) { |
| | | uni.showToast({ |
| | | title: `æå¤åªè½éæ©${uploadConfig.limit}个æä»¶`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | const remaining = uploadConfig.limit - getCurrentFiles().length; |
| | | |
| | | // ä¼å
ï¼chooseMediaï¼æ¯æ image/videoï¼ |
| | | if (typeof uni.chooseMedia === "function") { |
| | | uni.chooseMedia({ |
| | | count: Math.min(remaining, 1), |
| | | mediaType: [type || "image"], |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera"], |
| | | success: res => { |
| | | try { |
| | | const files = res?.tempFiles || []; |
| | | if (!files.length) throw new Error("æªè·åå°æä»¶"); |
| | | |
| | | files.forEach((tf, idx) => { |
| | | const filePath = tf.tempFilePath || tf.path || ""; |
| | | const fileType = tf.fileType || type || "image"; |
| | | const ext = fileType === "video" ? "mp4" : "jpg"; |
| | | const file = { |
| | | tempFilePath: filePath, |
| | | path: filePath, |
| | | type: fileType, |
| | | name: `${fileType}_${Date.now()}_${idx}.${ext}`, |
| | | size: tf.size || 0, |
| | | duration: tf.duration || 0, |
| | | createTime: Date.now(), |
| | | uid: Date.now() + Math.random() + idx, |
| | | }; |
| | | handleBeforeUpload(file); |
| | | }); |
| | | } catch (e) { |
| | | console.error("å¤çææç»æå¤±è´¥:", e); |
| | | uni.showToast({ title: "å¤çæä»¶å¤±è´¥", icon: "error" }); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | console.error("ææå¤±è´¥:", err); |
| | | uni.showToast({ title: "ææå¤±è´¥", icon: "error" }); |
| | | }, |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // é级ï¼chooseImage / chooseVideo |
| | | if (type === "video") { |
| | | chooseVideo(); |
| | | } else { |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera"], |
| | | success: res => { |
| | | const tempFilePath = res?.tempFilePaths?.[0]; |
| | | const tempFile = res?.tempFiles?.[0] || {}; |
| | | if (!tempFilePath) return; |
| | | handleBeforeUpload({ |
| | | tempFilePath, |
| | | path: tempFilePath, |
| | | type: "image", |
| | | name: `photo_${Date.now()}.jpg`, |
| | | size: tempFile.size || 0, |
| | | createTime: Date.now(), |
| | | uid: Date.now() + Math.random(), |
| | | }); |
| | | }, |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // æç
§ |
| | | const chooseImage = () => { |
| | | if (uploadFiles.value.length >= uploadConfig.limit) { |
| | | uni.showToast({ |
| | | title: `æå¤åªè½ææ${uploadConfig.limit}个æä»¶`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | uni.chooseMedia({ |
| | | count: 1, |
| | | mediaType: ["image", "video"], |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera"], |
| | | success: res => { |
| | | try { |
| | | if (!res.tempFiles || res.tempFiles.length === 0) { |
| | | throw new Error("æªè·åå°å¾çæä»¶"); |
| | | } |
| | | |
| | | const tempFilePath = res.tempFiles[0]; |
| | | const tempFile = |
| | | res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {}; |
| | | |
| | | const file = { |
| | | tempFilePath: tempFilePath, |
| | | path: tempFilePath, // ä¿æå
¼å®¹æ§ |
| | | type: "image", |
| | | name: `photo_${Date.now()}.jpg`, |
| | | size: tempFile.size || 0, |
| | | createTime: new Date().getTime(), |
| | | uid: Date.now() + Math.random(), |
| | | }; |
| | | |
| | | handleBeforeUpload(file); |
| | | } catch (error) { |
| | | console.error("å¤çæç
§ç»æå¤±è´¥:", error); |
| | | uni.showToast({ |
| | | title: "å¤çå¾ç失败", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | console.error("æç
§å¤±è´¥:", err); |
| | | uni.showToast({ |
| | | title: "æç
§å¤±è´¥: " + (err.errMsg || "æªç¥é误"), |
| | | icon: "error", |
| | | }); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // æè§é¢ |
| | | const chooseVideo = () => { |
| | | if (uploadFiles.value.length >= uploadConfig.limit) { |
| | | uni.showToast({ |
| | | title: `æå¤åªè½ææ${uploadConfig.limit}个æä»¶`, |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | uni.chooseVideo({ |
| | | sourceType: ["camera"], |
| | | maxDuration: uploadConfig.maxVideoDuration, |
| | | camera: "back", |
| | | success: res => { |
| | | try { |
| | | if (!res.tempFilePath) { |
| | | throw new Error("æªè·åå°è§é¢æä»¶"); |
| | | } |
| | | |
| | | const file = { |
| | | tempFilePath: res.tempFilePath, |
| | | path: res.tempFilePath, // ä¿æå
¼å®¹æ§ |
| | | type: "video", |
| | | name: `video_${Date.now()}.mp4`, |
| | | size: res.size || 0, |
| | | duration: res.duration || 0, |
| | | createTime: new Date().getTime(), |
| | | uid: Date.now() + Math.random(), |
| | | }; |
| | | |
| | | handleBeforeUpload(file); |
| | | } catch (error) { |
| | | console.error("å¤çæè§é¢ç»æå¤±è´¥:", error); |
| | | uni.showToast({ |
| | | title: "å¤çè§é¢å¤±è´¥", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | console.error("æè§é¢å¤±è´¥:", err); |
| | | uni.showToast({ |
| | | title: "æè§é¢å¤±è´¥: " + (err.errMsg || "æªç¥é误"), |
| | | icon: "error", |
| | | }); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const removeFile = index => { |
| | | uni.showModal({ |
| | | title: "确认å é¤", |
| | | content: "ç¡®å®è¦å é¤è¿ä¸ªæä»¶åï¼", |
| | | success: res => { |
| | | if (res.confirm) { |
| | | // æ ¹æ®å½åä¸ä¼ ç±»åå é¤å¯¹åºåç±»çæä»¶ |
| | | switch (currentUploadType.value) { |
| | | case "before": |
| | | beforeModelValue.value.splice(index, 1); |
| | | break; |
| | | case "after": |
| | | afterModelValue.value.splice(index, 1); |
| | | break; |
| | | case "issue": |
| | | issueModelValue.value.splice(index, 1); |
| | | break; |
| | | } |
| | | uni.showToast({ |
| | | title: "å 餿å", |
| | | icon: "success", |
| | | }); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // æ£æ¥ç½ç»è¿æ¥ |
| | | const checkNetworkConnection = () => { |
| | | return new Promise(resolve => { |
| | | uni.getNetworkType({ |
| | | success: res => { |
| | | if (res.networkType === "none") { |
| | | resolve(false); |
| | | } else { |
| | | resolve(true); |
| | | } |
| | | }, |
| | | fail: () => { |
| | | resolve(false); |
| | | }, |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | // ä¸ä¼ åæ ¡éª |
| | | const handleBeforeUpload = async file => { |
| | | // æ ¡éªæä»¶ç±»å |
| | | if ( |
| | | uploadConfig.fileType && |
| | | Array.isArray(uploadConfig.fileType) && |
| | | uploadConfig.fileType.length > 0 |
| | | ) { |
| | | const fileName = file.name || ""; |
| | | const fileExtension = fileName |
| | | ? fileName.split(".").pop().toLowerCase() |
| | | : ""; |
| | | |
| | | // æ ¹æ®æä»¶ç±»åç¡®å®ææçæ©å±å |
| | | let expectedTypes = []; |
| | | if (file.type === "image") { |
| | | expectedTypes = ["jpg", "jpeg", "png", "gif", "webp"]; |
| | | } else if (file.type === "video") { |
| | | expectedTypes = ["mp4", "mov", "avi", "wmv"]; |
| | | } |
| | | |
| | | // æ£æ¥æä»¶æ©å±åæ¯å¦å¨å
许çç±»åä¸ |
| | | if (fileExtension && expectedTypes.length > 0) { |
| | | const isAllowed = expectedTypes.some( |
| | | type => uploadConfig.fileType.includes(type) && type === fileExtension |
| | | ); |
| | | |
| | | if (!isAllowed) { |
| | | uni.showToast({ |
| | | title: `æä»¶æ ¼å¼ä¸æ¯æï¼è¯·ææ ${expectedTypes.join("/")} æ ¼å¼çæä»¶`, |
| | | icon: "none", |
| | | }); |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æ ¡éªéè¿ï¼å¼å§ä¸ä¼ |
| | | uploadFile(file); |
| | | return true; |
| | | }; |
| | | |
| | | // æä»¶ä¸ä¼ å¤çï¼çæºèµ° uni.uploadFileï¼ |
| | | const uploadFile = async file => { |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | | number.value++; // å¢å ä¸ä¼ è®¡æ° |
| | | |
| | | // ç¡®ä¿tokenåå¨ |
| | | const token = getToken(); |
| | | if (!token) { |
| | | handleUploadError("ç¨æ·æªç»å½"); |
| | | return; |
| | | } |
| | | |
| | | const typeValue = getTabType(); // ç产å:10, ç产ä¸:11, ç产å:12 |
| | | |
| | | uploadWithUniUploadFile( |
| | | file, |
| | | file.tempFilePath || file.path || "", |
| | | typeValue, |
| | | token |
| | | ); |
| | | }; |
| | | |
| | | // 使ç¨uni.uploadFileä¸ä¼ ï¼éH5ç¯å¢æH5åéæ¹æ¡ï¼ |
| | | const uploadWithUniUploadFile = (file, filePath, typeValue, token) => { |
| | | if (!filePath) { |
| | | handleUploadError("æä»¶è·¯å¾ä¸åå¨"); |
| | | return; |
| | | } |
| | | |
| | | const uploadTask = uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath: filePath, |
| | | name: "file", |
| | | formData: { |
| | | type: typeValue, |
| | | }, |
| | | header: { |
| | | Authorization: `Bearer ${token}`, |
| | | }, |
| | | success: res => { |
| | | try { |
| | | if (res.statusCode === 200) { |
| | | const response = JSON.parse(res.data); |
| | | if (response.code === 200) { |
| | | handleUploadSuccess(response, file); |
| | | uni.showToast({ |
| | | title: "ä¸ä¼ æå", |
| | | icon: "success", |
| | | }); |
| | | } else { |
| | | handleUploadError(response.msg || "æå¡å¨è¿åé误"); |
| | | } |
| | | } else { |
| | | handleUploadError(`æå¡å¨é误ï¼ç¶æç : ${res.statusCode}`); |
| | | } |
| | | } catch (e) { |
| | | console.error("è§£æååºå¤±è´¥:", e); |
| | | console.error("åå§ååºæ°æ®:", res.data); |
| | | handleUploadError("ååºæ°æ®è§£æå¤±è´¥: " + e.message); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | console.error("ä¸ä¼ 失败:", err.errMsg || err); |
| | | number.value--; // ä¸ä¼ 失败æ¶åå°è®¡æ° |
| | | |
| | | let errorMessage = "ä¸ä¼ 失败"; |
| | | if (err.errMsg) { |
| | | if (err.errMsg.includes("statusCode: null")) { |
| | | errorMessage = "ç½ç»è¿æ¥å¤±è´¥ï¼è¯·æ£æ¥ç½ç»è®¾ç½®"; |
| | | } else if (err.errMsg.includes("timeout")) { |
| | | errorMessage = "ä¸ä¼ è¶
æ¶ï¼è¯·éè¯"; |
| | | } else if (err.errMsg.includes("fail")) { |
| | | errorMessage = "ä¸ä¼ 失败ï¼è¯·æ£æ¥ç½ç»è¿æ¥"; |
| | | } else { |
| | | errorMessage = err.errMsg; |
| | | } |
| | | } |
| | | |
| | | handleUploadError(errorMessage); |
| | | }, |
| | | complete: () => { |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | }, |
| | | }); |
| | | |
| | | // çå¬ä¸ä¼ è¿åº¦ |
| | | if (uploadTask && uploadTask.onProgressUpdate) { |
| | | uploadTask.onProgressUpdate(res => { |
| | | uploadProgress.value = res.progress; |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // ä¸ä¼ 失败å¤ç |
| | | const handleUploadError = (message = "ä¸ä¼ æä»¶å¤±è´¥", showRetry = false) => { |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | |
| | | if (showRetry) { |
| | | uni.showModal({ |
| | | title: "ä¸ä¼ 失败", |
| | | content: message + "ï¼æ¯å¦éè¯ï¼", |
| | | success: res => { |
| | | if (res.confirm) { |
| | | // ç¨æ·éæ©éè¯ï¼è¿éå¯ä»¥éæ°è§¦åä¸ä¼ |
| | | } |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // ä¸ä¼ æååè° |
| | | const handleUploadSuccess = (res, file) => { |
| | | console.log("ä¸ä¼ æåååº:", res); |
| | | |
| | | // å¤çä¸åçæ°æ®ç»æï¼å¯è½æ¯æ°ç»ï¼ä¹å¯è½æ¯å个对象 |
| | | let uploadedFile = null; |
| | | uploadedFile = res.data; |
| | | |
| | | if (!uploadedFile) { |
| | | console.error("æ æ³è§£æä¸ä¼ ååºæ°æ®:", res); |
| | | number.value--; // ä¸ä¼ 失败æ¶åå°è®¡æ° |
| | | handleUploadError("ä¸ä¼ ååºæ°æ®æ ¼å¼é误", false); |
| | | return; |
| | | } |
| | | |
| | | // æ ¹æ®å½åä¸ä¼ ç±»å设置typeåæ®µ |
| | | let typeValue = 0; // é»è®¤ä¸ºç产å |
| | | switch (currentUploadType.value) { |
| | | case "before": |
| | | typeValue = 0; |
| | | break; |
| | | case "after": |
| | | typeValue = 1; |
| | | break; |
| | | case "issue": |
| | | typeValue = 2; |
| | | break; |
| | | } |
| | | |
| | | // ç¡®ä¿ä¸ä¼ çæä»¶æ°æ®å®æ´ï¼å
å«idåtype |
| | | const fileData = { |
| | | ...file, |
| | | id: uploadedFile.id, // æ·»å æå¡å¨è¿åçid |
| | | tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id, |
| | | url: |
| | | uploadedFile.url || |
| | | uploadedFile.downloadUrl || |
| | | file.tempFilePath || |
| | | file.path, |
| | | bucketFilename: |
| | | uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name, |
| | | downloadUrl: uploadedFile.downloadUrl || uploadedFile.url, |
| | | size: uploadedFile.size || uploadedFile.byteSize || file.size, |
| | | createTime: uploadedFile.createTime || new Date().getTime(), |
| | | type: typeValue, // æ·»å ç±»ååæ®µï¼0=ç产å, 1=ç产ä¸, 2=ç产å |
| | | }; |
| | | |
| | | uploadList.value.push(fileData); |
| | | |
| | | // ç«å³æ·»å å°å¯¹åºçåç±»ï¼ä¸çå¾
æææä»¶ä¸ä¼ 宿 |
| | | switch (currentUploadType.value) { |
| | | case "before": |
| | | beforeModelValue.value.push(fileData); |
| | | break; |
| | | case "after": |
| | | afterModelValue.value.push(fileData); |
| | | break; |
| | | case "issue": |
| | | issueModelValue.value.push(fileData); |
| | | break; |
| | | } |
| | | |
| | | // éç½®ä¸ä¼ å表ï¼å ä¸ºå·²ç»æ·»å å°å¯¹åºåç±»äºï¼ |
| | | uploadList.value = []; |
| | | number.value = 0; |
| | | }; |
| | | |
| | | // ä¸ä¼ ç»æå¤çï¼å·²åºå¼ï¼ç°å¨å¨handleUploadSuccessä¸ç´æ¥å¤çï¼ |
| | | const uploadedSuccessfully = () => { |
| | | // æ¤å½æ°å·²ä¸å使ç¨ï¼æä»¶ä¸ä¼ æååç«å³æ·»å å°å¯¹åºåç±» |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = size => { |
| | | if (!size) return ""; |
| | | if (size < 1024) return size + "B"; |
| | | if (size < 1024 * 1024) return (size / 1024).toFixed(1) + "KB"; |
| | | return (size / (1024 * 1024)).toFixed(1) + "MB"; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | /* ä¸ä¼ å¼¹çªæ ·å¼ */ |
| | | .upload-popup-content { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | width: 100%; |
| | | min-height: 300px; |
| | | max-height: 70vh; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); |
| | | } |
| | | |
| | | .upload-popup-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | border-bottom: 1px solid #eee; |
| | | } |
| | | |
| | | .upload-popup-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .upload-popup-body { |
| | | flex: 1; |
| | | padding: 20px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .upload-popup-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | padding: 15px 20px; |
| | | border-top: 1px solid #eee; |
| | | gap: 10px; |
| | | } |
| | | |
| | | /* ç®åä¸ä¼ ç»ä»¶æ ·å¼ */ |
| | | .simple-upload-area { |
| | | padding: 15px; |
| | | } |
| | | |
| | | .upload-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .file-list { |
| | | margin-top: 15px; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 8px; |
| | | border: 1px solid #e9ecef; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
| | | transition: all 0.3s ease; |
| | | width: calc(50% - 6px); |
| | | min-width: 120px; |
| | | } |
| | | |
| | | .file-item:hover { |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | .file-preview-container { |
| | | position: relative; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .file-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | border-radius: 8px; |
| | | object-fit: cover; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .video-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); |
| | | border-radius: 8px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: -6px; |
| | | right: -6px; |
| | | width: 20px; |
| | | height: 20px; |
| | | background: #ff4757; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | cursor: pointer; |
| | | box-shadow: 0 2px 4px rgba(255, 71, 87, 0.3); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .delete-btn:hover { |
| | | background: #ff3742; |
| | | transform: scale(1.1); |
| | | } |
| | | |
| | | .file-info { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | .file-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | max-width: 100px; |
| | | } |
| | | |
| | | .file-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | margin-top: 2px; |
| | | display: block; |
| | | } |
| | | |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 40px 20px; |
| | | color: #999; |
| | | font-size: 14px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | border: 2px dashed #ddd; |
| | | } |
| | | |
| | | .upload-progress { |
| | | margin: 15px 0; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | /* ä¸ä¼ æ ç¾é¡µæ ·å¼ */ |
| | | .upload-tabs { |
| | | display: flex; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | margin-bottom: 15px; |
| | | padding: 4px; |
| | | } |
| | | |
| | | .tab-item { |
| | | flex: 1; |
| | | text-align: center; |
| | | padding: 8px 12px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | border-radius: 6px; |
| | | transition: all 0.3s ease; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .tab-item.active { |
| | | background: #409eff; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .tab-item:hover:not(.active) { |
| | | background: #e9ecef; |
| | | color: #333; |
| | | } |
| | | |
| | | /* å¼å¸¸ç¶æéæ©æ ·å¼ */ |
| | | .exception-section { |
| | | margin-bottom: 20px; |
| | | padding: 15px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | border: 1px solid #e9ecef; |
| | | } |
| | | |
| | | .section-title { |
| | | display: block; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .exception-options { |
| | | display: flex; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .exception-option { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | padding: 12px 16px; |
| | | background: #fff; |
| | | border: 2px solid #e9ecef; |
| | | border-radius: 8px; |
| | | cursor: pointer; |
| | | transition: all 0.3s ease; |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .exception-option.active { |
| | | border-color: #409eff; |
| | | background: #f0f8ff; |
| | | color: #409eff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .exception-option:hover:not(.active) { |
| | | border-color: #d9d9d9; |
| | | background: #fafafa; |
| | | } |
| | | |
| | | /* ç»è®¡ä¿¡æ¯æ ·å¼ */ |
| | | .upload-summary { |
| | | margin-top: 15px; |
| | | padding: 10px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | border-left: 3px solid #409eff; |
| | | } |
| | | |
| | | .summary-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | /* æ¥çéä»¶å¼¹çªæ ·å¼ */ |
| | | .attachment-popup-content { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | width: 100%; |
| | | min-height: 400px; |
| | | max-height: 70vh; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); |
| | | } |
| | | |
| | | .attachment-popup-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | border-bottom: 1px solid #eee; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .attachment-popup-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .close-btn-attachment { |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | background: #f5f5f5; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | cursor: pointer; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .close-btn-attachment:hover { |
| | | background: #e9ecef; |
| | | transform: scale(1.1); |
| | | } |
| | | |
| | | .attachment-popup-body { |
| | | flex: 1; |
| | | padding: 15px 20px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .attachment-tabs { |
| | | display: flex; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | margin-bottom: 15px; |
| | | padding: 4px; |
| | | } |
| | | |
| | | .attachment-content { |
| | | min-height: 200px; |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .attachment-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 8px; |
| | | border: 1px solid #e9ecef; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
| | | transition: all 0.3s ease; |
| | | width: calc(33.33% - 8px); |
| | | min-width: 100px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .attachment-item:hover { |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | .attachment-preview-container { |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .attachment-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | border-radius: 8px; |
| | | object-fit: cover; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .attachment-video-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); |
| | | border-radius: 8px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .attachment-info { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | max-width: 80px; |
| | | } |
| | | |
| | | .attachment-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | margin-top: 2px; |
| | | display: block; |
| | | } |
| | | |
| | | .attachment-empty { |
| | | text-align: center; |
| | | padding: 60px 20px; |
| | | color: #999; |
| | | font-size: 14px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | border: 2px dashed #ddd; |
| | | } |
| | | |
| | | /* è§é¢é¢è§å¼¹çªæ ·å¼ */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="inspection-upload-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader title="ä¸ä¼ å·¡æ£è®°å½" @back="goBack" /> |
| | | |
| | | <!-- 页é¢å
容 --> |
| | | <view class="upload-content"> |
| | | <!-- ä»»å¡ä¿¡æ¯å¡ç --> |
| | | <view class="task-info-card" v-if="taskInfo"> |
| | | <view class="task-info-header"> |
| | | <text class="task-name">{{ taskInfo.taskName }}</text> |
| | | </view> |
| | | <view class="task-info-body"> |
| | | <view class="info-item"> |
| | | <text class="info-label">ä»»å¡ID</text> |
| | | <text class="info-value">{{ taskInfo.taskId || taskInfo.id }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">å·¡æ£ä½ç½®</text> |
| | | <text class="info-value">{{ taskInfo.inspectionLocation || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">æ§è¡äºº</text> |
| | | <text class="info-value">{{ taskInfo.inspector || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å¼å¸¸ç¶æéæ© --> |
| | | <view class="section-card"> |
| | | <view class="section-title">å·¡æ£ç¶æ</view> |
| | | <view class="exception-options"> |
| | | <view |
| | | class="exception-option" |
| | | :class="{ active: hasException === false }" |
| | | @click="setExceptionStatus(false)" |
| | | > |
| | | <u-icon name="checkmark-circle" size="20" color="#52c41a"></u-icon> |
| | | <text class="option-text">æ£å¸¸</text> |
| | | </view> |
| | | <view |
| | | class="exception-option" |
| | | :class="{ active: hasException === true }" |
| | | @click="setExceptionStatus(true)" |
| | | > |
| | | <u-icon name="close-circle" size="20" color="#ff4d4f"></u-icon> |
| | | <text class="option-text">åå¨å¼å¸¸</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å¼å¸¸æè¿°ï¼ä»
å¨å¼å¸¸æ¶æ¾ç¤ºï¼ --> |
| | | <view class="section-card" v-if="hasException === true"> |
| | | <view class="section-title">å¼å¸¸æè¿°</view> |
| | | <textarea |
| | | v-model="abnormalDescription" |
| | | class="exception-textarea" |
| | | maxlength="500" |
| | | placeholder="请æè¿°å¼å¸¸æ
åµ..." |
| | | /> |
| | | </view> |
| | | |
| | | <!-- åç±»æ ç¾é¡µï¼ä»
å¨å¼å¸¸æ¶æ¾ç¤ºï¼ --> |
| | | <view class="section-card" v-if="hasException === true"> |
| | | <view class="upload-tabs"> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentUploadType === 'before' }" |
| | | @click="switchUploadType('before')" |
| | | > |
| | | ç产å |
| | | </view> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentUploadType === 'after' }" |
| | | @click="switchUploadType('after')" |
| | | > |
| | | çäº§ä¸ |
| | | </view> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentUploadType === 'issue' }" |
| | | @click="switchUploadType('issue')" |
| | | > |
| | | ç产å |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å½ååç±»çä¸ä¼ åºå --> |
| | | <view class="upload-area"> |
| | | <view class="upload-buttons"> |
| | | <u-button |
| | | type="primary" |
| | | @click="chooseMedia('image')" |
| | | :loading="uploading" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }" |
| | | > |
| | | <u-icon name="camera" size="18" color="#fff" style="margin-right: 5px"></u-icon> |
| | | {{ uploading ? 'ä¸ä¼ ä¸...' : 'æç
§' }} |
| | | </u-button> |
| | | <u-button |
| | | type="success" |
| | | @click="chooseMedia('video')" |
| | | :loading="uploading" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit" |
| | | :customStyle="{ flex: 1 }" |
| | | > |
| | | <uni-icons type="videocam" size="18" color="#fff" style="margin-right: 5px"></uni-icons> |
| | | {{ uploading ? 'ä¸ä¼ ä¸...' : 'æè§é¢' }} |
| | | </u-button> |
| | | </view> |
| | | |
| | | <!-- ä¸ä¼ è¿åº¦ --> |
| | | <view v-if="uploading" class="upload-progress"> |
| | | <u-line-progress :percentage="uploadProgress" :showText="true" activeColor="#409eff"></u-line-progress> |
| | | </view> |
| | | |
| | | <!-- å½ååç±»çæä»¶å表 --> |
| | | <view v-if="getCurrentFiles().length > 0" class="file-list"> |
| | | <view v-for="(file, index) in getCurrentFiles()" :key="index" class="file-item"> |
| | | <view class="file-preview-container"> |
| | | <image |
| | | v-if="file.type === 'image' || (file.type !== 'video' && !file.type)" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | class="file-preview" |
| | | mode="aspectFill" |
| | | /> |
| | | <view v-else-if="file.type === 'video'" class="video-preview"> |
| | | <uni-icons type="videocam" size="18" color="#fff" style="margin-right: 5px"></uni-icons> |
| | | <text class="video-text">è§é¢</text> |
| | | </view> |
| | | <!-- å é¤æé® --> |
| | | <view class="delete-btn" @click="removeFile(index)"> |
| | | <u-icon name="close" size="12" color="#fff"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? 'å¾ç' : 'è§é¢') }}</text> |
| | | <text class="file-size">{{ formatFileSize(file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="getCurrentFiles().length === 0" class="empty-state"> |
| | | <text>è¯·éæ©è¦ä¸ä¼ ç{{ getUploadTypeText() }}å¾çæè§é¢</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <view class="upload-summary"> |
| | | <text class="summary-text"> |
| | | ç产å: {{ beforeModelValue.length }}个æä»¶ | |
| | | ç产ä¸: {{ afterModelValue.length }}个æä»¶ | |
| | | ç产å: {{ issueModelValue.length }}个æä»¶ |
| | | </text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- æ£å¸¸ç¶ææç¤º --> |
| | | <view class="normal-tip-card" v-if="hasException === false"> |
| | | <u-icon name="info-circle" size="60" color="#52c41a"></u-icon> |
| | | <text class="tip-text">设å¤è¿è¡æ£å¸¸ï¼æ éä¸ä¼ ç
§ç</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åºé¨æé® --> |
| | | <view class="footer-buttons"> |
| | | <u-button @click="goBack" :customStyle="{ marginRight: '10px' }">åæ¶</u-button> |
| | | <u-button v-if="hasException === true" type="warning" @click="goToRepair" :customStyle="{ marginRight: '10px' }"> |
| | | æ°å¢æ¥ä¿® |
| | | </u-button> |
| | | <u-button type="primary" @click="submitUpload">æäº¤</u-button> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from 'vue'; |
| | | import { onLoad } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { uploadInspectionTask } from '@/api/inspectionManagement'; |
| | | import { getToken } from '@/utils/auth'; |
| | | import config from '@/config'; |
| | | |
| | | // ä»»å¡ä¿¡æ¯ |
| | | const taskInfo = ref(null); |
| | | |
| | | // ä¸ä¼ ç¸å
³ç¶æ |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | |
| | | // ä¸ä¸ªåç±»çä¸ä¼ ç¶æ |
| | | const beforeModelValue = ref([]); // ç产å |
| | | const afterModelValue = ref([]); // çäº§ä¸ |
| | | const issueModelValue = ref([]); // ç产å |
| | | |
| | | // å½åæ¿æ´»çä¸ä¼ ç±»å |
| | | const currentUploadType = ref('before'); // 'before', 'after', 'issue' |
| | | |
| | | // å¼å¸¸ç¶æ |
| | | const hasException = ref(null); // null: æªéæ©, true: åå¨å¼å¸¸, false: æ£å¸¸ |
| | | // å¼å¸¸æè¿° |
| | | const abnormalDescription = ref(''); |
| | | |
| | | // ä¸ä¼ é
ç½® |
| | | const uploadConfig = { |
| | | action: '/file/upload', |
| | | limit: 10, |
| | | fileSize: 50, // MB |
| | | fileType: ['jpg', 'jpeg', 'png', 'mp4', 'mov'], |
| | | maxVideoDuration: 60, // ç§ |
| | | }; |
| | | |
| | | // 计ç®ä¸ä¼ URL |
| | | const uploadFileUrl = computed(() => { |
| | | const baseUrl = config.baseUrl; |
| | | return baseUrl + uploadConfig.action; |
| | | }); |
| | | |
| | | // 页é¢å è½½ |
| | | onLoad((options) => { |
| | | if (options.taskInfo) { |
| | | try { |
| | | taskInfo.value = JSON.parse(decodeURIComponent(options.taskInfo)); |
| | | } catch (e) { |
| | | console.error('è§£æä»»å¡ä¿¡æ¯å¤±è´¥:', e); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // 忢ä¸ä¼ ç±»å |
| | | const switchUploadType = (type) => { |
| | | currentUploadType.value = type; |
| | | }; |
| | | |
| | | // è·åå½ååç±»çæä»¶å表 |
| | | const getCurrentFiles = () => { |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | return beforeModelValue.value || []; |
| | | case 'after': |
| | | return afterModelValue.value || []; |
| | | case 'issue': |
| | | return issueModelValue.value || []; |
| | | default: |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | // è·åä¸ä¼ ç±»åææ¬ |
| | | const getUploadTypeText = () => { |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | return 'ç产å'; |
| | | case 'after': |
| | | return 'ç产ä¸'; |
| | | case 'issue': |
| | | return 'ç产å'; |
| | | default: |
| | | return ''; |
| | | } |
| | | }; |
| | | |
| | | // 设置å¼å¸¸ç¶æ |
| | | const setExceptionStatus = (status) => { |
| | | hasException.value = status; |
| | | }; |
| | | |
| | | // è·³è½¬å°æ°å¢æ¥ä¿®é¡µé¢ |
| | | const goToRepair = () => { |
| | | try { |
| | | const taskData = { |
| | | taskId: taskInfo.value?.taskId || taskInfo.value?.id, |
| | | taskName: taskInfo.value?.taskName, |
| | | inspectionLocation: taskInfo.value?.inspectionLocation, |
| | | inspector: taskInfo.value?.inspector, |
| | | uploadedFiles: { |
| | | before: beforeModelValue.value, |
| | | after: afterModelValue.value, |
| | | issue: issueModelValue.value, |
| | | }, |
| | | }; |
| | | |
| | | uni.setStorageSync('repairTaskInfo', JSON.stringify(taskData)); |
| | | |
| | | uni.navigateTo({ |
| | | url: '/pages/equipmentManagement/repair/add', |
| | | }); |
| | | } catch (error) { |
| | | console.error('跳转æ¥ä¿®é¡µé¢å¤±è´¥:', error); |
| | | uni.showToast({ |
| | | title: '跳转失败ï¼è¯·éè¯', |
| | | icon: 'error', |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // æäº¤ä¸ä¼ |
| | | const submitUpload = async () => { |
| | | try { |
| | | // æ£æ¥æ¯å¦éæ©äºå¼å¸¸ç¶æ |
| | | if (hasException.value === null) { |
| | | uni.showToast({ |
| | | title: 'è¯·éæ©å·¡æ£ç¶æ', |
| | | icon: 'none', |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // 妿æ¯å¼å¸¸ç¶æï¼æ£æ¥æ¯å¦æä¸ä¼ æä»¶åæè¿° |
| | | if (hasException.value === true) { |
| | | const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length; |
| | | if (totalFiles === 0) { |
| | | uni.showToast({ |
| | | title: '请ä¸ä¼ å¼å¸¸ç
§ç', |
| | | icon: 'none', |
| | | }); |
| | | return; |
| | | } |
| | | // æ£æ¥æ¯å¦å¡«åäºå¼å¸¸æè¿° |
| | | if (!abnormalDescription.value.trim()) { |
| | | uni.showToast({ |
| | | title: '请填åå¼å¸¸æè¿°', |
| | | icon: 'none', |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // æ¾ç¤ºæäº¤ä¸çå è½½æç¤º |
| | | uni.showLoading({ |
| | | title: 'æäº¤ä¸...', |
| | | mask: true, |
| | | }); |
| | | |
| | | // æç
§é»è¾åå¹¶ææåç±»çæä»¶ |
| | | let arr = []; |
| | | if (beforeModelValue.value.length > 0) { |
| | | arr.push(...beforeModelValue.value); |
| | | } |
| | | if (afterModelValue.value.length > 0) { |
| | | arr.push(...afterModelValue.value); |
| | | } |
| | | if (issueModelValue.value.length > 0) { |
| | | arr.push(...issueModelValue.value); |
| | | } |
| | | |
| | | // ä¼ ç»å端çä¸´æ¶æä»¶IDå表 |
| | | let tempFileIds = []; |
| | | if (arr !== null && arr.length > 0) { |
| | | tempFileIds = arr |
| | | .map((item) => item?.tempId ?? item?.tempFileId ?? item?.id) |
| | | .filter((v) => v !== undefined && v !== null && v !== ''); |
| | | } |
| | | |
| | | // æäº¤æ°æ® |
| | | const submitData = { |
| | | ...taskInfo.value, |
| | | storageBlobDTO: arr, |
| | | hasException: hasException.value, |
| | | abnormalDescription: abnormalDescription.value, |
| | | tempFileIds: tempFileIds, |
| | | }; |
| | | |
| | | const result = await uploadInspectionTask(submitData); |
| | | |
| | | // æ£æ¥æäº¤ç»æ |
| | | if (result && (result.code === 200 || result.success)) { |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: 'æäº¤æå', |
| | | icon: 'success', |
| | | }); |
| | | |
| | | // è¿ååè¡¨é¡µå¹¶å·æ° |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 500); |
| | | } else { |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: result?.msg || result?.message || 'æäº¤å¤±è´¥', |
| | | icon: 'error', |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error('æäº¤ä¸ä¼ 失败:', error); |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: error?.message || 'æäº¤å¤±è´¥', |
| | | icon: 'error', |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = (size) => { |
| | | if (!size) return '0 B'; |
| | | const units = ['B', 'KB', 'MB', 'GB']; |
| | | let index = 0; |
| | | let fileSize = size; |
| | | while (fileSize >= 1024 && index < units.length - 1) { |
| | | fileSize /= 1024; |
| | | index++; |
| | | } |
| | | return `${fileSize.toFixed(2)} ${units[index]}`; |
| | | }; |
| | | |
| | | // æç
§/æè§é¢ |
| | | const chooseMedia = (type) => { |
| | | if (getCurrentFiles().length >= uploadConfig.limit) { |
| | | uni.showToast({ |
| | | title: `æå¤åªè½éæ©${uploadConfig.limit}个æä»¶`, |
| | | icon: 'none', |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | const remaining = uploadConfig.limit - getCurrentFiles().length; |
| | | |
| | | // ä¼å
ä½¿ç¨ chooseMedia |
| | | if (typeof uni.chooseMedia === 'function') { |
| | | uni.chooseMedia({ |
| | | count: Math.min(remaining, 1), |
| | | mediaType: [type || 'image'], |
| | | sizeType: ['compressed', 'original'], |
| | | sourceType: ['camera'], |
| | | success: (res) => { |
| | | try { |
| | | const files = res?.tempFiles || []; |
| | | if (!files.length) throw new Error('æªè·åå°æä»¶'); |
| | | |
| | | files.forEach((tf, idx) => { |
| | | const filePath = tf.tempFilePath || tf.path || ''; |
| | | const fileType = tf.fileType || type || 'image'; |
| | | const ext = fileType === 'video' ? 'mp4' : 'jpg'; |
| | | const file = { |
| | | tempFilePath: filePath, |
| | | path: filePath, |
| | | type: fileType, |
| | | name: `${fileType}_${Date.now()}_${idx}.${ext}`, |
| | | size: tf.size || 0, |
| | | duration: tf.duration || 0, |
| | | createTime: Date.now(), |
| | | }; |
| | | uploadFile(file); |
| | | }); |
| | | } catch (err) { |
| | | uni.showToast({ title: err.message || 'å¤çæä»¶å¤±è´¥', icon: 'none' }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('éæ©åªä½å¤±è´¥:', err); |
| | | uni.showToast({ title: 'éæ©å¤±è´¥', icon: 'none' }); |
| | | }, |
| | | }); |
| | | } else { |
| | | // éçº§æ¹æ¡ |
| | | if (type === 'video') { |
| | | uni.chooseVideo({ |
| | | sourceType: ['camera'], |
| | | success: (res) => { |
| | | const file = { |
| | | tempFilePath: res.tempFilePath, |
| | | path: res.tempFilePath, |
| | | type: 'video', |
| | | name: `video_${Date.now()}.mp4`, |
| | | size: res.size || 0, |
| | | duration: res.duration || 0, |
| | | createTime: Date.now(), |
| | | }; |
| | | uploadFile(file); |
| | | }, |
| | | fail: () => { |
| | | uni.showToast({ title: 'éæ©è§é¢å¤±è´¥', icon: 'none' }); |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.chooseImage({ |
| | | count: Math.min(remaining, 9), |
| | | sizeType: ['compressed'], |
| | | sourceType: ['camera'], |
| | | success: (res) => { |
| | | const list = res.tempFilePaths || res.tempFiles || []; |
| | | list.forEach((src, idx) => { |
| | | const path = typeof src === 'string' ? src : src.path; |
| | | const file = { |
| | | tempFilePath: path, |
| | | path: path, |
| | | type: 'image', |
| | | name: `image_${Date.now()}_${idx}.jpg`, |
| | | size: 0, |
| | | createTime: Date.now(), |
| | | }; |
| | | uploadFile(file); |
| | | }); |
| | | }, |
| | | fail: () => { |
| | | uni.showToast({ title: 'éæ©å¾ç失败', icon: 'none' }); |
| | | }, |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // ä¸ä¼ å个æä»¶ |
| | | const uploadFile = (file) => { |
| | | const token = getToken(); |
| | | if (!token) { |
| | | uni.showToast({ title: 'ç¨æ·æªç»å½', icon: 'none' }); |
| | | return; |
| | | } |
| | | |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | | |
| | | const uploadTask = uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath: file.tempFilePath, |
| | | name: 'file', |
| | | header: { |
| | | Authorization: `Bearer ${token}`, |
| | | }, |
| | | formData: { |
| | | type: getTabType(), |
| | | }, |
| | | success: (res) => { |
| | | try { |
| | | const data = JSON.parse(res.data); |
| | | if (data.code === 200) { |
| | | const uploadedFile = { |
| | | ...file, |
| | | url: data.data.url, |
| | | tempId: data.data.tempId || data.data.id, |
| | | status: 'success', |
| | | }; |
| | | |
| | | // æ ¹æ®å½åç±»åæ·»å å°å¯¹åºæ°ç» |
| | | if (currentUploadType.value === 'before') { |
| | | beforeModelValue.value.push(uploadedFile); |
| | | } else if (currentUploadType.value === 'after') { |
| | | afterModelValue.value.push(uploadedFile); |
| | | } else if (currentUploadType.value === 'issue') { |
| | | issueModelValue.value.push(uploadedFile); |
| | | } |
| | | |
| | | uni.showToast({ title: 'ä¸ä¼ æå', icon: 'success' }); |
| | | } else { |
| | | uni.showToast({ title: data.msg || 'ä¸ä¼ 失败', icon: 'none' }); |
| | | } |
| | | } catch (e) { |
| | | uni.showToast({ title: 'è§£æååºå¤±è´¥', icon: 'none' }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('ä¸ä¼ 失败:', err); |
| | | uni.showToast({ title: 'ä¸ä¼ 失败', icon: 'none' }); |
| | | }, |
| | | complete: () => { |
| | | uploading.value = false; |
| | | }, |
| | | }); |
| | | |
| | | // çå¬ä¸ä¼ è¿åº¦ |
| | | uploadTask.onProgressUpdate((res) => { |
| | | uploadProgress.value = res.progress; |
| | | }); |
| | | }; |
| | | |
| | | // è·åtypeå¼ |
| | | const getTabType = () => { |
| | | switch (currentUploadType.value) { |
| | | case 'before': |
| | | return 10; |
| | | case 'after': |
| | | return 11; |
| | | case 'issue': |
| | | return 12; |
| | | default: |
| | | return 10; |
| | | } |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const removeFile = (index) => { |
| | | const files = getCurrentFiles(); |
| | | files.splice(index, 1); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .inspection-upload-page { |
| | | min-height: 100vh; |
| | | background-color: #f5f5f5; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | .upload-content { |
| | | padding: 15px; |
| | | } |
| | | |
| | | /* ä»»å¡ä¿¡æ¯å¡ç */ |
| | | .task-info-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | margin-bottom: 15px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .task-info-header { |
| | | margin-bottom: 12px; |
| | | padding-bottom: 12px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .task-name { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .task-info-body { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 13px; |
| | | color: #999; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 13px; |
| | | color: #666; |
| | | } |
| | | |
| | | /* éç¨å¡çæ ·å¼ */ |
| | | .section-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | margin-bottom: 15px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | /* å¼å¸¸ç¶æéæ© */ |
| | | .exception-options { |
| | | display: flex; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .exception-option { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | padding: 14px 16px; |
| | | background: #f8f9fa; |
| | | border: 2px solid #e9ecef; |
| | | border-radius: 8px; |
| | | cursor: pointer; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .exception-option.active { |
| | | border-color: #409eff; |
| | | background: #f0f8ff; |
| | | } |
| | | |
| | | .option-text { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | /* å¼å¸¸æè¿° */ |
| | | .exception-textarea { |
| | | width: 100%; |
| | | min-height: 100px; |
| | | padding: 12px; |
| | | background: #f8f9fa; |
| | | border: 1px solid #e9ecef; |
| | | border-radius: 8px; |
| | | font-size: 14px; |
| | | color: #333; |
| | | resize: none; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .exception-textarea:focus { |
| | | outline: none; |
| | | border-color: #409eff; |
| | | background: #fff; |
| | | } |
| | | |
| | | /* åç±»æ ç¾é¡µ */ |
| | | .upload-tabs { |
| | | display: flex; |
| | | gap: 10px; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .tab-item { |
| | | flex: 1; |
| | | padding: 10px; |
| | | text-align: center; |
| | | background: #f5f5f5; |
| | | border-radius: 6px; |
| | | font-size: 13px; |
| | | color: #666; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | } |
| | | |
| | | .tab-item.active { |
| | | background: #409eff; |
| | | color: #fff; |
| | | } |
| | | |
| | | /* ä¸ä¼ åºå */ |
| | | .upload-area { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .upload-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .upload-progress { |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | /* æä»¶å表 */ |
| | | .file-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .file-item { |
| | | width: calc(33.33% - 7px); |
| | | } |
| | | |
| | | .file-preview-container { |
| | | position: relative; |
| | | width: 100%; |
| | | aspect-ratio: 1; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | background: #f5f5f5; |
| | | } |
| | | |
| | | .file-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .video-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: #333; |
| | | } |
| | | |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #fff; |
| | | margin-top: 5px; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 5px; |
| | | right: 5px; |
| | | width: 22px; |
| | | height: 22px; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .file-info { |
| | | margin-top: 5px; |
| | | } |
| | | |
| | | .file-name { |
| | | display: block; |
| | | font-size: 11px; |
| | | color: #666; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-size { |
| | | display: block; |
| | | font-size: 10px; |
| | | color: #999; |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 30px; |
| | | color: #999; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | /* ç»è®¡ä¿¡æ¯ */ |
| | | .upload-summary { |
| | | margin-top: 15px; |
| | | padding: 10px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | border-left: 3px solid #409eff; |
| | | } |
| | | |
| | | .summary-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | /* æ£å¸¸ç¶ææç¤º */ |
| | | .normal-tip-card { |
| | | background: #f6ffed; |
| | | border: 2px dashed #b7eb8f; |
| | | border-radius: 12px; |
| | | padding: 50px 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .normal-tip-card .tip-text { |
| | | margin-top: 15px; |
| | | font-size: 16px; |
| | | color: #52c41a; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | /* åºé¨æé® */ |
| | | .footer-buttons { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | display: flex; |
| | | padding: 15px; |
| | | background: #fff; |
| | | border-top: 1px solid #f0f0f0; |
| | | box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="record-container"> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <up-input |
| | | class="search-text" |
| | | placeholder="请è¾å
¥äº§å大类" |
| | | v-model="searchForm.productName" |
| | | @confirm="handleQuery" |
| | | clearable |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="handleQuery"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <scroll-view scroll-y class="ledger-list" v-if="tableData.length > 0" @scrolltolower="loadMore"> |
| | | <view v-for="item in tableData" :key="item.id" 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.productName }}</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.model }}</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.batchNo }}</text> |
| | | </view> |
| | | |
| | | <view class="quantity-section"> |
| | | <view class="quantity-box qualified"> |
| | | <text class="q-label">åæ ¼åºå</text> |
| | | <text class="q-value">{{ item.qualifiedQuantity }}</text> |
| | | </view> |
| | | <view class="quantity-box unqualified"> |
| | | <text class="q-label">ä¸åæ ¼åºå</text> |
| | | <text class="q-value">{{ item.unQualifiedQuantity }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="quantity-section"> |
| | | <view class="quantity-box locked"> |
| | | <text class="q-label">åæ ¼å»ç»</text> |
| | | <text class="q-value">{{ item.qualifiedLockedQuantity }}</text> |
| | | </view> |
| | | <view class="quantity-box locked"> |
| | | <text class="q-label">ä¸åæ ¼å»ç»</text> |
| | | <text class="q-value">{{ item.unQualifiedLockedQuantity }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åºåé¢è¦</text> |
| | | <text class="detail-value">{{ item.warnNum }}</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.updateTime }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <up-loadmore :status="loadStatus" /> |
| | | </scroll-view> |
| | | <view v-else-if="!loading" class="no-data"> |
| | | <up-empty mode="data" text="ææ åºåæ°æ®"></up-empty> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue'; |
| | | import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js"; |
| | | |
| | | const props = defineProps({ |
| | | productId: { |
| | | type: Number, |
| | | required: true |
| | | } |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const loading = ref(false); |
| | | const loadStatus = ref('loadmore'); |
| | | const page = reactive({ current: 1, size: 10 }); |
| | | const total = ref(0); |
| | | const searchForm = reactive({ |
| | | productName: '', |
| | | topParentProductId: props.productId |
| | | }); |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | tableData.value = []; |
| | | getList(); |
| | | }; |
| | | |
| | | const getList = () => { |
| | | if (loading.value) return; |
| | | loading.value = true; |
| | | loadStatus.value = 'loading'; |
| | | |
| | | getStockInventoryListPageCombined({ |
| | | ...searchForm, |
| | | current: page.current, |
| | | size: page.size |
| | | }).then(res => { |
| | | loading.value = false; |
| | | const records = res.data.records || []; |
| | | tableData.value = page.current === 1 ? records : [...tableData.value, ...records]; |
| | | total.value = res.data.total; |
| | | loadStatus.value = tableData.value.length >= total.value ? 'nomore' : 'loadmore'; |
| | | }).catch(() => { |
| | | loading.value = false; |
| | | loadStatus.value = 'loadmore'; |
| | | }); |
| | | }; |
| | | |
| | | const loadMore = () => { |
| | | if (loadStatus.value === 'loadmore') { |
| | | page.current++; |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .record-container { |
| | | height: 100%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .search-section { |
| | | padding: 20rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .search-bar { |
| | | display: flex; |
| | | align-items: center; |
| | | background-color: #f2f2f2; |
| | | border-radius: 40rpx; |
| | | padding: 0 30rpx; |
| | | height: 80rpx; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | } |
| | | |
| | | .search-text { |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .filter-button { |
| | | padding-left: 20rpx; |
| | | } |
| | | |
| | | .ledger-list { |
| | | flex: 1; |
| | | padding: 20rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .ledger-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .document-icon { |
| | | width: 40rpx; |
| | | height: 40rpx; |
| | | background: linear-gradient(135deg, #2979ff, #1565c0); |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 16rpx; |
| | | } |
| | | |
| | | .item-id { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .item-details { |
| | | .detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 16rpx; |
| | | font-size: 26rpx; |
| | | |
| | | .detail-label { |
| | | color: #909399; |
| | | } |
| | | |
| | | .detail-value { |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .quantity-section { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | margin: 20rpx 0; |
| | | |
| | | .quantity-box { |
| | | flex: 1; |
| | | padding: 16rpx; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .q-label { |
| | | font-size: 22rpx; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .q-value { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | &.qualified { |
| | | background-color: #ecf5ff; |
| | | color: #409eff; |
| | | } |
| | | |
| | | &.unqualified { |
| | | background-color: #fef0f0; |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | &.locked { |
| | | background-color: #f4f4f5; |
| | | color: #909399; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view class="app-container"> |
| | | <PageHeader title="åºå管ç" @back="goBack" /> |
| | | <up-tabs :list="tabs" @click="handleTabClick" :current="activeTab"/> |
| | | <swiper class="swiper-box" :current="activeTab" @change="handleSwiperChange"> |
| | | <swiper-item class="swiper-item"> |
| | | <qualified-record /> |
| | | </swiper-item> |
| | | <swiper-item class="swiper-item"> |
| | | <unqualified-record /> |
| | | </swiper-item> |
| | | </swiper> |
| | | <PageHeader title="åºå管ç" |
| | | @back="goBack" /> |
| | | <view v-if="loading" |
| | | class="loading-state"> |
| | | <up-loading-icon text="å è½½ä¸..."></up-loading-icon> |
| | | </view> |
| | | <template v-else> |
| | | <up-tabs :list="tabs" |
| | | @click="handleTabClick" |
| | | :current="activeTab" /> |
| | | <swiper class="swiper-box" |
| | | :current="activeTab" |
| | | @change="handleSwiperChange"> |
| | | <swiper-item class="swiper-item" |
| | | v-for="tab in products" |
| | | :key="tab.id"> |
| | | <record :product-id="tab.id" |
| | | v-if="activeTab === products.indexOf(tab) || initializedTabs.includes(tab.id)" /> |
| | | </swiper-item> |
| | | </swiper> |
| | | </template> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import QualifiedRecord from "./Qualified.vue"; |
| | | import UnqualifiedRecord from "./Unqualified.vue"; |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import Record from "./Record.vue"; |
| | | import { productTreeList } from "@/api/basicData/product.js"; |
| | | |
| | | const activeTab = ref(0); |
| | | const tabs = ref([ |
| | | { name: 'åæ ¼åºå' }, |
| | | { name: 'ä¸åæ ¼åºå' } |
| | | ]); |
| | | const activeTab = ref(0); |
| | | const tabs = ref([]); |
| | | const products = ref([]); |
| | | const loading = ref(false); |
| | | const initializedTabs = ref([]); |
| | | |
| | | const handleTabClick = (item) => { |
| | | activeTab.value = item.index; |
| | | }; |
| | | const handleTabClick = item => { |
| | | activeTab.value = item.index; |
| | | if (!initializedTabs.value.includes(products.value[item.index].id)) { |
| | | initializedTabs.value.push(products.value[item.index].id); |
| | | } |
| | | }; |
| | | |
| | | const handleSwiperChange = (e) => { |
| | | activeTab.value = e.detail.current; |
| | | }; |
| | | const handleSwiperChange = e => { |
| | | const index = e.detail.current; |
| | | activeTab.value = index; |
| | | if (!initializedTabs.value.includes(products.value[index].id)) { |
| | | initializedTabs.value.push(products.value[index].id); |
| | | } |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | const fetchProducts = async () => { |
| | | loading.value = true; |
| | | try { |
| | | const res = await productTreeList(); |
| | | // è¿æ»¤æ ¹èç¹äº§å |
| | | products.value = res |
| | | .filter(item => item.parentId === null) |
| | | .map(({ id, productName }) => ({ id, productName })); |
| | | tabs.value = products.value.map(p => ({ name: p.productName })); |
| | | |
| | | if (products.value.length > 0) { |
| | | activeTab.value = 0; |
| | | initializedTabs.value = [products.value[0].id]; |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchProducts(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .app-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: 100vh; |
| | | background-color: #f8f9fa; |
| | | } |
| | | .swiper-box { |
| | | flex: 1; |
| | | } |
| | | .swiper-item { |
| | | height: 100%; |
| | | } |
| | | :deep(.up-tabs) { |
| | | background-color: #fff; |
| | | } |
| | | .app-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: 100vh; |
| | | background-color: #f8f9fa; |
| | | } |
| | | .loading-state { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .swiper-box { |
| | | flex: 1; |
| | | } |
| | | .swiper-item { |
| | | height: 100%; |
| | | } |
| | | :deep(.up-tabs) { |
| | | background-color: #fff; |
| | | } |
| | | </style> |
| | |
| | | </up-form-item> |
| | | <up-form-item label="ä¾åºååç§°" |
| | | prop="supplierName" |
| | | required |
| | | > |
| | | required> |
| | | <up-input v-model="form.supplierName" |
| | | readonly |
| | | :disabled="isReadOnly" |
| | |
| | | placeholder="请è¾å
¥" |
| | | disabled /> |
| | | </up-form-item> |
| | | <view class="approval-process"> |
| | | <view class="approval-header"> |
| | | <text class="approval-title">å®¡æ ¸æµç¨</text> |
| | | <text class="approval-desc">æ¯ä¸ªæ¥éª¤åªè½éæ©ä¸ä¸ªå®¡æ¹äºº</text> |
| | | </view> |
| | | <view class="approval-steps"> |
| | | <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="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" |
| | | v-if="!isReadOnly" |
| | | @click="removeApprover(stepIndex)">Ã</view> |
| | | </view> |
| | | <view v-else-if="!isReadOnly" |
| | | 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 < approverNodes.length - 1"></view> |
| | | <view class="delete-step-btn" |
| | | v-if="approverNodes.length > 1 && !isReadOnly" |
| | | @click="removeApprovalStep(stepIndex)">å é¤èç¹</view> |
| | | </view> |
| | | </view> |
| | | <view class="add-step-btn" v-if="!isReadOnly"> |
| | | <u-button icon="plus" |
| | | plain |
| | | type="primary" |
| | | style="width: 100%" |
| | | @click="addApprovalStep">æ°å¢èç¹</u-button> |
| | | </view> |
| | | </view> |
| | | <up-popup :show="showTimePicker" |
| | | mode="bottom" |
| | | @close="showTimePicker = false"> |
| | |
| | | // ç产管æ§åè½æ°æ® |
| | | const productionItems = reactive([ |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | icon: "/static/images/icon/shengchanjihua.svg", |
| | | label: "主ç产计å", |
| | | }, |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | icon: "/static/images/icon/shengchandingdan.svg", |
| | | label: "ç产订å", |
| | | }, |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | icon: "/static/images/icon/shengchanzhuisu.svg", |
| | | label: "ç产追溯", |
| | | }, |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | icon: "/static/images/icon/shengchanshikuang.svg", |
| | | label: "å·¥åºç产å®åµ", |
| | | }, |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | icon: "/static/images/icon/shengchanpaichan.svg", |
| | | label: "ç产æäº§", |
| | | }, |
| | | |
| | |
| | | label: "ç产æ¥å·¥", |
| | | }, |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | icon: "/static/images/icon/baogongtaizhang.svg", |
| | | label: "æ¥å·¥å°è´¦", |
| | | }, |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | icon: "/static/images/icon/shengchanhesuan.svg", |
| | | label: "çäº§æ ¸ç®", |
| | | }, |
| | | |
| | | { |
| | | icon: "/static/images/icon/shengchanbaogong.svg", |
| | | label: "å·¥åºå®åµ", |
| | | }, |
| | | ]); |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34784"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34784)"><path d="M22.914785281249998,14.7584486125C19.94918328125,14.5709972125,17.63954128125,12.1115937125,17.63853528125,9.140073812499999C17.63853528125,7.8441987125,18.08040928125,6.6541986125,18.81540928125,5.7021988625C16.85716058125,5.1570737325,16.802909881250002,5.6943240125,15.13253498125,6.5203237125C13.82265998125,6.7941990125,13.91541008125,5.0013237625,12.260785081249999,5.8281989124999996C13.102535281249999,7.6236989125,13.792035081249999,8.1040738125,13.94516038125,8.736698412500001C13.753535281249999,8.7576985125,13.60566088125,8.8758237125,13.60566088125,9.0201988125C13.60566088125,9.1636982125,13.753535281249999,9.282698912499999,13.94516138125,9.3028235125C13.93991188125,9.3308234125,13.93641088125,9.3570738125,13.92853638125,9.3894486125C13.799910981250001,9.8951988125,12.595911481249999,10.705448112500001,11.571286181249999,11.464948612499999L11.571286181249999,18.7930733125C11.571286181249999,19.3863233125,11.16791058125,19.865823312499998,10.670036281249999,19.865823312499998L7.51128649725,19.865823312499998C7.19803667125,22.8863223125,8.09316146125,25.6539483125,11.87228678125,25.7878223125C15.48691178125,25.9138223125,15.94628718125,25.9138223125,15.94628718125,25.9138223125C15.94628718125,25.9138223125,16.40566158125,25.9138223125,20.021161281250002,25.7878223125C26.02016028125,25.5751973125,24.75928728125,18.7221973125,22.914785281249998,14.7584486125ZM18.59141028125,17.703697312499997L18.59141028125,19.0109473125L16.719785681250002,19.0109473125L16.719785681250002,19.546446312500002L18.57128528125,19.546446312500002L18.57828428125,20.8598213125L16.71978378125,20.8545703125L16.71978378125,22.4269463125L15.25503448125,22.4199463125L15.25503448125,20.8545723125L13.40003438125,20.8545723125L13.40003438125,19.547323312499998L15.25503448125,19.547323312499998L15.25503448125,19.0118233125L13.40003438125,19.0118233125L13.40003438125,17.704573312500003L15.04503438125,17.697574312500002L12.55653478125,14.2518244125L14.20153378125,14.2518244125L16.04865928125,16.6685753125L17.693658281250002,14.2308264125L19.33778628125,14.2308264125L16.934161181249998,17.6975763125L18.59141028125,17.703697312499997Z" fill="#CFCFCF" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M22.914785281249998,14.7584486125C19.94918328125,14.5709972125,17.63954128125,12.1115937125,17.63853528125,9.140073812499999C17.63853528125,7.8441987125,18.08040928125,6.6541986125,18.81540928125,5.7021988625C16.85716058125,5.1570737325,16.802909881250002,5.6943240125,15.13253498125,6.5203237125C13.82265998125,6.7941990125,13.91541008125,5.0013237625,12.260785081249999,5.8281989124999996C13.102535281249999,7.6236989125,13.792035081249999,8.1040738125,13.94516038125,8.736698412500001C13.753535281249999,8.7576985125,13.60566088125,8.8758237125,13.60566088125,9.0201988125C13.60566088125,9.1636982125,13.753535281249999,9.282698912499999,13.94516138125,9.3028235125C13.93991188125,9.3308234125,13.93641088125,9.3570738125,13.92853638125,9.3894486125C13.799910981250001,9.8951988125,12.595911481249999,10.705448112500001,11.571286181249999,11.464948612499999L11.571286181249999,18.7930733125C11.571286181249999,19.3863233125,11.16791058125,19.865823312499998,10.670036281249999,19.865823312499998L7.51128649725,19.865823312499998C7.19803667125,22.8863223125,8.09316146125,25.6539483125,11.87228678125,25.7878223125C15.48691178125,25.9138223125,15.94628718125,25.9138223125,15.94628718125,25.9138223125C15.94628718125,25.9138223125,16.40566158125,25.9138223125,20.021161281250002,25.7878223125C26.02016028125,25.5751973125,24.75928728125,18.7221973125,22.914785281249998,14.7584486125ZM18.59141028125,17.703697312499997L18.59141028125,19.0109473125L16.719785681250002,19.0109473125L16.719785681250002,19.546446312500002L18.57128528125,19.546446312500002L18.57828428125,20.8598213125L16.71978378125,20.8545703125L16.71978378125,22.4269463125L15.25503448125,22.4199463125L15.25503448125,20.8545723125L13.40003438125,20.8545723125L13.40003438125,19.547323312499998L15.25503448125,19.547323312499998L15.25503448125,19.0118233125L13.40003438125,19.0118233125L13.40003438125,17.704573312500003L15.04503438125,17.697574312500002L12.55653478125,14.2518244125L14.20153378125,14.2518244125L16.04865928125,16.6685753125L17.693658281250002,14.2308264125L19.33778628125,14.2308264125L16.934161181249998,17.6975763125L18.59141028125,17.703697312499997Z" fill="#FF9533" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M10.38227634375,2.37548828125L0.92615223375,2.37548828125C0.42740228375,2.37548828125,0.02490234375,2.85586330125,0.02490234375,3.4464882812499997L0.02490234375,18.43261328125C0.02490234375,19.02323728125,0.42740231375,19.50186328125,0.92615223375,19.50186328125L10.38315134375,19.50186328125C10.88102634375,19.50186328125,11.28440134375,19.02323728125,11.28440134375,18.43261328125L11.28440134375,3.4464882812499997C11.28352534375,2.85586330125,10.88102534375,2.37548828125,10.38227634375,2.37548828125ZM3.62815214375,17.89798828125C3.62815214375,18.19286328125,3.42690204375,18.43261328125,3.17840214375,18.43261328125L1.82565224375,18.43261328125C1.57802724375,18.43261328125,1.37590234375,18.191988281249998,1.37590234375,17.89798828125L1.37590234375,16.29148728125C1.37590234375,15.99573828125,1.57715224375,15.75511128125,1.82565224375,15.75511128125L3.17665244375,15.75511128125C3.42602734375,15.75511128125,3.62815214375,15.99573728125,3.62815214375,16.29148728125L3.62815214375,17.89798828125ZM3.62815214375,14.15123828125C3.62815214375,14.44698828125,3.42690204375,14.68673828125,3.17840214375,14.68673828125L1.82565224375,14.68673828125C1.57802724375,14.68673828125,1.37590234375,14.44698828125,1.37590234375,14.15123828125L1.37590234375,12.54561328125C1.37590234375,12.25073818125,1.57715224375,12.01186368125,1.82565224375,12.01186368125L3.17665244375,12.01186368125C3.42602734375,12.01186368125,3.62815214375,12.25073818125,3.62815214375,12.54561328125L3.62815214375,14.15123828125ZM3.62815214375,10.40448858125C3.62815214375,10.70023918125,3.42690204375,10.94086358125,3.17840214375,10.94086358125L1.82565224375,10.94086358125C1.57802724375,10.94086358125,1.37590234375,10.70111368125,1.37590234375,10.40448858125L1.37590234375,8.79886388125C1.37590234375,8.503988781250001,1.57715224375,8.26423888125,1.82565224375,8.26423888125L3.17665244375,8.26423888125C3.42602734375,8.26423888125,3.62815214375,8.503988781250001,3.62815214375,8.79886388125L3.62815214375,10.40448858125ZM6.32927704375,18.43261528125L4.97827724375,18.43261528125C4.72890234375,18.43261528125,4.52852724375,18.19199028125,4.52852724375,17.89798928125L4.52852724375,16.29148928125C4.52852724375,15.99574028125,4.73065234375,15.75511328125,4.97827724375,15.75511328125L6.32927704375,15.75511328125C6.57865194375,15.75511328125,6.77902694375,15.99573828125,6.77902694375,16.29148928125L6.77902694375,17.89798928125L6.77990194375,17.89798928125C6.78077694375,18.19286428125,6.57865194375,18.43261528125,6.32927704375,18.43261528125ZM6.32927704375,14.68674028125L4.97827724375,14.68674028125C4.72890234375,14.68674028125,4.52852724375,14.44698928125,4.52852724375,14.15124028125L4.52852724375,12.54561528125C4.52852724375,12.25074008125,4.73065234375,12.01186558125,4.97827724375,12.01186558125L6.32927704375,12.01186558125C6.57865194375,12.01186558125,6.77902694375,12.25074008125,6.77902694375,12.54561528125L6.77902694375,14.15124028125L6.77990194375,14.15124028125C6.78077694375,14.44698928125,6.57865194375,14.68674028125,6.32927704375,14.68674028125ZM6.32927704375,10.93999008125L4.97827724375,10.93999008125C4.72890234375,10.93999008125,4.52852724375,10.70024018125,4.52852724375,10.40361498125L4.52852724375,8.79798988125C4.52852724375,8.50311518125,4.73065234375,8.26336478125,4.97827724375,8.26336478125L6.32927704375,8.26336478125C6.57865194375,8.26336478125,6.77902694375,8.50311518125,6.77902694375,8.79798988125L6.77902694375,10.40361498125L6.77990194375,10.40361498125C6.78077694375,10.70024018125,6.57865194375,10.93999008125,6.32927704375,10.93999008125ZM9.93252654375,17.89798928125C9.93252654375,18.19286428125,9.73127744375,18.43261528125,9.48277664375,18.43261528125L8.13090134375,18.43261528125C7.88152694375,18.43261528125,7.68027644375,18.19199028125,7.68027644375,17.89798928125L7.68027644375,16.29148928125C7.68027644375,15.99574028125,7.88152594375,15.75511328125,8.13090134375,15.75511328125L9.48190114375,15.75511328125C9.72952654375,15.75511328125,9.93165114375,15.99573828125,9.93165114375,16.29148928125L9.93165114375,17.89798928125L9.93252654375,17.89798928125ZM9.93252654375,14.15124028125C9.93252654375,14.44698928125,9.73127744375,14.68674028125,9.48277664375,14.68674028125L8.13090134375,14.68674028125C7.88152694375,14.68674028125,7.68027644375,14.44698928125,7.68027644375,14.15124028125L7.68027644375,12.54561528125C7.68027644375,12.25074008125,7.88152594375,12.01186558125,8.13090134375,12.01186558125L9.48190114375,12.01186558125C9.72952654375,12.01186558125,9.93165114375,12.25074008125,9.93165114375,12.54561528125L9.93165114375,14.15124028125L9.93252654375,14.15124028125ZM9.93252654375,10.40449048125C9.93252654375,10.70024108125,9.73127744375,10.94086548125,9.48277664375,10.94086548125L8.13090134375,10.94086548125C7.88152694375,10.94086548125,7.68027644375,10.70111558125,7.68027644375,10.40449048125L7.68027644375,8.79886528125C7.68027644375,8.50399068125,7.88152594375,8.26424028125,8.13090134375,8.26424028125L9.48190114375,8.26424028125C9.72952654375,8.26424028125,9.93165114375,8.50399068125,9.93165114375,8.79886528125L9.93165114375,10.40449048125L9.93252654375,10.40449048125ZM9.93252654375,6.65686558125L1.37590234375,6.65686558125L1.37590234375,3.98286338125L9.93165204375,3.98286338125L9.93165204375,6.65686318125L9.93252654375,6.65686558125ZM23.27452634375,4.50086328125C20.71282234375,4.50086328125,18.63615234375,6.57753468125,18.63615234375,9.13923788125C18.63615234375,11.70094208125,20.71282234375,13.77761328125,23.27452634375,13.77761328125C25.83623134375,13.77761328125,27.91290234375,11.70094208125,27.91290234375,9.13923788125C27.91290234375,6.57753468125,25.83623134375,4.50086328125,23.27452634375,4.50086328125ZM26.45340134375,8.56961348125C26.45340134375,8.606363281250001,26.41489934375,8.64398858125,26.37815134375,8.64398858125L25.85314934375,8.64398858125C25.81552534375,8.64398858125,25.77702534375,8.68161388125,25.74027634375,8.719239281250001L25.28965234375,9.20573898125L25.28965234375,9.317738981249999L25.66502734375,10.06761458125C25.70177634375,10.10436438125,25.74027634375,10.14198968125,25.77702734375,10.14198968125L26.00190134375,10.14198968125C26.04236234375,10.14420228125,26.07453734375,10.176757381249999,26.07627634375,10.21723988125L26.07627634375,10.81748958125C26.07627634375,10.85423948125,26.03952634375,10.89186478125,26.00190134375,10.89186478125L25.40252734375,10.89186478125C25.36577834375,10.89186478125,25.28965234375,10.85423948125,25.28965234375,10.81748958125L24.80140334375,9.842739581250001C24.80140334375,9.805114281249999,24.76465234375,9.805114281249999,24.72702834375,9.842739581250001L23.86515234375,10.81748958125C23.86515234375,10.85423948125,23.78990134375,10.89186478125,23.75227734375,10.89186478125L23.15290234375,10.89186478125C23.11527834375,10.89186478125,23.07852934375,10.85423948125,23.07852934375,10.81748958125L23.07852934375,10.21723988125C23.07852934375,10.17961458125,23.11527834375,10.14198968125,23.15290234375,10.14198968125L23.67790434375,10.14198968125C23.71640434375,10.14198968125,23.75227734375,10.10436438125,23.78990334375,10.10436438125L24.46452934375,9.317738981249999L24.46452934375,9.20573898125L24.24052834375,8.719239281250001C24.20202834375,8.68248938125,24.16527734375,8.64398858125,24.12677934375,8.64398858125L23.52565334375,8.64398858125C23.48890534375,8.64398858125,23.45215434375,8.606363281250001,23.45215434375,8.56961348125C23.45215434375,8.606363281250001,23.41452934375,8.64398858125,23.37690334375,8.64398858125L22.51590534375,8.64398858125L21.95152834375,10.89273838125C21.87890434375,11.26723858125,21.76602934375,11.60411358125,21.57703034375,11.79223828125C21.42828034375,11.97948838125,21.12903034375,12.01711368125,20.82802934375,12.01711368125L20.45352934375,12.01711368125L20.45352934375,11.26723768125L20.82802934375,11.26723768125C20.82802934375,11.26723768125,21.12990334375,11.34161278125,21.24190334375,10.89273738125L21.80365334375,8.64398768125L20.90502934375,8.64398768125C20.86652734375,8.64398768125,20.82890334375,8.606362781249999,20.82890334375,8.56961298125L20.82890334375,7.97023778125C20.82890334375,7.93261238125,20.86652734375,7.89498708125,20.90502934375,7.89498708125L21.99177934375,7.89498708125L22.17902934375,7.14598708125C22.29102934375,6.77061228125,22.47915434375,6.54573728125,22.66552934375,6.39611248125C23.37777934375,5.79586268125,24.20290334375,6.05836198125,24.57827934375,6.13361268125L24.57827934375,6.77061228125C24.20290334375,6.69711258125,23.64115534375,6.50811198125,23.22815534375,6.80823758125C23.11615534375,6.88348768125,22.92890534375,7.07161238125,22.89128134375,7.18361238125L22.70490634375,7.89498708125L23.37865634375,7.89498708125C23.41628034375,7.89498708125,23.45390734375,7.93261238125,23.45390734375,7.97023778125C23.45390734375,7.93261238125,23.49065634375,7.89498708125,23.52740634375,7.89498708125L24.50303034375,7.89498708125C24.54153034375,7.89498708125,24.57828134375,7.93261238125,24.61590534375,7.97023778125L24.95278134375,8.64486218125C24.95278134375,8.68248748125,24.99040634375,8.68248748125,25.02803034375,8.64486218125L25.66590734375,7.97023778125C25.66590734375,7.93261238125,25.74115734375,7.89498708125,25.77790634375,7.89498708125L26.37903234375,7.89498708125C26.41578134375,7.89498708125,26.45428234375,7.93261238125,26.45428234375,7.97023778125L26.45428234375,8.56961298125L26.45340134375,8.56961348125Z" fill="#FFDB42" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34441"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34750"><rect x="7" y="5" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34441)"><g transform="matrix(1,3.7247229744963306e-9,-3.7247229744963306e-9,1,7.449445948992661e-9,-7.449445948992661e-9)"><path d="M8.6032233,27C7.2891606,27,6.33216852,28.2456321,6.67075348,29.5153255L7.2081735,31.5306506C7.6751106,33.2816644,9.260909999999999,34.5,11.0731134,34.5L16.926887999999998,34.5C18.739091000000002,34.5,20.324889,33.2816644,20.791826,31.5306506L21.329247000000002,29.5153253C21.667831,28.2456321,20.710839,27,19.396776,27L8.6032233,27Z" fill="#69FFB4" fill-opacity="1" transform="matrix(1,0,0,-1,0,54)"/><path d="M2,8.000001000000001L2,17.499999000000003C2,20.813709,4.6862917,23.5,8.000000499999999,23.5L20,23.5C23.313709,23.5,26,20.813709,26,17.5L26,8.000001000000001C26,4.6862917,23.313709,2,20,2L8.000001000000001,2C4.6862917,2,2,4.6862917,2,8.000001000000001Z" fill="#01BF34" fill-opacity="1" style="opacity:0.800000011920929;"/></g><g clip-path="url(#master_svg1_141_34750)"><path d="M10.5241249,13.861916515625001C10.4281249,13.975916815625,10.383124800000001,14.121916815625,10.3871248,14.299916315625001C10.3911247,14.477916715625,10.4491248,14.623916615625,10.5581248,14.737916015625C10.6671247,14.851916315625,10.8131247,14.908916515625,10.9961247,14.908916515625L12.3081245,14.908916515625C12.2621245,15.045916515625,12.235124599999999,15.177916515625,12.226124800000001,15.304916415625C12.2171249,15.432916615625,12.2171249,15.564916615625,12.226124800000001,15.700916315625C12.226124800000001,16.229916615625,12.2881246,16.728916015625,12.4111247,17.197916015624997C12.5341249,17.667916015625,12.705124900000001,18.102916015625,12.9241247,18.503916015625002L10.203125,18.503916015625002C9.7111249,18.485916015625,9.30112505,18.312916015625,8.97312498,17.983915015625C8.64512491,17.654915015625,8.472125053,17.245915015625002,8.453125,16.753916015625002L8.453125,8.263916015625C8.471125007,7.771915915625,8.64412498,7.359915975625,8.97312498,7.026916025625C9.30112505,6.693916085625,9.7111249,6.522916078525,10.203125,6.513916015625L17.8181248,6.513916015625C18.3101244,6.522915959325,18.722125,6.693916085625,19.055125,7.026916025625C19.388125000000002,7.359915975625,19.559125,7.771915915625,19.568126,8.263916015625L19.568126,10.533916015625C19.304126,10.405916215625,19.010126,10.314916115625,18.686126,10.260915715625C18.3621254,10.205915915624999,18.0461254,10.183915615625,17.736125899999998,10.192915915625L17.640126199999997,10.192915915625C17.5941267,10.101915815625,17.5331259,10.033916015625,17.455125799999998,9.987916015625C17.3771257,9.941916015625,17.293126100000002,9.919916115625,17.2021255,9.919916115625L10.818125,9.919916115625C10.6811249,9.919916115625,10.5601251,9.965916115625,10.456125,10.056916215625C10.351125,10.147916315625,10.299125,10.279916315625,10.299125,10.452916115625C10.299125,10.625916015625,10.351125,10.760916215624999,10.456125,10.855916015624999C10.561125,10.951916215625001,10.6821251,11.021915915625,10.818125,11.067915915625L14.851124800000001,11.067915915625C14.322125,11.368916015625,13.864124799999999,11.737916015625,13.477124700000001,12.174915815624999C13.0891247,12.612916015625,12.7871246,13.117916115625,12.5681248,13.692915915625L10.996125,13.692915915625C10.7771249,13.690916015625,10.6201251,13.747916215625,10.5241249,13.861916515625001ZM20.327126,13.061916315625C21.024124999999998,13.726916315625001,21.387126000000002,14.574916815625,21.414125,15.604916615625C21.387124999999997,16.643917015625,21.024124999999998,17.509916015625002,20.327126,18.202917015624998C19.630126,18.895918015625,18.766126,19.255917015625002,17.736125,19.282917015625C16.697125399999997,19.255917015625002,15.8311253,18.886917015625002,15.1381254,18.175917015625C14.4451256,17.464916015625,14.085125399999999,16.607917015625,14.0581255,15.605917015625C14.085125399999999,14.621916815625,14.4451256,13.784916915625,15.1381254,13.096917115625C15.8311253,12.408916915625,16.697125399999997,12.050917115625001,17.736125,12.023917215625C18.766125000000002,12.050917115625001,19.630125,12.396917315625,20.327126,13.061916315625ZM20.020125,16.671916015625C20.120126,16.571916015625,20.170125,16.452916115625,20.170125,16.316916515625C20.170125,16.180916815625,20.120125,16.059916515624998,20.020125,15.954916015625C19.920125,15.849916415625,19.801126,15.792916315625,19.665126,15.783916515625L17.8191261,15.783916515625L17.8191261,13.854915615625C17.8191261,13.717915515625,17.7661257,13.599915515625,17.6621265,13.499916115625C17.557127,13.399915715625,17.436126700000003,13.349916415625,17.3001261,13.349916415625C17.1641254,13.349916415625,17.0421257,13.399916615625,16.9381266,13.499916115625C16.833126999999998,13.599916415625,16.7761269,13.718915915625,16.767127000000002,13.854915615625L16.767127000000002,16.315916015625C16.7761269,16.452916115625,16.833126999999998,16.570916015625002,16.9381266,16.670916015625C17.043126100000002,16.770916015624998,17.1641264,16.825915015625,17.3001261,16.834915015625L19.665126,16.834915015625C19.801126,16.826915015624998,19.919126,16.771915015624998,20.020125,16.671916015625Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34449"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34776"><rect x="6" y="5" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34449)"><path d="M8.6032233,27C7.2891606,27,6.33216852,28.2456321,6.67075348,29.5153255L7.2081735,31.5306506C7.6751106,33.2816644,9.260909999999999,34.5,11.0731134,34.5L16.926887999999998,34.5C18.739091000000002,34.5,20.324889,33.2816644,20.791826,31.5306506L21.329247000000002,29.5153253C21.667831,28.2456321,20.710839,27,19.396776,27L8.6032233,27Z" fill="#FC99E5" fill-opacity="1" transform="matrix(1,0,0,-1,0,54)"/><path d="M2,8.000001000000001L2,17.499999000000003C2,20.813709,4.6862917,23.5,8.000000499999999,23.5L20,23.5C23.313709,23.5,26,20.813709,26,17.5L26,8.000001000000001C26,4.6862917,23.313709,2,20,2L8.000001000000001,2C4.6862917,2,2,4.6862917,2,8.000001000000001Z" fill="#6234F9" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_34776)"><path d="M19.536021859374998,6.0341796875L15.876568759375,6.0341796875C15.067975059375,6.0341796875,14.412474659375,6.6896797375,14.412474659375,7.4982734875L14.412474659375,11.157726287500001C14.412474659375,11.9663200875,15.067975059375,12.621820487499999,15.876568759375,12.621820487499999L19.536021859374998,12.621820487499999C20.344615859375,12.621820487499999,21.000115859375,11.9663200875,21.000115859375,11.157726287500001L21.000115859375,7.4982890875C21.000131859375,6.6896796275,20.344632859375,6.03417956829071,19.536021859374998,6.0341796875ZM19.634476859375,9.7672736875L15.778131459375,9.7672736875C15.535570159375,9.7672736875,15.338896759375,9.5706016875,15.338896759375,9.3280391875C15.338896759375,9.0854770875,15.535568259375,8.888804887500001,15.778131459375,8.888804887500001L19.634475859375,8.888804887500001C19.877036859375,8.888804887500001,20.073709859375,9.0854770875,20.073709859375,9.3280391875C20.073693859375,9.5706016875,19.877037859375,9.7672736875,19.634476859375,9.7672736875ZM19.525537859375,13.4172105875L15.873585659375,13.4172105875C15.066647559375,13.4172105875,14.412490859375,14.0713681875,14.412490859375,14.8783044875L14.412490859375,18.5303366875C14.412490859375,19.3372726875,15.066647559375,19.9914306875,15.873585659375,19.9914306875L19.525553859375,19.9914306875C20.332491859375,19.9914306875,20.986648859375002,19.3372726875,20.986648859375002,18.5303366875L20.986648859375002,14.8783197875C20.986648859375002,14.0713672875,20.332491859375,13.4172105875,19.525537859375,13.4172105875ZM17.698413859375002,14.9017886875L17.699912859375,14.9017886875C17.941976859375,14.9017886875,18.138240859375,15.0980548875,18.138240859375,15.3401164875C18.138240859375,15.5821790875,17.941976859375,15.7784452875,17.699912859375,15.7784452875C17.457850859375,15.7784452875,17.260866859375,15.5821790875,17.260866859375,15.3401164875C17.260866859375,15.0980548875,17.456350859375,14.9017886875,17.698413859375002,14.9017886875ZM17.699912859375,18.526601687499998C17.457850859375,18.526601687499998,17.260866859375,18.3303366875,17.260866859375,18.0882736875C17.260866859375,17.846211687500002,17.456350859375,17.6499456875,17.698413859375002,17.6499456875L17.699912859375,17.6499456875C17.941976859375,17.6499456875,18.138240859375,17.846211687500002,18.138240859375,18.0882736875C18.138240859375,18.3303366875,17.941976859375,18.526601687499998,17.699912859375,18.526601687499998ZM19.623787859375,17.1426626875L15.775351559375,17.1426626875C15.533288959375,17.1426626875,15.337023759375,16.9463986875,15.337023759375,16.7043356875C15.337023759375,16.4622726875,15.533288959375,16.266008687499998,15.775351559375,16.266008687499998L19.623787859375,16.266008687499998C19.865851859375,16.266008687499998,20.062116859375,16.4622726875,20.062116859375,16.7043356875C20.062116859375,16.9463986875,19.865851859375,17.1426626875,19.623787859375,17.1426626875ZM12.146208759375,6.0341796875L8.492740259375001,6.0341796875C7.685443399375,6.0341796875,7.031005859375,6.6886171675,7.031005859375,7.4959140875L7.031005859375,11.1494612875C7.031005859375,11.9567579875,7.685443339375,12.6111950875,8.492740259375001,12.6111950875L12.146209259375,12.6111950875C12.953506459375,12.6111950875,13.607944059375,11.9567579875,13.607944059375,11.1494612875L13.607944059375,7.4959140875C13.607944059375,6.6886172275,12.953506459375,6.0341796875,12.146208759375,6.0341796875ZM12.244490159375001,9.7612421875L10.757990359375,9.7612421875L10.757990359375,11.2477421875C10.757990359375,11.4899139875,10.561646659375,11.6862578875,10.319474959375,11.6862578875C10.077303159374999,11.6862578875,9.880959259375,11.4899139875,9.880959259375,11.2477421875L9.880959259375,9.7612421875L8.394459259375001,9.7612421875C8.152287459375,9.7612421875,7.955943529375,9.5648979875,7.955943529375,9.3227262875C7.955943529375,9.0805546875,8.152287259375,8.884210787499999,8.394459259375001,8.884210787499999L9.880959259375,8.884210787499999L9.880959259375,7.3977107875C9.880959259375,7.1555389875,10.077303159374999,6.9591950775,10.319474959375,6.9591950775C10.561646659375,6.9591950775,10.757990359375,7.1555387875,10.757990359375,7.3977107875L10.757990359375,8.884210787499999L12.244490159375001,8.884210787499999C12.486661959374999,8.884210787499999,12.683006259375,9.0805546875,12.683006259375,9.3227262875C12.683022059375,9.5648979875,12.486661959374999,9.7612421875,12.244490159375001,9.7612421875ZM12.154397059375,13.4631013875L8.521380759374999,13.4631013875C7.718615169375,13.4631013875,7.067833904375,14.1138667875,7.067833904375,14.9166488875L7.067833904375,18.549820687500002C7.067833904375,19.3525866875,7.718615169375,20.003366687499998,8.521380759374999,20.003366687499998L12.154397059375,20.003366687499998C12.957162859375,20.003366687499998,13.607944059375,19.3525856875,13.607944059375,18.549820687500002L13.607944059375,14.9166488875C13.607944059375,14.1138820875,12.957162859375,13.4631013875,12.154397059375,13.4631013875ZM11.999740159375001,17.7785396875C12.170084459375001,17.9488036875,12.170084459375001,18.2248996875,11.999740159375001,18.3951646875C11.914646659375,18.4803366875,11.803005659375,18.5229136875,11.691427659375,18.5229136875C11.579850159374999,18.5229136875,11.468209259375,18.4803366875,11.383115259375,18.3951646875L10.337849859375,17.3498996875L9.292584159375,18.3951646875C9.207490659375,18.4803366875,9.095849559375,18.5229136875,8.984271759375,18.5229136875C8.872693759375,18.5229136875,8.761053059375,18.4803366875,8.675959359375,18.3951646875C8.505615459375,18.2248996875,8.505615459375,17.9488036875,8.675959359375,17.7785396875L9.721224759375,16.733273687500002L8.675959359375,15.6880092875C8.505615459375,15.5177430875,8.505615459375,15.2416486875,8.675959359375,15.0713843875C8.846162559375,14.9010400875,9.122381259375,14.9010400875,9.292584659375,15.0713843875L10.337850359375,16.1166486875L11.383115759375,15.0713843875C11.553318959375,14.9010400875,11.829538359375,14.9010400875,11.999741059375001,15.0713843875C12.170084959375,15.2416486875,12.170084959375,15.5177430875,11.999741059375001,15.6880092875L10.954475159375,16.733273687500002L11.999740159375001,17.7785396875Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34460"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34770"><rect x="6.5" y="4" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34460)"><path d="M25,21.1513879C25,21.3787005,25.067284852,21.6009274,25.19337524,21.7900629L27.1094005,24.6641006C27.6657977,25.4986968,28.6024919,26,29.6055508,26L37,26C38.656855,26,40,24.656854199999998,40,23C40,21.343146,38.656855,20,37,20L26.1513879,20C25.51549393,20,25,20.51549393,25,21.1513879Z" fill="#FFDB42" fill-opacity="1" transform="matrix(-1,0,0,1,50,0)"/><rect x="2" y="2" width="25" height="21" rx="6" fill="#FF7B00" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_34770)"><path d="M19.6875,17.8359375L9.3125,17.8359375C8.59623432,17.8359375,8.015625,17.255328499999997,8.015625,16.5390625L8.015625,9.40625L20.984375,9.40625L20.984375,16.5390625C20.984375,17.255328499999997,20.403765999999997,17.8359375,19.6875,17.8359375ZM16.4453125,11.3515625C16.4453125,10.9934998,16.154999699999998,10.703125,15.796875,10.703125C15.7324848,10.703125,13.2675157,10.703125,13.203125,10.703125C12.8450623,10.703125,12.5546875,10.9934998,12.5546875,11.3515625C12.5546875,11.7096877,12.8450623,12,13.203125,12C13.2675157,12,13.4754062,12,13.5273438,12L14.9863281,12L13.203125,15.5664063C13.203125,15.7454996,13.348312400000001,15.890625,13.5273438,15.890625L14.1757812,15.890625C14.354875100000001,15.890625,14.5,15.7454996,14.5,15.5664063L16.4453125,11.6757813C16.4453125,11.6238437,16.4453125,11.4159532,16.4453125,11.3515625ZM8.015625,8.109375C8.015625,7.3931093,8.59623432,6.8125,9.3125,6.8125L10.6747971,6.8125C10.8093438,6.4365938,11.1598282,6.1640625,11.5820312,6.1640625C12.0042343,6.1640625,12.3547029,6.4365938,12.4892654,6.8125L16.5107498,6.8125C16.6452971,6.4365938,16.9957819,6.1640625,17.417984,6.1640625C17.8401871,6.1640625,18.190656,6.4365938,18.325219,6.8125L19.6875,6.8125C20.403765999999997,6.8125,20.984375,7.3931093,20.984375,8.109375L20.984375,8.7578125L8.015625,8.7578125L8.015625,8.109375ZM17.09375,8.109375L17.7421875,8.109375L17.7421875,6.8125L17.09375,6.8125L17.09375,8.109375ZM11.2578125,8.109375L11.90625,8.109375L11.90625,6.8125L11.2578125,6.8125L11.2578125,8.109375Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34407"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34756"><rect x="9" y="7" width="14" height="14" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34407)"><path d="M1,10L1,18C1,20.209139,2.7908611,22,5.000001,22L6.9999995,22C8.6568542,22,10,20.656855,10,19L10,9.0000005C10,7.3431458,8.6568542,6,6.9999995,6L5.0000005,6C2.7908611,6,1,7.7908611,1,10Z" fill="#7BF1FF" fill-opacity="1"/><rect x="5" y="2" width="22" height="24" rx="6" fill="#0066FF" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_34756)"><path d="M20.666666,7L11.3333335,7C10.0422223,7,9,8.0422223,9,9.3333335L9,18.666667C9,19.957777999999998,10.0422223,21,11.3333335,21L20.666667,21L19.888889,21L19.904445000000003,20.984445C20.106667,20.984445,20.308888,20.906667,20.464444999999998,20.758889L22.782222,18.441112C22.945556,18.277779000000002,23.015556,18.052222,23,17.834443999999998L23,9.3333335C23,8.0422223,21.957777999999998,7,20.666666,7ZM12.8888891,10.1111112L19.111111,10.1111112C19.538888999999998,10.1111112,19.888889,10.461111299999999,19.888889,10.8888891C19.888889,11.3166666,19.538888999999998,11.666667,19.111111,11.666667L12.8888891,11.666667C12.461111299999999,11.666667,12.1111114,11.3166666,12.1111114,10.8888891C12.1111114,10.4611115,12.4611115,10.1111114,12.8888891,10.1111112ZM12.1111112,14C12.1111112,13.572222199999999,12.461111299999999,13.2222223,12.8888891,13.2222223L19.111111,13.2222223C19.538888999999998,13.2222223,19.888889,13.572222199999999,19.888889,14C19.888889,14.427777800000001,19.538888999999998,14.777778099999999,19.111111,14.777778099999999L12.8888891,14.777778099999999C12.461111299999999,14.777778099999999,12.1111114,14.427778199999999,12.1111112,14ZM20.666666,17.888889L19.888888,17.888889L19.888888,18.666667C19.888888,19.094445,19.538888999999998,19.444446,19.11111,19.444446C18.6833315,19.444446,18.3333321,19.094445999999998,18.3333321,18.666667L18.3333321,17.111112C18.3333321,16.6833344,18.6833315,16.333334,19.11111,16.333334L20.666666,16.333334C21.094443,16.333334,21.444444,16.683333400000002,21.444444,17.111112C21.444444,17.538890000000002,21.094444,17.88889,20.666666,17.888889Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_42939"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_42954"><rect x="6" y="5" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_42939)"><path d="M22,22.0000005L22,22.9999995C22,25.2091389,23.7908611,27,26,27L34,27C36.209139,27,38,25.2091389,38,22.9999995L38,22.0000005C38,20.3431458,36.656855,19,35,19L25.0000005,19C23.3431458,19,22,20.3431458,22,22.0000005Z" fill="#FDC3C1" fill-opacity="1" transform="matrix(-1,0,0,1,44,0)"/><rect x="2" y="2" width="24" height="22" rx="6" fill="#FF2B00" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_42954)"><path d="M19.5976818125,7.975256428125C19.429908812500003,7.822927328125,19.2089708125,7.739575828125,18.980501812500002,7.739575828125C18.9596658125,7.739575828125,18.9388328125,7.739575828125,18.9180168125,7.741015028125C18.9054158125,7.742438228125,18.759929812499998,7.7511042281249996,18.5325648125,7.7511042281249996C18.1557318125,7.7511042281249996,17.4200373125,7.725233628125,16.7231331125,7.560001828125C15.823278412499999,7.345890628125,14.7847490125,6.354385998125,14.4826178125,6.158966188125C14.3303046125,6.059849266125,14.1550002125,6.010986328125,13.9793429125,6.010986328125C13.8044057125,6.010986328125,13.6290998125,6.059849266125,13.4771390125,6.157543068125C13.4405084125,6.181974528125,12.3530998125,7.3070687281249995,11.2894523125,7.560001828125C10.5929158125,7.725233928125,9.8431994125,7.7511042281249996,9.4667181125,7.7511042281249996C9.2393200125,7.7511042281249996,9.0942183125,7.742438228125,9.0798602125,7.741015028125C9.0600977525,7.739575828125,9.0403350025,7.739575828125,9.0202204625,7.739575828125C8.7913834425,7.739575828125,8.5697414925,7.822911628125,8.4012634725,7.975256428125C8.2184271925,8.140488628125,8.1142578125,8.371819928125,8.1142578125,8.614663628125001L8.1142578125,10.803070528125C8.1142578125,18.892904328125,13.5874648125,19.934664328125002,13.8194995125,19.976283328125C13.8726792125,19.984965328125,13.926195612499999,19.989251328125,13.9797263125,19.989251328125C14.0328917125,19.989251328125,14.0871253125,19.984965328125,14.1395697125,19.976283328125C14.3719735125,19.934649328124998,19.8853928125,18.892889328125,19.8853928125,10.803071028125L19.8853928125,8.614663628125001C19.8853928125,8.371819928125,19.7808728125,8.140504328125001,19.5976818125,7.975256428125ZM17.5744924125,11.104817828125L13.779988812500001,14.776156428125C13.756276612499999,14.814913728125,13.7271771125,14.853767428125,13.6919684125,14.886800728125C13.580604512499999,14.994567828125,13.4333286125,15.046293228125,13.287458412500001,15.043415028125C13.1416373125,15.046293228125,12.9946804125,14.994567828125,12.8833489125,14.886800728125C12.8481564125,14.853767428125,12.8187046125,14.814913728125,12.7949929125,14.776156428125L10.7671649125,12.813325428125001C10.5512478125,12.604953728125,10.5512478125,12.267311128125,10.7671649125,12.057500328125C10.9830506125,11.849193528125,11.3332775125,11.849193528125,11.549195812499999,12.057500328125L13.287475112500001,13.740152828125L16.7928295125,10.349056728125C17.0087471125,10.140685528125001,17.3586063125,10.140685528125001,17.5745096125,10.349056728125C17.7903948125,10.557348228125,17.7903948125,10.896494828125,17.5744924125,11.104817828125Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_42929"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_42949"><rect x="6" y="8" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_42929)"><path d="M6,13.0000005L6,15.590147C6,16.842423,7.1377500000000005,17.7866721,8.368577,17.555892L14,16.5L19.631422999999998,17.555892C20.86225,17.7866721,22,16.842423,22,15.590147L22,13.0000005C22,11.3431458,20.656855,10,19,10L9.0000005,10C7.3431458,10,6,11.3431458,6,13.0000005Z" fill="#FC99E5" fill-opacity="1" transform="matrix(1,0,0,-1,0,20)"/><rect x="2" y="5" width="24" height="22" rx="6" fill="#6234F9" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_42949)"><path d="M19.743700671875,9.612548828125L8.276513691875,9.612548828125C7.901513691875,9.612548828125,7.598388671875,9.915673848125,7.598388671875,10.290673968125L7.598388671875,21.709423828125C7.598388671875,22.082861828125,7.901513691875,22.385986828125,8.276513691875,22.385986828125L19.204639671875,22.385986828125L18.021826671875,21.204736828125C17.992138671875,21.175048828125,17.967139671875,21.143798828125,17.943701671875,21.109424828125C17.193701771875,21.618798828125,16.309327171874997,21.892236828125,15.381201771875,21.892236828125C14.159326571874999,21.892236828125,13.010889071874999,21.415673828125,12.146826771875,20.551611828125C11.282764471875,19.687549828125,10.806201971875,18.539112128124998,10.806201971875,17.317236928125C10.806201971875,16.095361728125,11.282764471875,14.946924228124999,12.146826771875,14.082861928125C13.010889071874999,13.218799828125,14.159326571874999,12.742237328125,15.381201771875,12.742237328125C16.603076971874998,12.742237328125,17.751514671875,13.218799828125,18.617138671875,14.082861928125C19.481200671875,14.946924228124999,19.957763671875,16.095361728125,19.957763671875,17.317236928125C19.957763671875,18.417237328124997,19.571825671875,19.456299828124997,18.865575671875,20.282861828125C18.879638671875,20.293799828125,18.892138671875,20.306298828125,18.906200671875,20.318798828125L20.410887671875003,21.823486828125C20.417137671875,21.785985828125,20.421825671875,21.746923828125,20.421825671875,21.707860828125L20.421825671875,10.289111378125C20.420263671875,9.915673848125,20.117137671875,9.612548828125,19.743700671875,9.612548828125ZM12.195263871875,12.225048828125C12.082763671875,12.337548928124999,11.926513671875,12.407861428124999,11.753076571874999,12.407861428124999L9.846826571874999,12.407861428124999C9.501513871875,12.407861428124999,9.221826471875,12.128174028125,9.221826471875,11.782861428124999C9.221826471875,11.610986428124999,9.292138971875,11.454736428124999,9.404638871875001,11.340673928125C9.517138871875,11.226611328125,9.673388971875,11.157861428124999,9.846826571874999,11.157861428124999L11.754639171874999,11.157861428124999C12.099951771875,11.157861428124999,12.379639171874999,11.437548828125,12.379639171874999,11.782861428124999C12.378076571874999,11.956299028125,12.309326671874999,12.112549028125,12.195263871875,12.225048828125ZM17.787451671875,15.867236128125L18.546826671875,15.867236128125C17.995263671875,14.670361528125,16.784326571875,13.837549228124999,15.382763871875,13.837549228124999C13.464014071874999,13.837549228124999,11.901514071874999,15.400049228124999,11.901514071874999,17.318799028125C11.901514071874999,19.239111928125,13.464014071874999,20.800048828125,15.382763871875,20.800048828125C16.682764071875,20.800048828125,17.818701671875,20.082860828125,18.417138671875,19.025049228125L17.787451671875,19.025049228125C17.442139671874997,19.025049228125,17.162451771875,18.745361328125,17.162451771875,18.400049228125L17.162451771875,16.492236128125C17.162451771875,16.146924028125,17.442139671874997,15.867236128125,17.787451671875,15.867236128125ZM15.045264271875,18.398486128125C15.045264271875,18.743798228125,14.765576371875,19.023486128125,14.420264271875,19.023486128125C14.248389271875,19.023486128125,14.092139271875,18.953173628125,13.978076971875,18.840673428125C13.865576771875,18.728173228125,13.795264271875,18.571923228125,13.795264271875,18.398486128125L13.795264271875,16.492236128125C13.795264271875,16.146924028125,14.074952171875001,15.867236128125,14.420264271875,15.867236128125C14.592139271875,15.867236128125,14.748389271875,15.937548628125,14.862451571874999,16.050048828125C14.974951771875,16.162549028125,15.045264271875,16.318799028125,15.045264271875,16.492236128125L15.045264271875,18.398486128125ZM16.787451771875,18.398486128125C16.787451771875,18.743798228125,16.507763871875,19.023486128125,16.162451771875,19.023486128125C15.990576771875,19.023486128125,15.834326771875,18.953173628125,15.720264471875,18.840673428125C15.606202171875,18.728173228125,15.537451771875,18.571923228125,15.537451771875,18.398486128125L15.537451771875,16.492236128125C15.537451771875,16.146924028125,15.817139671875,15.867236128125,16.162451771875,15.867236128125C16.334326771875,15.867236128125,16.490576771875,15.937548628125,16.604639071875,16.050048828125C16.717139271875,16.162549028125,16.787451771875,16.318799028125,16.787451771875,16.492236128125L16.787451771875,18.398486128125Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |