| | |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * @desc æ¥ä¿®éªæ¶ç¡®è®¤ |
| | | * @param {éªæ¶åæ°} data |
| | | * @returns |
| | | */ |
| | | export const repairAcceptance = (data) => { |
| | | return request({ |
| | | url: "/device/repair/acceptance", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| | | export const getSparePartsList = (params) => { |
| | | return request({ |
| | | url: "/spareParts/listPage", |
| | |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/repair/acceptance", |
| | | "style": { |
| | | "navigationBarTitleText": "è®¾å¤æ¥ä¿®éªæ¶", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/repair/detail", |
| | | "style": { |
| | | "navigationBarTitleText": "è®¾å¤æ¥ä¿®è¯¦æ
", |
| | | "navigationStyle": "custom" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/equipmentManagement/upkeep/index", |
| | | "style": { |
| | | "navigationBarTitleText": "设å¤ä¿å
»", |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="repair-acceptance"> |
| | | <PageHeader title="è®¾å¤æ¥ä¿®éªæ¶" |
| | | @back="goBack" /> |
| | | <!-- æ¥ä¿®ä¿¡æ¯ --> |
| | | <view class="section"> |
| | | <view class="section-title">æ¥ä¿®ä¿¡æ¯</view> |
| | | <view class="info-item"> |
| | | <text class="info-label">设å¤åç§°</text> |
| | | <text class="info-value">{{ detail.deviceName || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">è§æ ¼åå·</text> |
| | | <text class="info-value">{{ detail.deviceModel || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">æ¥ä¿®æ¥æ</text> |
| | | <text class="info-value">{{ formatDate(detail.repairTime) || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">æ¥ä¿®äºº</text> |
| | | <text class="info-value">{{ detail.repairName || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">维修人</text> |
| | | <text class="info-value">{{ detail.maintenanceName || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">维修项ç®</text> |
| | | <text class="info-value">{{ detail.machineryCategory || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">æ
éç°è±¡</text> |
| | | <text class="info-value multi-line">{{ detail.remark || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">ç»´ä¿®ç»æ</text> |
| | | <text class="info-value multi-line">{{ detail.maintenanceResult || '-' }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">ç»´ä¿®æ¥æ</text> |
| | | <text class="info-value">{{ formatDateTime(detail.maintenanceTime) || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- éªæ¶è¡¨å --> |
| | | <u-form ref="formRef" |
| | | :model="form" |
| | | label-width="110"> |
| | | <u-cell-group title="éªæ¶ä¿¡æ¯"> |
| | | <u-form-item label="éªæ¶äºº" |
| | | prop="acceptanceName" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.acceptanceName" |
| | | disabled |
| | | placeholder="éªæ¶äºº" /> |
| | | </u-form-item> |
| | | <u-form-item label="éªæ¶æ¶é´" |
| | | prop="acceptanceTime" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.acceptanceTime" |
| | | placeholder="è¯·éæ©éªæ¶æ¶é´" |
| | | readonly |
| | | @click="showDatePicker = true" /> |
| | | <template #right> |
| | | <u-icon name="arrow-right" |
| | | @click="showDatePicker = true"></u-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="éªæ¶å¤æ³¨" |
| | | prop="acceptanceRemark" |
| | | required |
| | | border-bottom> |
| | | <u-textarea v-model="form.acceptanceRemark" |
| | | placeholder="请è¾å
¥éªæ¶å¤æ³¨" |
| | | :maxlength="200" |
| | | count |
| | | :autoHeight="true" /> |
| | | </u-form-item> |
| | | </u-cell-group> |
| | | <view class="footer-btns"> |
| | | <u-button class="cancel-btn" |
| | | @click="goBack">åæ¶</u-button> |
| | | <u-button class="save-btn" |
| | | type="primary" |
| | | @click="handleSubmit" |
| | | :loading="loading">éªæ¶ç¡®è®¤</u-button> |
| | | </view> |
| | | </u-form> |
| | | <up-datetime-picker :show="showDatePicker" |
| | | v-model="pickerDateValue" |
| | | mode="datetime" |
| | | title="éæ©éªæ¶æ¶é´" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDatePicker = false" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { repairAcceptance, getRepairById } from "@/api/equipmentManagement/repair"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | defineOptions({ name: "repair-acceptance" }); |
| | | |
| | | const showToast = message => { |
| | | uni.showToast({ title: message, icon: "none" }); |
| | | }; |
| | | |
| | | const userStore = useUserStore(); |
| | | const loading = ref(false); |
| | | const showDatePicker = ref(false); |
| | | const pickerDateValue = ref(Date.now()); |
| | | const repairId = ref(""); |
| | | const detail = ref({}); |
| | | |
| | | const form = ref({ |
| | | id: "", |
| | | acceptanceName: "", |
| | | acceptanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | acceptanceRemark: "", |
| | | }); |
| | | |
| | | const formatDate = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | const formatDateTime = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss"); |
| | | }; |
| | | |
| | | const canAccept = item => { |
| | | const currentName = userStore.nickName || userStore.name || ""; |
| | | return currentName && item.acceptanceName === currentName; |
| | | }; |
| | | |
| | | const loadDetail = async id => { |
| | | try { |
| | | const cached = uni.getStorageSync("repairDetail"); |
| | | if (cached) { |
| | | detail.value = typeof cached === "string" ? JSON.parse(cached) : cached; |
| | | } |
| | | if (id && (!detail.value?.id || detail.value.id != id)) { |
| | | const { code, data } = await getRepairById(id); |
| | | if (code == 200) { |
| | | detail.value = data; |
| | | } |
| | | } |
| | | if (!canAccept(detail.value)) { |
| | | showToast("ä»
æå®éªæ¶äººå¯è¿è¡éªæ¶"); |
| | | setTimeout(() => uni.navigateBack(), 1500); |
| | | return; |
| | | } |
| | | form.value.id = detail.value.id; |
| | | form.value.acceptanceName = detail.value.acceptanceName || ""; |
| | | } catch (e) { |
| | | showToast("è·åæ¥ä¿®ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | }; |
| | | |
| | | const onDateConfirm = e => { |
| | | form.value.acceptanceTime = dayjs(e.value).format("YYYY-MM-DD HH:mm:ss"); |
| | | pickerDateValue.value = e.value; |
| | | showDatePicker.value = false; |
| | | }; |
| | | |
| | | const handleSubmit = async () => { |
| | | if (!form.value.acceptanceTime?.trim()) { |
| | | showToast("è¯·éæ©éªæ¶æ¶é´"); |
| | | return; |
| | | } |
| | | if (!form.value.acceptanceRemark?.trim()) { |
| | | showToast("请è¾å
¥éªæ¶å¤æ³¨"); |
| | | return; |
| | | } |
| | | try { |
| | | loading.value = true; |
| | | const { code } = await repairAcceptance({ |
| | | id: form.value.id, |
| | | acceptanceTime: form.value.acceptanceTime, |
| | | acceptanceRemark: form.value.acceptanceRemark, |
| | | }); |
| | | if (code == 200) { |
| | | showToast("éªæ¶æå"); |
| | | uni.removeStorageSync("repairDetail"); |
| | | setTimeout(() => uni.navigateBack(), 500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast("éªæ¶æäº¤å¤±è´¥"); |
| | | } |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.removeStorageSync("repairDetail"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onLoad(options => { |
| | | if (options.id) { |
| | | repairId.value = options.id; |
| | | } |
| | | }); |
| | | |
| | | onMounted(async () => { |
| | | if (!userStore.nickName) { |
| | | await userStore.getInfo().catch(() => {}); |
| | | } |
| | | if (repairId.value) { |
| | | loadDetail(repairId.value); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .repair-acceptance { |
| | | min-height: 100vh; |
| | | background-color: #f8f9fa; |
| | | padding-bottom: 160rpx; |
| | | } |
| | | |
| | | .section { |
| | | background-color: #ffffff; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333333; |
| | | padding: 16px 16px 12px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | padding: 14px 16px; |
| | | border-bottom: 1px solid #f8f8f8; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .info-item:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .info-label { |
| | | font-size: 14px; |
| | | color: #666666; |
| | | min-width: 80px; |
| | | flex-shrink: 0; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #333333; |
| | | flex: 1; |
| | | line-height: 22px; |
| | | text-align: right; |
| | | } |
| | | |
| | | .multi-line { |
| | | text-align: left; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .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: #666; |
| | | background: #f5f5f5; |
| | | border: 1px solid #ddd; |
| | | width: 45%; |
| | | height: 2.5rem; |
| | | border-radius: 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 500; |
| | | font-size: 1rem; |
| | | color: #fff; |
| | | background: linear-gradient(140deg, #00baff 0%, #006cfb 100%); |
| | | border: none; |
| | | width: 45%; |
| | | height: 2.5rem; |
| | | border-radius: 2.5rem; |
| | | } |
| | | </style> |
| | |
| | | placeholder="请è¾å
¥ç»´ä¿®äºº" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="éªæ¶äºº" |
| | | prop="acceptanceName" |
| | | border-bottom> |
| | | <u-input v-model="form.acceptanceName" |
| | | placeholder="请è¾å
¥éªæ¶äºº" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="维修项ç®" |
| | | prop="machineryCategory" |
| | | border-bottom> |
| | |
| | | repairTime: dayjs().format("YYYY-MM-DD"), // æ¥ä¿®æ¥æ |
| | | repairName: undefined, // æ¥ä¿®äºº |
| | | maintenanceName: undefined, // 维修人 |
| | | acceptanceName: undefined, // éªæ¶äºº |
| | | status: "0", // æ¥ä¿®ç¶æ |
| | | machineryCategory: undefined, // ç»´ä¿®é¡¹ç® |
| | | remark: undefined, // æ
éç°è±¡ |
| | | storageBlobDTOs: [], // å¾çéä»¶ |
| | |
| | | // æ¥ä¿®ç¶æé项 |
| | | const repairStatusOptions = ref([ |
| | | { name: "å¾
ç»´ä¿®", value: "0" }, |
| | | { name: "å®ç»", value: "1" }, |
| | | { name: "失败", value: "2" }, |
| | | { name: "宿", value: "1" }, |
| | | { name: "维修失败", value: "2" }, |
| | | { name: "å¾
éªæ¶", value: "3" }, |
| | | ]); |
| | | const repairStatusText = ref(""); |
| | | const repairStatusText = ref("å¾
ç»´ä¿®"); |
| | | |
| | | // æå¼æ¥ä¿®ç¶æéæ©å¨ |
| | | const openRepairStatusPicker = () => { |
| | |
| | | form.value.repairTime = dayjs(data.repairTime).format("YYYY-MM-DD"); |
| | | form.value.repairName = data.repairName; |
| | | form.value.maintenanceName = data.maintenanceName; |
| | | form.value.acceptanceName = data.acceptanceName; |
| | | form.value.status = String(data.status ?? "0"); |
| | | 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 || ""; |
| | | repairStatusOptions.value.find( |
| | | item => item.value == String(data.status) |
| | | )?.name || ""; |
| | | // 设置设å¤åç§°æ¾ç¤º |
| | | const device = deviceOptions.value.find( |
| | | item => item.id === data.deviceLedgerId |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="repair-detail"> |
| | | <PageHeader title="è®¾å¤æ¥ä¿®è¯¦æ
" |
| | | @back="goBack" /> |
| | | <view class="content-container"> |
| | | <!-- 1. æ¥ä¿®ç»è®° --> |
| | | <view class="section"> |
| | | <view class="section-head"> |
| | | <view class="section-bar"></view> |
| | | <text class="section-head-text">1. æ¥ä¿®ç»è®°</text> |
| | | </view> |
| | | <view class="desc-table"> |
| | | <view class="desc-row"> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">设å¤åç§°</text> |
| | | <text class="desc-value">{{ detail.deviceName || '-' }}</text> |
| | | </view> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">è§æ ¼åå·</text> |
| | | <text class="desc-value">{{ detail.deviceModel || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="desc-row"> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">æ¥ä¿®æ¥æ</text> |
| | | <text class="desc-value">{{ formatDate(detail.repairTime) || '-' }}</text> |
| | | </view> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">æ¥ä¿®äºº</text> |
| | | <text class="desc-value">{{ detail.repairName || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="desc-row"> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">éªæ¶äºº</text> |
| | | <text class="desc-value">{{ detail.acceptanceName || '-' }}</text> |
| | | </view> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">维修人</text> |
| | | <text class="desc-value">{{ detail.maintenanceName || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="desc-row full"> |
| | | <view class="desc-cell full"> |
| | | <text class="desc-label">æ
éç°è±¡</text> |
| | | <text class="desc-value">{{ detail.remark || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="desc-row full"> |
| | | <view class="desc-cell full status-cell"> |
| | | <text class="desc-label">å½åç¶æ</text> |
| | | <view class="desc-value"> |
| | | <u-tag :type="getStatusType(detail.status)" |
| | | size="mini">{{ getStatusLabel(detail.status) }}</u-tag> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="image-block"> |
| | | <text class="image-block-title">设å¤é®é¢å¾ç</text> |
| | | <view v-if="imageList.length" |
| | | class="image-content"> |
| | | <CommonUpload :model-value="imageList" |
| | | disabled /> |
| | | </view> |
| | | <view v-else |
| | | class="image-empty"> |
| | | <up-icon name="photo" |
| | | size="40" |
| | | color="#c0c4cc"></up-icon> |
| | | <text class="image-empty-text">ææ è®¾å¤é®é¢å¾ç</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 2. ç»´ä¿®å¤ç --> |
| | | <view class="section"> |
| | | <view class="section-head"> |
| | | <view class="section-bar"></view> |
| | | <text class="section-head-text">2. ç»´ä¿®å¤ç</text> |
| | | </view> |
| | | <view class="desc-table"> |
| | | <view class="desc-row"> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">维修人</text> |
| | | <text class="desc-value">{{ detail.maintenanceName || '-' }}</text> |
| | | </view> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">ç»´ä¿®æ¶é´</text> |
| | | <text class="desc-value">{{ formatDateTime(detail.maintenanceTime) || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="desc-row full"> |
| | | <view class="desc-cell full"> |
| | | <text class="desc-label">ç»´ä¿®ç»æ</text> |
| | | <text class="desc-value">{{ detail.maintenanceResult || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 3. éªæ¶ --> |
| | | <view class="section"> |
| | | <view class="section-head"> |
| | | <view class="section-bar"></view> |
| | | <text class="section-head-text">3. éªæ¶</text> |
| | | </view> |
| | | <view class="desc-table"> |
| | | <view class="desc-row"> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">éªæ¶äºº</text> |
| | | <text class="desc-value">{{ detail.acceptanceName || '-' }}</text> |
| | | </view> |
| | | <view class="desc-cell"> |
| | | <text class="desc-label">éªæ¶æ¶é´</text> |
| | | <text class="desc-value">{{ formatDateTime(detail.acceptanceTime) || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="desc-row full"> |
| | | <view class="desc-cell full"> |
| | | <text class="desc-label">éªæ¶å¤æ³¨</text> |
| | | <text class="desc-value">{{ detail.acceptanceRemark || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import CommonUpload from "@/components/CommonUpload.vue"; |
| | | import { getRepairById } from "@/api/equipmentManagement/repair"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | defineOptions({ name: "repair-detail" }); |
| | | |
| | | const showToast = message => { |
| | | uni.showToast({ title: message, icon: "none" }); |
| | | }; |
| | | |
| | | const repairId = ref(""); |
| | | const detail = ref({}); |
| | | |
| | | const STATUS_MAP = { |
| | | 0: { label: "å¾
ç»´ä¿®", type: "error" }, |
| | | 1: { label: "宿", type: "success" }, |
| | | 2: { label: "维修失败", type: "warning" }, |
| | | 3: { label: "å¾
éªæ¶", type: "primary" }, |
| | | }; |
| | | |
| | | const getStatusLabel = status => STATUS_MAP[status]?.label || "-"; |
| | | const getStatusType = status => STATUS_MAP[status]?.type || "info"; |
| | | |
| | | const imageList = computed(() => { |
| | | return detail.value.storageBlobVOs || detail.value.storageBlobDTOs || []; |
| | | }); |
| | | |
| | | const formatDate = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | const formatDateTime = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss"); |
| | | }; |
| | | |
| | | const loadDetail = async id => { |
| | | try { |
| | | const cached = uni.getStorageSync("repairDetail"); |
| | | if (cached) { |
| | | detail.value = typeof cached === "string" ? JSON.parse(cached) : cached; |
| | | } |
| | | if (id) { |
| | | const { code, data } = await getRepairById(id); |
| | | if (code == 200) { |
| | | detail.value = { ...detail.value, ...data }; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | showToast("è·å详æ
失败"); |
| | | } |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.removeStorageSync("repairDetail"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onLoad(options => { |
| | | if (options.id) { |
| | | repairId.value = options.id; |
| | | } |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | loadDetail(repairId.value); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .repair-detail { |
| | | min-height: 100vh; |
| | | background-color: #f0f2f5; |
| | | padding-bottom: 24px; |
| | | } |
| | | |
| | | .content-container { |
| | | padding: 12px; |
| | | } |
| | | |
| | | .section { |
| | | background-color: #ffffff; |
| | | border-radius: 4px; |
| | | margin-bottom: 12px; |
| | | overflow: hidden; |
| | | border: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .section-head { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #ebeef5; |
| | | background: #fff; |
| | | } |
| | | |
| | | .section-bar { |
| | | width: 4px; |
| | | height: 16px; |
| | | background: #409eff; |
| | | border-radius: 2px; |
| | | margin-right: 8px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .section-head-text { |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .desc-table { |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .desc-row { |
| | | display: flex; |
| | | border-bottom: 1px solid #ebeef5; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | &.full { |
| | | .desc-cell.full { |
| | | flex: 1; |
| | | width: 100%; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .desc-cell { |
| | | flex: 1; |
| | | display: flex; |
| | | min-width: 0; |
| | | border-right: 1px solid #ebeef5; |
| | | |
| | | &:last-child { |
| | | border-right: none; |
| | | } |
| | | |
| | | &.full { |
| | | border-right: none; |
| | | } |
| | | } |
| | | |
| | | .desc-label { |
| | | width: 88px; |
| | | flex-shrink: 0; |
| | | padding: 10px 12px; |
| | | font-size: 13px; |
| | | color: #606266; |
| | | background: #f5f7fa; |
| | | border-right: 1px solid #ebeef5; |
| | | line-height: 20px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .desc-value { |
| | | flex: 1; |
| | | padding: 10px 12px; |
| | | font-size: 13px; |
| | | color: #303133; |
| | | background: #ffffff; |
| | | line-height: 20px; |
| | | word-break: break-all; |
| | | min-width: 0; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .status-cell { |
| | | .desc-value { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | |
| | | .image-block { |
| | | padding: 12px 16px 16px; |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .image-block-title { |
| | | display: block; |
| | | font-size: 13px; |
| | | color: #606266; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .image-content { |
| | | padding: 0; |
| | | } |
| | | |
| | | .image-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | min-height: 120px; |
| | | background: #fafafa; |
| | | border: 1px dashed #dcdfe6; |
| | | border-radius: 4px; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .image-empty-text { |
| | | font-size: 13px; |
| | | color: #c0c4cc; |
| | | } |
| | | |
| | | @media (max-width: 400px) { |
| | | .desc-row:not(.full) { |
| | | flex-direction: column; |
| | | |
| | | .desc-cell { |
| | | border-right: none; |
| | | border-bottom: 1px solid #ebeef5; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <text class="item-id">设å¤åç§°ï¼{{ item.deviceName }}</text> |
| | | </view> |
| | | <view class="status-tag"> |
| | | <u-tag v-if="item.status === 1" |
| | | type="success">å®ç»</u-tag> |
| | | <u-tag v-if="item.status === 0" |
| | | type="error">å¾
ç»´ä¿®</u-tag> |
| | | <u-tag :type="getStatusType(item.status)" |
| | | size="mini">{{ getStatusLabel(item.status) }}</u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | |
| | | <text class="detail-value">{{ item.maintenanceName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">éªæ¶äºº</text> |
| | | <text class="detail-value">{{ item.acceptanceName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">维修项ç®</text> |
| | | <text class="detail-value">{{ item.machineryCategory || '-' }}</text> |
| | | </view> |
| | |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç»´ä¿®æ¥æ</text> |
| | | <text class="detail-value">{{ formatDate(item.maintenanceTime) || '-' }}</text> |
| | | <text class="detail-value">{{ formatDateTime(item.maintenanceTime) || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- æé®åºå --> |
| | | <view class="action-buttons"> |
| | | <u-button type="info" |
| | | size="small" |
| | | plain |
| | | class="action-btn" |
| | | @click="goDetail(item)"> |
| | | 详æ
|
| | | </u-button> |
| | | <u-button type="success" |
| | | size="small" |
| | | class="action-btn" |
| | | v-if="item.status === 3" |
| | | :disabled="!canAccept(item)" |
| | | @click="goAcceptance(item)"> |
| | | éªæ¶ |
| | | </u-button> |
| | | <u-button type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | const STATUS_MAP = { |
| | | 0: { label: "å¾
ç»´ä¿®", type: "error" }, |
| | | 1: { label: "宿", type: "success" }, |
| | | 2: { label: "维修失败", type: "warning" }, |
| | | 3: { label: "å¾
éªæ¶", type: "primary" }, |
| | | }; |
| | | |
| | | const getStatusLabel = status => STATUS_MAP[status]?.label || "-"; |
| | | const getStatusType = status => STATUS_MAP[status]?.type || "info"; |
| | | |
| | | const canAccept = item => { |
| | | if (item.status !== 3) return false; |
| | | const currentName = userStore.nickName || userStore.name || ""; |
| | | return currentName && item.acceptanceName === currentName; |
| | | }; |
| | | |
| | | // æç´¢å
³é®è¯ |
| | | const searchKeyword = ref(""); |
| | | |
| | |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | }; |
| | | |
| | | const formatDateTime = dateStr => { |
| | | if (!dateStr) return ""; |
| | | const date = new Date(dateStr); |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | const hours = String(date.getHours()).padStart(2, "0"); |
| | | const minutes = String(date.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(date.getSeconds()).padStart(2, "0"); |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | // 使ç¨uni.setStorageSyncåå¨id |
| | | uni.setStorageSync("repairId", id); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/repair/maintain", |
| | |
| | | }); |
| | | }; |
| | | |
| | | // ç¼è¾ - 跳转å°add页é¢ï¼éè¿idåºåæ°å¢è¿æ¯ç¼è¾ |
| | | // ç¼è¾ |
| | | const edit = id => { |
| | | if (!id) return; |
| | | // 使ç¨uni.setStorageSyncåå¨id |
| | | // uni.setStorageSync("repairId", id); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/repair/add?id=" + id, |
| | | }); |
| | | }; |
| | | |
| | | // 详æ
|
| | | const goDetail = item => { |
| | | uni.setStorageSync("repairDetail", JSON.stringify(item)); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/repair/detail?id=" + item.id, |
| | | }); |
| | | }; |
| | | |
| | | // éªæ¶ |
| | | const goAcceptance = item => { |
| | | if (!canAccept(item)) { |
| | | showToast("ä»
æå®éªæ¶äººå¯è¿è¡éªæ¶"); |
| | | return; |
| | | } |
| | | uni.setStorageSync("repairDetail", JSON.stringify(item)); |
| | | uni.navigateTo({ |
| | | url: "/pages/equipmentManagement/repair/acceptance?id=" + item.id, |
| | | }); |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | if (!userStore.nickName) { |
| | | userStore.getInfo().catch(() => {}); |
| | | } |
| | | getList(); |
| | | }); |
| | | |
| | |
| | | <style scoped lang="scss"> |
| | | @import "@/styles/sales-common.scss"; |
| | | |
| | | // 设å¤ç»´ä¿®ç¹ææ ·å¼ |
| | | .sales-account { |
| | | padding-bottom: 80px; // ä¸ºæµ®å¨æé®çåºç©ºé´ |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | .status-tag { |
| | |
| | | } |
| | | |
| | | .action-buttons { |
| | | gap: 8px; // ä¸å
Œ
±æ ·å¼ä¸ç 12px ä¸å |
| | | gap: 8px; |
| | | flex-wrap: wrap; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | <u-cell-group title="维修信æ¯" |
| | | inset> |
| | | <u-form-item prop="maintenanceName" |
| | | label="æ¥ä¿®äºº" |
| | | label="维修人" |
| | | required> |
| | | <u-input v-model="form.maintenanceName" |
| | | placeholder="请è¾å
¥æ¥ä¿®äºº" |
| | | placeholder="请è¾å
¥ç»´ä¿®äºº" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item prop="maintenanceResult" |
| | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | maintenanceName: [ |
| | | { required: true, trigger: "blur", message: "请è¾å
¥æ¥ä¿®äºº" }, |
| | | { required: true, trigger: "blur", message: "请è¾å
¥ç»´ä¿®äºº" }, |
| | | ], |
| | | maintenanceResult: [ |
| | | { required: true, trigger: "blur", message: "请è¾å
¥ç»´ä¿®ç»æ" }, |
| | |
| | | }; |
| | | const repairStatusOptions = ref([ |
| | | { name: "å¾
ç»´ä¿®", value: "0" }, |
| | | { name: "å®ç»", value: "1" }, |
| | | { name: "失败", value: "2" }, |
| | | { name: "宿", value: "1" }, |
| | | { name: "维修失败", value: "2" }, |
| | | { name: "å¾
éªæ¶", value: "3" }, |
| | | ]); |
| | | const repairStatusText = ref("å®ç»"); |
| | | const repairStatusText = ref("å¾
éªæ¶"); |
| | | // æå¼æ¥ä¿®ç¶æéæ©å¨ |
| | | const openRepairStatusPicker = () => { |
| | | uni.showActionSheet({ |
| | |
| | | maintenanceName: userStore.nickName || "", // é»è®¤ä½¿ç¨å½åç¨æ·æµç§° |
| | | maintenanceResult: undefined, // ç»´ä¿®ç»æ |
| | | maintenanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // ç»´ä¿®æ¥æï¼åªæ¾ç¤ºæ¥æï¼ |
| | | status: "1", |
| | | status: "3", |
| | | sparePartsIds: [], |
| | | }); |
| | | |
| | |
| | | maintenanceName: userStore.nickName || "", |
| | | maintenanceResult: undefined, |
| | | maintenanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | status: "1", |
| | | status: "3", |
| | | sparePartsIds: [], |
| | | }; |
| | | selectedSpareParts.value = []; |
| | |
| | | |
| | | // åå§åè¡¨åæ°æ® |
| | | const initForm = async () => { |
| | | form.value.status = "1"; |
| | | form.value.status = "3"; |
| | | // 设置æ¥ä¿®äººä¸ºå½åç¨æ·æµç§° |
| | | form.value.maintenanceName = userStore.nickName || ""; |
| | | // 设置å½åæ¥æï¼åªå
å«å¹´ææ¥ï¼ |
| | |
| | | <template> |
| | | <view class="attachment-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader :title="`æ¥çéä»¶ - ${taskInfo?.taskName || ''}`" @back="goBack" /> |
| | | |
| | | <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 v-if="attachmentList.length > 0" |
| | | class="attachment-list"> |
| | | <view v-for="(file, index) in attachmentList" |
| | | :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> |
| | | <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> |
| | | </view> |
| | | </view> |
| | | <view v-else class="attachment-empty"> |
| | | <u-icon name="folder-open" size="60" color="#ccc"></u-icon> |
| | | <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 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 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> |
| | | <video v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | @error="handleVideoError"></video> |
| | | </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'; |
| | | 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 taskInfo = ref(null); |
| | | |
| | | // éä»¶å表 |
| | | const attachmentList = ref([]); |
| | | // éä»¶å表 |
| | | const attachmentList = ref([]); |
| | | |
| | | // å½åæ¥çç±»å |
| | | const currentViewType = ref('before'); // 'before', 'after', 'issue' |
| | | // è§é¢é¢è§ç¸å
³ç¶æ |
| | | const showVideoDialog = ref(false); |
| | | const currentVideoFile = ref(null); |
| | | |
| | | // è§é¢é¢è§ç¸å
³ç¶æ |
| | | const showVideoDialog = ref(false); |
| | | const currentVideoFile = ref(null); |
| | | // æä»¶è®¿é®åºç¡å |
| | | const filePreviewBase = config.fileUrl; |
| | | |
| | | // æä»¶è®¿é®åºç¡å |
| | | 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' |
| | | }); |
| | | // 页é¢å è½½ |
| | | 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; |
| | | // å è½½éä»¶æ°æ® |
| | | const loadAttachments = () => { |
| | | const task = taskInfo.value; |
| | | if (!task) return; |
| | | |
| | | attachmentList.value = []; |
| | | 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); |
| | | // è·åéä»¶å表ï¼ä¼å
ä» commonFileListBeforeVO è·å |
| | | let rawList = []; |
| | | if (Array.isArray(task.commonFileListBeforeVO)) { |
| | | rawList = task.commonFileListBeforeVO; |
| | | } else if (Array.isArray(task.commonFileListBefore)) { |
| | | rawList = task.commonFileListBefore; |
| | | } else if (Array.isArray(task.commonFileList)) { |
| | | // é级ï¼ä»éç¨åè¡¨è¿æ»¤ type 为 10 ç |
| | | rawList = task.commonFileList.filter(f => f?.type === 10); |
| | | } |
| | | |
| | | 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, |
| | | const mapToViewFile = file => { |
| | | // ä¼å
ä½¿ç¨ previewURL æ url |
| | | const rawUrl = |
| | | file?.previewURL || |
| | | file?.url || |
| | | file?.downloadUrl || |
| | | file?.downloadURL || |
| | | ""; |
| | | const u = normalizeFileUrl(rawUrl); |
| | | |
| | | return { |
| | | ...file, |
| | | name: |
| | | file?.name || file?.originalFilename || file?.bucketFilename || "éä»¶", |
| | | bucketFilename: |
| | | file?.bucketFilename || file?.name || file?.originalFilename, |
| | | originalFilename: file?.originalFilename || file?.name, |
| | | url: u, |
| | | downloadUrl: u, |
| | | size: file?.size || file?.byteSize || 0, |
| | | }; |
| | | }; |
| | | |
| | | attachmentList.value = rawList.map(f => mapToViewFile(f)); |
| | | }; |
| | | |
| | | 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}`; |
| | | |
| | | // å°å端è¿åçæä»¶å°åè§èæå¯è®¿é®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}`; |
| | | // 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 normalized; |
| | | |
| | | return `${filePreviewBase}/${url.replace(/^\//, "")}`; |
| | | } catch (e) { |
| | | return rawUrl || ""; |
| | | } |
| | | }; |
| | | |
| | | return `${filePreviewBase}/${url.replace(/^\//, '')}`; |
| | | } catch (e) { |
| | | return rawUrl || ''; |
| | | } |
| | | }; |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // 夿æ¯å¦ä¸ºå¾çæä»¶ |
| | | const isImageFile = file => { |
| | | if (file.contentType && file.contentType.startsWith("image/")) { |
| | | return true; |
| | | } |
| | | if (file.type === "image") return true; |
| | | |
| | | // 忢æ¥çç±»å |
| | | const switchViewType = (type) => { |
| | | currentViewType.value = type; |
| | | }; |
| | | const name = file.bucketFilename || file.originalFilename || file.name || ""; |
| | | const ext = name.split(".").pop()?.toLowerCase(); |
| | | return ["jpg", "jpeg", "png", "gif", "webp"].includes(ext); |
| | | }; |
| | | |
| | | // æ ¹æ®typeè·å对åºåç±»çéä»¶ |
| | | const getAttachmentsByType = (typeValue) => { |
| | | return attachmentList.value.filter((file) => file.type === typeValue) || []; |
| | | }; |
| | | // é¢è§éä»¶ |
| | | const previewAttachment = file => { |
| | | if (isImageFile(file)) { |
| | | const imageUrls = attachmentList.value |
| | | .filter(f => isImageFile(f)) |
| | | .map(f => f.url || f.downloadUrl); |
| | | |
| | | // è·åå½åæ¥çç±»åçéä»¶ |
| | | const getCurrentViewAttachments = () => { |
| | | switch (currentViewType.value) { |
| | | case 'before': |
| | | return getAttachmentsByType(0); |
| | | case 'after': |
| | | return getAttachmentsByType(1); |
| | | case 'issue': |
| | | return getAttachmentsByType(2); |
| | | default: |
| | | return []; |
| | | } |
| | | }; |
| | | uni.previewImage({ |
| | | urls: imageUrls, |
| | | current: file.url || file.downloadUrl, |
| | | }); |
| | | } else { |
| | | showVideoPreview(file); |
| | | } |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºå¾çæä»¶ |
| | | const isImageFile = (file) => { |
| | | if (file.contentType && file.contentType.startsWith('image/')) { |
| | | return true; |
| | | } |
| | | if (file.type === 'image') return true; |
| | | // æ¾ç¤ºè§é¢é¢è§ |
| | | const showVideoPreview = file => { |
| | | currentVideoFile.value = file; |
| | | showVideoDialog.value = true; |
| | | }; |
| | | |
| | | const name = file.bucketFilename || file.originalFilename || file.name || ''; |
| | | const ext = name.split('.').pop()?.toLowerCase(); |
| | | return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext); |
| | | }; |
| | | // å
³éè§é¢é¢è§ |
| | | const closeVideoPreview = () => { |
| | | showVideoDialog.value = false; |
| | | currentVideoFile.value = null; |
| | | }; |
| | | |
| | | // é¢è§éä»¶ |
| | | 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, |
| | | // è§é¢ææ¾é误å¤ç |
| | | const handleVideoError = () => { |
| | | uni.showToast({ |
| | | title: "è§é¢ææ¾å¤±è´¥", |
| | | icon: "error", |
| | | }); |
| | | } 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'; |
| | | }; |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | 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-page { |
| | | min-height: 100vh; |
| | | background-color: #f5f5f5; |
| | | } |
| | | |
| | | .attachment-content { |
| | | padding: 15px; |
| | | } |
| | | .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); |
| | | } |
| | | /* æ ç¾é¡µæ ·å¼ */ |
| | | .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 { |
| | | 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; |
| | | } |
| | | .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-container { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | min-height: 400px; |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 15px; |
| | | } |
| | | .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 { |
| | | 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-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-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-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .attachment-video-preview { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | .attachment-video-preview { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .attachment-info { |
| | | padding: 10px; |
| | | } |
| | | .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-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-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | } |
| | | |
| | | /* ç©ºç¶ææ ·å¼ */ |
| | | .attachment-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 80px 20px; |
| | | 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; |
| | | } |
| | | .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-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-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-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; |
| | | } |
| | | .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%; |
| | | } |
| | | .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-modal-body { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .video-player { |
| | | width: 100%; |
| | | height: 400px; |
| | | border-radius: 8px; |
| | | } |
| | | .video-player { |
| | | width: 100%; |
| | | height: 400px; |
| | | border-radius: 8px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <u-popup |
| | | v-model="dialogVisitable" |
| | | mode="center" |
| | | :round="10" |
| | | :closeable="true" |
| | | @close="cancel" |
| | | > |
| | | <u-popup v-model="dialogVisitable" |
| | | mode="center" |
| | | :round="10" |
| | | :closeable="true" |
| | | @close="cancel"> |
| | | <view class="popup-content"> |
| | | <view class="popup-header"> |
| | | <text class="popup-title">å·¡æ£è®°å½ä¸ä¼ </text> |
| | | </view> |
| | | |
| | | <view class="upload-container"> |
| | | <!-- å¼å¸¸ç¶æéæ© --> |
| | | <view class="form-container"> |
| | | <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> |
| | | <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> |
| | | <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="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' }" |
| | | /> |
| | | <u-input v-model="exceptionDescription" |
| | | type="textarea" |
| | | :maxlength="500" |
| | | placeholder="请æè¿°å¼å¸¸æ
åµ..." |
| | | :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }" /> |
| | | </view> |
| | | |
| | | <!-- ä¸ä¼ åºåï¼ä»
å¨å¼å¸¸æ¶æ¾ç¤ºï¼ --> |
| | | <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 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> |
| | | </template> |
| | | |
| | | <!-- æ£å¸¸ç¶ææç¤º --> |
| | | <view class="form-container normal-tip" v-if="hasException === false"> |
| | | <u-icon name="info-circle" size="40" color="#52c41a"></u-icon> |
| | | <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> |
| | | |
| | | <view class="popup-footer"> |
| | | <u-button @click="cancel" :customStyle="{ marginRight: '10px' }">åæ¶</u-button> |
| | | <u-button type="primary" @click="submitForm">ä¿å</u-button> |
| | | <u-button @click="cancel" |
| | | :customStyle="{ marginRight: '10px' }">åæ¶</u-button> |
| | | <u-button type="primary" |
| | | @click="submitForm">ä¿å</u-button> |
| | | </view> |
| | | </view> |
| | | </u-popup> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed } from 'vue' |
| | | import { submitInspectionRecord } from '@/api/equipmentManagement/inspection.js' |
| | | import { getToken } from '@/utils/auth' |
| | | import config from '@/config' |
| | | import { ref, computed } from "vue"; |
| | | import { submitInspectionRecord } from "@/api/equipmentManagement/inspection.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import config from "@/config"; |
| | | |
| | | const emit = defineEmits(['closeDia']) |
| | | const emit = defineEmits(["closeDia"]); |
| | | |
| | | const dialogVisitable = ref(false) |
| | | const beforeModelValue = ref([]) |
| | | const afterModelValue = ref([]) |
| | | const issueModelValue = ref([]) |
| | | const infoData = ref(null) |
| | | const dialogVisitable = ref(false); |
| | | const beforeModelValue = ref([]); |
| | | const infoData = ref(null); |
| | | |
| | | // å¼å¸¸ç¶æï¼null=æªéæ©, false=æ£å¸¸, true=å¼å¸¸ |
| | | const hasException = ref(null) |
| | | // å¼å¸¸æè¿° |
| | | const exceptionDescription = ref('') |
| | | // å¼å¸¸ç¶æï¼null=æªéæ©, false=æ£å¸¸, true=å¼å¸¸ |
| | | const hasException = ref(null); |
| | | // å¼å¸¸æè¿° |
| | | const exceptionDescription = ref(""); |
| | | |
| | | // 计ç®ä¸ä¼ URL |
| | | const uploadFileUrl = computed(() => { |
| | | let baseUrl = ''; |
| | | |
| | | if (process.env.VUE_APP_BASE_API) { |
| | | baseUrl = process.env.VUE_APP_BASE_API; |
| | | } else { |
| | | baseUrl = config.baseUrl; |
| | | } |
| | | |
| | | return baseUrl + '/file/upload'; |
| | | }) |
| | | // 计ç®ä¸ä¼ URL |
| | | const uploadFileUrl = computed(() => { |
| | | let baseUrl = ""; |
| | | |
| | | const uploadSingleFile = async (fileItem, typeValue) => { |
| | | const token = getToken() |
| | | if (!token) throw new Error('ç¨æ·æªç»å½') |
| | | |
| | | // H5: u-upload å¯è½ç»åç Fileï¼fileItem.fileï¼ |
| | | const rawFile = fileItem?.file |
| | | if (rawFile) { |
| | | const formData = new FormData() |
| | | formData.append('file', rawFile, rawFile.name || 'image.jpg') |
| | | formData.append('type', String(typeValue)) |
| | | const res = await fetch(uploadFileUrl.value, { |
| | | method: 'POST', |
| | | headers: { Authorization: 'Bearer ' + token }, |
| | | body: formData |
| | | }) |
| | | const data = await res.json() |
| | | if (data?.code !== 200) throw new Error(data?.msg || 'ä¸ä¼ 失败') |
| | | return { |
| | | url: data?.data?.url, |
| | | name: rawFile.name || 'image.jpg', |
| | | status: 'success' |
| | | if (process.env.VUE_APP_BASE_API) { |
| | | baseUrl = process.env.VUE_APP_BASE_API; |
| | | } else { |
| | | baseUrl = config.baseUrl; |
| | | } |
| | | } |
| | | |
| | | // é H5 / å
¼å®¹ï¼èµ° uni.uploadFile |
| | | return await new Promise((resolve, reject) => { |
| | | uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath: fileItem.url, |
| | | name: 'file', |
| | | header: { |
| | | 'Authorization': `Bearer ${token}` |
| | | }, |
| | | formData: { |
| | | type: typeValue |
| | | }, |
| | | success: (res) => { |
| | | try { |
| | | const data = JSON.parse(res.data) |
| | | if (data.code === 200) { |
| | | resolve({ |
| | | url: data.data.url, |
| | | name: fileItem.name, |
| | | status: 'success' |
| | | }) |
| | | } else { |
| | | reject(new Error(data.msg || 'ä¸ä¼ 失败')) |
| | | return baseUrl + "/file/upload"; |
| | | }); |
| | | |
| | | const uploadSingleFile = async (fileItem, typeValue) => { |
| | | const token = getToken(); |
| | | if (!token) throw new Error("ç¨æ·æªç»å½"); |
| | | |
| | | // H5: u-upload å¯è½ç»åç Fileï¼fileItem.fileï¼ |
| | | const rawFile = fileItem?.file; |
| | | if (rawFile) { |
| | | const formData = new FormData(); |
| | | formData.append("file", rawFile, rawFile.name || "image.jpg"); |
| | | formData.append("type", String(typeValue)); |
| | | const res = await fetch(uploadFileUrl.value, { |
| | | method: "POST", |
| | | headers: { Authorization: "Bearer " + token }, |
| | | body: formData, |
| | | }); |
| | | const data = await res.json(); |
| | | if (data?.code !== 200) throw new Error(data?.msg || "ä¸ä¼ 失败"); |
| | | return { |
| | | url: data?.data?.url, |
| | | name: rawFile.name || "image.jpg", |
| | | status: "success", |
| | | }; |
| | | } |
| | | |
| | | // é H5 / å
¼å®¹ï¼èµ° uni.uploadFile |
| | | return await new Promise((resolve, reject) => { |
| | | uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath: fileItem.url, |
| | | name: "file", |
| | | header: { |
| | | Authorization: `Bearer ${token}`, |
| | | }, |
| | | formData: { |
| | | type: typeValue, |
| | | }, |
| | | success: res => { |
| | | try { |
| | | const data = JSON.parse(res.data); |
| | | if (data.code === 200) { |
| | | resolve({ |
| | | url: data.data.url, |
| | | name: fileItem.name, |
| | | status: "success", |
| | | }); |
| | | } else { |
| | | reject(new Error(data.msg || "ä¸ä¼ 失败")); |
| | | } |
| | | } catch (e) { |
| | | reject(e); |
| | | } |
| | | } catch (e) { |
| | | reject(e) |
| | | }, |
| | | fail: err => reject(err), |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | // æä»¶ä¸ä¼ å¤ç |
| | | const afterRead = event => { |
| | | const { file } = event; |
| | | |
| | | // ä»
ä¿çç产å(typeValue=10) |
| | | let typeValue = 10; |
| | | |
| | | const files = Array.isArray(file) ? file : [file]; |
| | | Promise.resolve() |
| | | .then(async () => { |
| | | for (const f of files) { |
| | | const uploaded = await uploadSingleFile(f, typeValue); |
| | | beforeModelValue.value.push(uploaded); |
| | | } |
| | | }, |
| | | fail: (err) => reject(err) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | // æä»¶ä¸ä¼ å¤ç |
| | | const afterRead = (event) => { |
| | | const { name, file } = event |
| | | |
| | | // æ ¹æ®ä¸ä¼ ç±»å设置ä¸åçtypeå¼ |
| | | let typeValue = 10 // é»è®¤å¼ |
| | | if (name === 'before') { |
| | | typeValue = 10 // ç产å |
| | | } else if (name === 'after') { |
| | | typeValue = 11 // çäº§ä¸ |
| | | } else if (name === 'issue') { |
| | | typeValue = 12 // ç产å |
| | | } |
| | | |
| | | const files = Array.isArray(file) ? file : [file] |
| | | Promise.resolve().then(async () => { |
| | | for (const f of files) { |
| | | const uploaded = await uploadSingleFile(f, typeValue) |
| | | if (name === 'before') { |
| | | beforeModelValue.value.push(uploaded) |
| | | } else if (name === 'after') { |
| | | afterModelValue.value.push(uploaded) |
| | | } else if (name === 'issue') { |
| | | issueModelValue.value.push(uploaded) |
| | | } |
| | | } |
| | | uni.showToast({ title: 'ä¸ä¼ æå', icon: 'success' }) |
| | | }).catch((err) => { |
| | | console.error('ä¸ä¼ 失败:', err) |
| | | uni.showToast({ title: 'ä¸ä¼ 失败', icon: 'error' }) |
| | | }) |
| | | } |
| | | |
| | | // å 餿件 |
| | | const deleteFile = (event) => { |
| | | const { name, index } = event |
| | | |
| | | if (name === 'before') { |
| | | beforeModelValue.value.splice(index, 1) |
| | | } else if (name === 'after') { |
| | | afterModelValue.value.splice(index, 1) |
| | | } else if (name === 'issue') { |
| | | issueModelValue.value.splice(index, 1) |
| | | } |
| | | } |
| | | |
| | | // 设置å¼å¸¸ç¶æ |
| | | const setExceptionStatus = (status) => { |
| | | hasException.value = status |
| | | } |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | | // æ£æ¥æ¯å¦éæ©äºå·¡æ£ç¶æ |
| | | if (hasException.value === null) { |
| | | uni.showToast({ |
| | | title: 'è¯·éæ©å·¡æ£ç¶æ', |
| | | icon: 'none' |
| | | uni.showToast({ title: "ä¸ä¼ æå", icon: "success" }); |
| | | }) |
| | | return |
| | | } |
| | | .catch(err => { |
| | | console.error("ä¸ä¼ 失败:", err); |
| | | uni.showToast({ title: "ä¸ä¼ 失败", icon: "error" }); |
| | | }); |
| | | }; |
| | | |
| | | // 妿æ¯å¼å¸¸ç¶æï¼æ£æ¥æ¯å¦æä¸ä¼ æä»¶ |
| | | if (hasException.value === true) { |
| | | const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length |
| | | if (totalFiles === 0) { |
| | | // å 餿件 |
| | | const deleteFile = event => { |
| | | const { index } = event; |
| | | beforeModelValue.value.splice(index, 1); |
| | | }; |
| | | |
| | | // 设置å¼å¸¸ç¶æ |
| | | const setExceptionStatus = status => { |
| | | hasException.value = status; |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | | // æ£æ¥æ¯å¦éæ©äºå·¡æ£ç¶æ |
| | | if (hasException.value === null) { |
| | | uni.showToast({ |
| | | title: '请ä¸ä¼ å¼å¸¸ç
§ç', |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | title: "è¯·éæ©å·¡æ£ç¶æ", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | // æ£æ¥æ¯å¦å¡«åäºå¼å¸¸æè¿° |
| | | if (!exceptionDescription.value.trim()) { |
| | | uni.showToast({ |
| | | title: '请填åå¼å¸¸æè¿°', |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | |
| | | // 妿æ¯å¼å¸¸ç¶æï¼æ£æ¥æ¯å¦æä¸ä¼ æä»¶ |
| | | if (hasException.value === true) { |
| | | if (beforeModelValue.value.length === 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 }))) |
| | | } |
| | | if (afterModelValue.value.length > 0) { |
| | | arr.push(...afterModelValue.value.map(item => ({ ...item, statusType: 1 }))) |
| | | } |
| | | if (issueModelValue.value.length > 0) { |
| | | arr.push(...issueModelValue.value.map(item => ({ ...item, statusType: 2 }))) |
| | | } |
| | | |
| | | // æäº¤æ°æ® |
| | | infoData.value.storageBlobDTO = arr |
| | | infoData.value.hasException = hasException.value |
| | | infoData.value.exceptionDescription = exceptionDescription.value |
| | | await submitInspectionRecord({ ...infoData.value }) |
| | | |
| | | uni.showToast({ |
| | | title: 'æäº¤æå', |
| | | icon: 'success' |
| | | }) |
| | | |
| | | cancel() |
| | | } catch (error) { |
| | | console.error('æäº¤å¤±è´¥:', error) |
| | | uni.showToast({ |
| | | title: 'æäº¤å¤±è´¥', |
| | | icon: 'error' |
| | | }) |
| | | } |
| | | } |
| | | let arr = []; |
| | | if (beforeModelValue.value.length > 0) { |
| | | arr.push( |
| | | ...beforeModelValue.value.map(item => ({ ...item, statusType: 0 })) |
| | | ); |
| | | } |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async (row) => { |
| | | infoData.value = row |
| | | dialogVisitable.value = true |
| | | |
| | | // æ¸
空ä¹åçæ°æ® |
| | | beforeModelValue.value = [] |
| | | afterModelValue.value = [] |
| | | issueModelValue.value = [] |
| | | hasException.value = null |
| | | exceptionDescription.value = '' |
| | | } |
| | | // æäº¤æ°æ® |
| | | infoData.value.storageBlobDTO = arr; |
| | | infoData.value.hasException = hasException.value; |
| | | infoData.value.exceptionDescription = exceptionDescription.value; |
| | | await submitInspectionRecord({ ...infoData.value }); |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const cancel = () => { |
| | | dialogVisitable.value = false |
| | | emit('closeDia') |
| | | } |
| | | uni.showToast({ |
| | | title: "æäº¤æå", |
| | | icon: "success", |
| | | }); |
| | | |
| | | defineExpose({ openDialog }) |
| | | cancel(); |
| | | } catch (error) { |
| | | console.error("æäº¤å¤±è´¥:", error); |
| | | uni.showToast({ |
| | | title: "æäº¤å¤±è´¥", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async row => { |
| | | infoData.value = row; |
| | | dialogVisitable.value = true; |
| | | |
| | | // æ¸
空ä¹åçæ°æ® |
| | | beforeModelValue.value = []; |
| | | hasException.value = null; |
| | | exceptionDescription.value = ""; |
| | | }; |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const cancel = () => { |
| | | dialogVisitable.value = false; |
| | | emit("closeDia"); |
| | | }; |
| | | |
| | | defineExpose({ openDialog }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .popup-content { |
| | | width: 90vw; |
| | | max-width: 400px; |
| | | background-color: #fff; |
| | | border-radius: 10px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .popup-header { |
| | | padding: 20px 20px 10px; |
| | | text-align: center; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .popup-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .upload-container { |
| | | padding: 20px; |
| | | max-height: 60vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .form-container { |
| | | margin-bottom: 20px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | .popup-content { |
| | | width: 90vw; |
| | | max-width: 400px; |
| | | background-color: #fff; |
| | | border-radius: 10px; |
| | | overflow: hidden; |
| | | } |
| | | } |
| | | |
| | | .title { |
| | | font-size: 14px; |
| | | color: #1890ff; |
| | | line-height: 20px; |
| | | font-weight: 600; |
| | | padding-left: 10px; |
| | | position: relative; |
| | | margin: 6px 0 10px; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 3px; |
| | | width: 4px; |
| | | height: 14px; |
| | | background-color: #1890ff; |
| | | .popup-header { |
| | | padding: 20px 20px 10px; |
| | | text-align: center; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | } |
| | | |
| | | .popup-footer { |
| | | display: flex; |
| | | justify-content: center; |
| | | padding: 15px 20px; |
| | | 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; |
| | | .popup-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | &:active { |
| | | opacity: 0.8; |
| | | |
| | | .upload-container { |
| | | padding: 20px; |
| | | max-height: 60vh; |
| | | overflow-y: auto; |
| | | } |
| | | } |
| | | |
| | | .option-text { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | .form-container { |
| | | margin-bottom: 20px; |
| | | |
| | | // æ£å¸¸ç¶ææç¤ºæ ·å¼ |
| | | .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; |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .title { |
| | | font-size: 14px; |
| | | color: #52c41a; |
| | | color: #1890ff; |
| | | line-height: 20px; |
| | | font-weight: 600; |
| | | padding-left: 10px; |
| | | position: relative; |
| | | margin: 6px 0 10px; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 3px; |
| | | width: 4px; |
| | | height: 14px; |
| | | background-color: #1890ff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .popup-footer { |
| | | display: flex; |
| | | justify-content: center; |
| | | padding: 15px 20px; |
| | | 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> |
| | |
| | | }; |
| | | |
| | | const getFileStatus = record => { |
| | | let _beforeProduction = |
| | | record.beforeProduction && record.beforeProduction.length; |
| | | let _afterProduction = |
| | | record.afterProduction && record.afterProduction.length; |
| | | let _productionIssues = |
| | | record.productionIssues && record.productionIssues.length; |
| | | if (_beforeProduction && _afterProduction && _productionIssues) { |
| | | return 2; |
| | | } else if (_beforeProduction || _afterProduction || _productionIssues) { |
| | | return 1; |
| | | // æ£æ¥æ¯å¦æå·¡æ£ç
§ç (commonFileListBeforeVO) |
| | | const hasFiles = |
| | | (record.commonFileListBeforeVO && |
| | | record.commonFileListBeforeVO.length > 0) || |
| | | (record.commonFileListAfterVO && record.commonFileListAfterVO.length > 0) || |
| | | (record.commonFileListVO && record.commonFileListVO.length > 0); |
| | | |
| | | if (hasFiles) { |
| | | return 2; // 已宿 |
| | | } else if ( |
| | | record.inspectionResult !== undefined && |
| | | record.inspectionResult !== null |
| | | ) { |
| | | return 1; // å·¡æ£ä¸ (å·²æç»æä½æ²¡ç
§çï¼æè
æ ¹æ®ä¸å¡é»è¾å®ä¹) |
| | | } else { |
| | | return 0; |
| | | return 0; // æªå·¡æ£ |
| | | } |
| | | }; |
| | | |
| | |
| | | |
| | | // æ¥çéä»¶ - 跳转å°éä»¶é¡µé¢ |
| | | const viewAttachments = async task => { |
| | | const taskData = encodeURIComponent(JSON.stringify(task)); |
| | | // ä»
ä¼ éå¿
è¦çä»»å¡ä¿¡æ¯å commonFileListBeforeVO éä»¶å表 |
| | | const taskInfoToPass = { |
| | | taskName: task.taskName, |
| | | commonFileListBeforeVO: task.commonFileListBeforeVO || [], |
| | | }; |
| | | const taskData = encodeURIComponent(JSON.stringify(taskInfoToPass)); |
| | | uni.navigateTo({ |
| | | url: `/pages/inspectionUpload/attachment?taskInfo=${taskData}`, |
| | | }); |
| | |
| | | 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="section-title">å·¡æ£ç
§ç/è§é¢</view> |
| | | <!-- å½ååç±»çä¸ä¼ åºå --> |
| | | <view class="upload-area"> |
| | | <view class="upload-buttons"> |
| | | <u-button type="primary" |
| | | @click="chooseMedia('image')" |
| | | :loading="uploading" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit" |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }"> |
| | | <u-icon name="camera" |
| | | size="18" |
| | |
| | | <u-button type="success" |
| | | @click="chooseMedia('video')" |
| | | :loading="uploading" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit" |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit" |
| | | :customStyle="{ flex: 1 }"> |
| | | <uni-icons type="videocam" |
| | | size="18" |
| | |
| | | activeColor="#409eff"></u-line-progress> |
| | | </view> |
| | | <!-- å½ååç±»çæä»¶å表 --> |
| | | <view v-if="getCurrentFiles().length > 0" |
| | | <view v-if="beforeModelValue.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in getCurrentFiles()" |
| | | <view v-for="(file, index) in beforeModelValue" |
| | | :key="index" |
| | | class="file-item"> |
| | | <view class="file-preview-container"> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="getCurrentFiles().length === 0" |
| | | <view v-if="beforeModelValue.length === 0" |
| | | class="empty-state"> |
| | | <text>è¯·éæ©è¦ä¸ä¼ ç{{ getUploadTypeText() }}å¾çæè§é¢</text> |
| | | <text>è¯·éæ©è¦ä¸ä¼ çå¾çæè§é¢</text> |
| | | </view> |
| | | </view> |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <view class="upload-summary"> |
| | | <text class="summary-text"> |
| | | ç产å: {{ beforeModelValue.length }}个æä»¶ | |
| | | ç产ä¸: {{ afterModelValue.length }}个æä»¶ | |
| | | ç产å: {{ issueModelValue.length }}个æä»¶ |
| | | å·²ä¸ä¼ : {{ beforeModelValue.length }}个æä»¶ |
| | | </text> |
| | | </view> |
| | | </view> |
| | |
| | | const uploadProgress = ref(0); |
| | | |
| | | // ä¸ä¸ªåç±»çä¸ä¼ ç¶æ |
| | | const beforeModelValue = ref([]); // ç产å |
| | | const afterModelValue = ref([]); // çäº§ä¸ |
| | | const issueModelValue = ref([]); // ç产å |
| | | |
| | | // å½åæ¿æ´»çä¸ä¼ ç±»å |
| | | const currentUploadType = ref("before"); // 'before', 'after', 'issue' |
| | | const beforeModelValue = ref([]); // å·¡æ£ç
§ç |
| | | |
| | | // å¼å¸¸ç¶æ |
| | | const hasException = ref(null); // null: æªéæ©, true: åå¨å¼å¸¸, false: æ£å¸¸ |
| | |
| | | }); |
| | | }; |
| | | |
| | | // æ ¹æ®ç¨æ·è¦æ±æ å°ï¼AfterDTO(ç产å), DTO(ç产ä¸), BeforeDTO(ç产å) |
| | | // æ ¹æ®ç¨æ·è¦æ±æ å°ï¼ä»
ä¿çç产å |
| | | if ( |
| | | info.commonFileListAfterVO && |
| | | Array.isArray(info.commonFileListAfterVO) |
| | | ) { |
| | | beforeModelValue.value = mapFiles(info.commonFileListAfterVO); |
| | | } |
| | | console.log(beforeModelValue.value, "beforeModelValue"); |
| | | |
| | | if (info.commonFileListVO && Array.isArray(info.commonFileListVO)) { |
| | | afterModelValue.value = mapFiles(info.commonFileListVO); |
| | | } |
| | | if ( |
| | | } else if ( |
| | | info.commonFileListVO && |
| | | Array.isArray(info.commonFileListVO) |
| | | ) { |
| | | beforeModelValue.value = mapFiles(info.commonFileListVO); |
| | | } else if ( |
| | | info.commonFileListBeforeVO && |
| | | Array.isArray(info.commonFileListBeforeVO) |
| | | ) { |
| | | issueModelValue.value = mapFiles(info.commonFileListBeforeVO); |
| | | beforeModelValue.value = mapFiles(info.commonFileListBeforeVO); |
| | | } |
| | | |
| | | console.log(beforeModelValue.value, "beforeModelValue"); |
| | | |
| | | // 妿æå¼å¸¸æè¿°ï¼ä¹æ¢å¤ |
| | | if (info.abnormalDescription) { |
| | |
| | | } |
| | | |
| | | // èªå¨å
åºï¼å¦æåå¨å·²ä¸ä¼ æä»¶ï¼åå¿
ç¶æ¯å¼å¸¸ç¶æï¼ç¡®ä¿ UI æ£å¸¸æ¾ç¤º |
| | | if ( |
| | | !hasException.value && |
| | | (beforeModelValue.value.length > 0 || |
| | | afterModelValue.value.length > 0 || |
| | | issueModelValue.value.length > 0) |
| | | ) { |
| | | if (!hasException.value && beforeModelValue.value.length > 0) { |
| | | hasException.value = true; |
| | | } |
| | | } catch (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 ""; |
| | | } |
| | | }; |
| | | |
| | | // 设置å¼å¸¸ç¶æ |
| | |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, // 0-å¼å¸¸ï¼1-æ£å¸¸ |
| | | commonFileListAfterDTO: beforeModelValue.value, |
| | | commonFileListDTO: afterModelValue.value, |
| | | commonFileListBeforeDTO: issueModelValue.value, |
| | | uploadedFiles: { |
| | | before: beforeModelValue.value, |
| | | after: afterModelValue.value, |
| | | issue: issueModelValue.value, |
| | | }, |
| | | }; |
| | | |
| | |
| | | |
| | | // 妿æ¯å¼å¸¸ç¶æï¼æ£æ¥æ¯å¦æä¸ä¼ æä»¶åæè¿° |
| | | if (hasException.value === true) { |
| | | const totalFiles = |
| | | beforeModelValue.value.length + |
| | | afterModelValue.value.length + |
| | | issueModelValue.value.length; |
| | | if (totalFiles === 0) { |
| | | if (beforeModelValue.value.length === 0) { |
| | | uni.showToast({ |
| | | title: "请ä¸ä¼ å¼å¸¸ç
§ç", |
| | | icon: "none", |
| | |
| | | }); |
| | | |
| | | // æç
§é»è¾åå¹¶ææåç±»çæä»¶ç¨äºæåID |
| | | const allFiles = [ |
| | | ...beforeModelValue.value, |
| | | ...afterModelValue.value, |
| | | ...issueModelValue.value, |
| | | ]; |
| | | const allFiles = [...beforeModelValue.value]; |
| | | |
| | | // ä¼ ç»å端çä¸´æ¶æä»¶IDå表 |
| | | let tempFileIds = []; |
| | |
| | | const submitData = { |
| | | ...taskInfo.value, |
| | | commonFileListAfterDTO: beforeModelValue.value, // ç产å |
| | | commonFileListDTO: afterModelValue.value, // çäº§ä¸ |
| | | commonFileListBeforeDTO: issueModelValue.value, // ç产å |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, // 0-å¼å¸¸ï¼1-æ£å¸¸ |
| | | abnormalDescription: abnormalDescription.value, |
| | |
| | | |
| | | // æç
§/æè§é¢ |
| | | const chooseMedia = type => { |
| | | if (getCurrentFiles().length >= uploadConfig.limit) { |
| | | if (beforeModelValue.value.length >= uploadConfig.limit) { |
| | | uni.showToast({ |
| | | title: `æå¤åªè½éæ©${uploadConfig.limit}个æä»¶`, |
| | | icon: "none", |
| | |
| | | return; |
| | | } |
| | | |
| | | const remaining = uploadConfig.limit - getCurrentFiles().length; |
| | | const remaining = uploadConfig.limit - beforeModelValue.value.length; |
| | | |
| | | // ä¼å
ä½¿ç¨ chooseMedia |
| | | if (typeof uni.chooseMedia === "function") { |
| | |
| | | Authorization: `Bearer ${token}`, |
| | | }, |
| | | formData: { |
| | | type: getTabType(), |
| | | type: 10, |
| | | }, |
| | | success: res => { |
| | | try { |
| | |
| | | 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); |
| | | } |
| | | // ä»
æ·»å å° beforeModelValue |
| | | beforeModelValue.value.push(uploadedFile); |
| | | |
| | | uni.showToast({ title: "ä¸ä¼ æå", icon: "success" }); |
| | | } else { |
| | |
| | | }); |
| | | }; |
| | | |
| | | // è·å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); |
| | | beforeModelValue.value.splice(index, 1); |
| | | }; |
| | | </script> |
| | | |
| | |
| | | placeholder="请è¾å
¥åä½" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="æ°é" |
| | | <up-form-item label="æ»æ°é" |
| | | prop="quantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ°é" |
| | | placeholder="请è¾å
¥æ»æ°é" |
| | | :disabled="processQuantityDisabled" /> |
| | | </up-form-item> |
| | | <up-form-item label="åæ ¼æ°é" |
| | | prop="qualifiedQuantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.qualifiedQuantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥åæ ¼æ°é" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="ä¸åæ ¼æ°é" |
| | | prop="unqualifiedQuantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.unqualifiedQuantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥ä¸åæ ¼æ°é" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµåä½" |
| | | prop="checkCompany" |
| | |
| | | <up-input v-model="form.checkCompany" |
| | | placeholder="请è¾å
¥æ£æµåä½" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµç»æ" |
| | | prop="checkResult" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.checkResult" |
| | | placeholder="è¯·éæ©æ£æµç»æ" |
| | | readonly |
| | | @click="showResultSheet" /> |
| | | <template #right> |
| | | <up-icon @click="showResultSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="æ£éªå" |
| | | prop="checkName" |
| | |
| | | @select="selectModel" |
| | | @close="showModelSheet = false" |
| | | title="éæ©è§æ ¼åå·" /> |
| | | <!-- æ£æµç»æéæ© --> |
| | | <up-action-sheet :show="showResultSheet" |
| | | :actions="resultSheetOptions" |
| | | @select="selectResult" |
| | | @close="showResultSheet = false" |
| | | title="éæ©æ£æµç»æ" /> |
| | | <!-- æ£éªåéæ© --> |
| | | <up-action-sheet :show="showInspectorSheet" |
| | | :actions="userSheetOptions" |
| | |
| | | const showProductTree = ref(false); |
| | | // è§æ ¼åå·éæ© |
| | | const showModelSheet = ref(false); |
| | | // æ£æµç»æéæ© |
| | | const showResultSheet = ref(false); |
| | | // æ£éªåéæ© |
| | | const showInspectorSheet = ref(false); |
| | | // ææ éæ© |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }); |
| | |
| | | const modelOptions = ref([]); |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | // æ£æµç»æé项 |
| | | const resultOptions = ref([ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]); |
| | | // ææ é项 |
| | | const testStandardOptions = ref([]); |
| | | // å½å产åID |
| | |
| | | return modelOptions.value.map(item => ({ |
| | | name: item.model, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | |
| | | const resultSheetOptions = computed(() => { |
| | | return resultOptions.value.map(item => ({ |
| | | name: item.label, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | |
| | | ], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [ |
| | | { required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // æ¯å¦ä¸ºç¼è¾æ¨¡å¼ |
| | |
| | | modelOptions.value.find(item => item.id == value)?.model || ""; |
| | | form.value.unit = |
| | | modelOptions.value.find(item => item.id == value)?.unit || ""; |
| | | }; |
| | | |
| | | // éæ©æ£æµç»æ |
| | | const selectResult = e => { |
| | | form.value.checkResult = e.value; |
| | | showResultSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | |
| | | // return; |
| | | // } |
| | | if (!form.value.quantity) { |
| | | showToast("请è¾å
¥æ°é"); |
| | | showToast("请è¾å
¥æ»æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.qualifiedQuantity && form.value.qualifiedQuantity !== 0) { |
| | | showToast("请è¾å
¥åæ ¼æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.unqualifiedQuantity && form.value.unqualifiedQuantity !== 0) { |
| | | showToast("请è¾å
¥ä¸åæ ¼æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.productId) { |
| | |
| | | showToast("è¯·éæ©ææ "); |
| | | return; |
| | | } |
| | | if (!form.value.checkResult) { |
| | | showToast("è¯·éæ©æ£æµç»æ"); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | |
| | | form.value.inspectType = 2; |
| | |
| | | |
| | | const data = { ...form.value, qualityInspectParams: tableData.value }; |
| | | data.quantity = Number(data.quantity); |
| | | data.qualifiedQuantity = Number(data.qualifiedQuantity); |
| | | data.unqualifiedQuantity = Number(data.unqualifiedQuantity); |
| | | if (isEdit.value) { |
| | | const res = await qualityInspectUpdate(data); |
| | | showToast("ä¿åæå"); |
| | |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | unit: "kg", |
| | | quantity: 1000, |
| | | checkCompany: "ç¬¬ä¸æ¹æ£æµæºæ", |
| | | checkResult: "åæ ¼", |
| | | qualifiedQuantity: 0, |
| | | unqualifiedQuantity: 0, |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | </view> |
| | | <text class="header-title">{{ detailData.productName || '-' }}</text> |
| | | <view class="status-tags"> |
| | | <u-tag v-if="detailData.checkResult" |
| | | :type="getTagType(detailData.checkResult)" |
| | | <u-tag v-if="detailData.passRate != null && detailData.passRate !== ''" |
| | | type="primary" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.checkResult || '-' }} |
| | | åæ ¼ç {{ formatPassRate(detailData.passRate) }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(detailData.inspectState)" |
| | | size="small" |
| | |
| | | <text class="detail-value">{{ detailData.unit || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ detailData.quantity || 0 }}</text> |
| | | <text class="detail-label">æ»æ°é</text> |
| | | <text class="detail-value">{{ detailData.quantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åæ ¼æ°é</text> |
| | | <text class="detail-value">{{ detailData.qualifiedQuantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¸åæ ¼æ°é</text> |
| | | <text class="detail-value">{{ detailData.unqualifiedQuantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åæ ¼ç</text> |
| | | <text class="detail-value">{{ formatPassRate(detailData.passRate) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = result => { |
| | | switch (result) { |
| | | case "åæ ¼": |
| | | return "success"; |
| | | case "ä¸åæ ¼": |
| | | return "error"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | const formatPassRate = rate => { |
| | | if (rate === null || rate === undefined || rate === "") return "-"; |
| | | const num = Number(rate); |
| | | if (isNaN(num)) return rate; |
| | | if (num <= 1) return `${(num * 100).toFixed(2)}%`; |
| | | return `${num}%`; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | |
| | | </view> |
| | | </view> |
| | | <view class="status-tags"> |
| | | <u-tag v-if="item.checkResult" |
| | | :type="getTagType(item.checkResult)" |
| | | <u-tag v-if="item.passRate != null && item.passRate !== ''" |
| | | type="primary" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.checkResult }} |
| | | åæ ¼ç {{ formatPassRate(item.passRate) }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(item.inspectState)" |
| | | size="mini" |
| | |
| | | return inspectState ? "checkmark-circle" : "time"; |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = checkResult => { |
| | | if (checkResult === "åæ ¼") return "success"; |
| | | if (checkResult === "ä¸åæ ¼") return "error"; |
| | | return "default"; |
| | | // æ ¼å¼ååæ ¼ç |
| | | const formatPassRate = rate => { |
| | | if (rate === null || rate === undefined || rate === "") return "-"; |
| | | const num = Number(rate); |
| | | if (isNaN(num)) return rate; |
| | | if (num <= 1) return `${(num * 100).toFixed(2)}%`; |
| | | return `${num}%`; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | |
| | | pendingCount.value = inspectionList.value.filter( |
| | | item => !item.inspectState |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter( |
| | | item => item.checkResult === "åæ ¼" |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter(item => { |
| | | const rate = Number(item.passRate); |
| | | if (isNaN(rate)) return false; |
| | | return rate <= 1 ? rate >= 1 : rate >= 100; |
| | | }).length; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | |
| | | placeholder="请è¾å
¥åä½" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="æ°é" |
| | | <up-form-item label="æ»æ°é" |
| | | prop="quantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ°é" |
| | | placeholder="请è¾å
¥æ»æ°é" |
| | | :disabled="supplierQuantityDisabled" /> |
| | | </up-form-item> |
| | | <up-form-item label="åæ ¼æ°é" |
| | | prop="qualifiedQuantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.qualifiedQuantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥åæ ¼æ°é" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="ä¸åæ ¼æ°é" |
| | | prop="unqualifiedQuantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.unqualifiedQuantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥ä¸åæ ¼æ°é" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµåä½" |
| | | prop="checkCompany" |
| | |
| | | <up-input v-model="form.checkCompany" |
| | | placeholder="请è¾å
¥æ£æµåä½" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµç»æ" |
| | | prop="checkResult" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.checkResult" |
| | | placeholder="è¯·éæ©æ£æµç»æ" |
| | | readonly |
| | | @click="showResultSheet" /> |
| | | <template #right> |
| | | <up-icon @click="showResultSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="æ£éªå" |
| | | prop="checkName" |
| | |
| | | @select="selectModel" |
| | | @close="showModelSheet = false" |
| | | title="éæ©è§æ ¼åå·" /> |
| | | <!-- æ£æµç»æéæ© --> |
| | | <up-action-sheet :show="showResultSheet" |
| | | :actions="resultSheetOptions" |
| | | @select="selectResult" |
| | | @close="showResultSheet = false" |
| | | title="éæ©æ£æµç»æ" /> |
| | | <!-- æ£éªåéæ© --> |
| | | <up-action-sheet :show="showInspectorSheet" |
| | | :actions="userSheetOptions" |
| | |
| | | const showProductTree = ref(false); |
| | | // è§æ ¼åå·éæ© |
| | | const showModelSheet = ref(false); |
| | | // æ£æµç»æéæ© |
| | | const showResultSheet = ref(false); |
| | | // æ£éªåéæ© |
| | | const showInspectorSheet = ref(false); |
| | | // ææ éæ© |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }); |
| | |
| | | const modelOptions = ref([]); |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | // æ£æµç»æé项 |
| | | const resultOptions = ref([ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]); |
| | | // ææ é项 |
| | | const testStandardOptions = ref([]); |
| | | // å½å产åID |
| | |
| | | return modelOptions.value.map(item => ({ |
| | | name: item.model, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | |
| | | const resultSheetOptions = computed(() => { |
| | | return resultOptions.value.map(item => ({ |
| | | name: item.label, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | |
| | | ], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [ |
| | | { required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // æ¯å¦ä¸ºç¼è¾æ¨¡å¼ |
| | |
| | | modelOptions.value.find(item => item.id == value)?.model || ""; |
| | | form.value.unit = |
| | | modelOptions.value.find(item => item.id == value)?.unit || ""; |
| | | }; |
| | | |
| | | // éæ©æ£æµç»æ |
| | | const selectResult = e => { |
| | | form.value.checkResult = e.value; |
| | | showResultSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | |
| | | return; |
| | | } |
| | | if (!form.value.quantity) { |
| | | showToast("请è¾å
¥æ°é"); |
| | | showToast("请è¾å
¥æ»æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.qualifiedQuantity && form.value.qualifiedQuantity !== 0) { |
| | | showToast("请è¾å
¥åæ ¼æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.unqualifiedQuantity && form.value.unqualifiedQuantity !== 0) { |
| | | showToast("请è¾å
¥ä¸åæ ¼æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.productId) { |
| | |
| | | showToast("è¯·éæ©ææ "); |
| | | return; |
| | | } |
| | | if (!form.value.checkResult) { |
| | | showToast("è¯·éæ©æ£æµç»æ"); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | |
| | | form.value.inspectType = 0; |
| | |
| | | |
| | | const data = { ...form.value, qualityInspectParams: tableData.value }; |
| | | data.quantity = Number(data.quantity); |
| | | data.qualifiedQuantity = Number(data.qualifiedQuantity); |
| | | data.unqualifiedQuantity = Number(data.unqualifiedQuantity); |
| | | if (isEdit.value) { |
| | | const res = await qualityInspectUpdate(data); |
| | | showToast("ä¿åæå"); |
| | |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | unit: "kg", |
| | | quantity: 1000, |
| | | checkCompany: "ç¬¬ä¸æ¹æ£æµæºæ", |
| | | checkResult: "åæ ¼", |
| | | qualifiedQuantity: 0, |
| | | unqualifiedQuantity: 0, |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | </view> |
| | | <text class="header-title">{{ detailData.productName || '-' }}</text> |
| | | <view class="status-tags"> |
| | | <u-tag v-if="detailData.checkResult" |
| | | :type="getTagType(detailData.checkResult)" |
| | | <u-tag v-if="detailData.passRate != null && detailData.passRate !== ''" |
| | | type="primary" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.checkResult || '-' }} |
| | | åæ ¼ç {{ formatPassRate(detailData.passRate) }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(detailData.inspectState)" |
| | | size="small" |
| | |
| | | <text class="detail-value">{{ detailData.unit || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ detailData.quantity || 0 }}</text> |
| | | <text class="detail-label">æ»æ°é</text> |
| | | <text class="detail-value">{{ detailData.quantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åæ ¼æ°é</text> |
| | | <text class="detail-value">{{ detailData.qualifiedQuantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¸åæ ¼æ°é</text> |
| | | <text class="detail-value">{{ detailData.unqualifiedQuantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åæ ¼ç</text> |
| | | <text class="detail-value">{{ formatPassRate(detailData.passRate) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | |
| | | return dayjs(date).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = result => { |
| | | switch (result) { |
| | | case "åæ ¼": |
| | | return "success"; |
| | | case "ä¸åæ ¼": |
| | | return "error"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | // æ ¼å¼ååæ ¼ç |
| | | const formatPassRate = rate => { |
| | | if (rate === null || rate === undefined || rate === "") return "-"; |
| | | const num = Number(rate); |
| | | if (isNaN(num)) return rate; |
| | | if (num <= 1) return `${(num * 100).toFixed(2)}%`; |
| | | return `${num}%`; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | |
| | | </view> |
| | | </view> |
| | | <view class="status-tags"> |
| | | <u-tag v-if="item.checkResult" |
| | | :type="getTagType(item.checkResult)" |
| | | <u-tag v-if="item.passRate != null && item.passRate !== ''" |
| | | type="primary" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.checkResult }} |
| | | åæ ¼ç {{ formatPassRate(item.passRate) }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(item.inspectState)" |
| | | size="mini" |
| | |
| | | return inspectState ? "checkmark-circle" : "time"; |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = checkResult => { |
| | | if (checkResult === "åæ ¼") return "success"; |
| | | if (checkResult === "ä¸åæ ¼") return "error"; |
| | | return "default"; |
| | | // æ ¼å¼ååæ ¼ç |
| | | const formatPassRate = rate => { |
| | | if (rate === null || rate === undefined || rate === "") return "-"; |
| | | const num = Number(rate); |
| | | if (isNaN(num)) return rate; |
| | | if (num <= 1) return `${(num * 100).toFixed(2)}%`; |
| | | return `${num}%`; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | |
| | | pendingCount.value = inspectionList.value.filter( |
| | | item => !item.inspectState |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter( |
| | | item => item.checkResult === "åæ ¼" |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter(item => { |
| | | const rate = Number(item.passRate); |
| | | if (isNaN(rate)) return false; |
| | | return rate <= 1 ? rate >= 1 : rate >= 100; |
| | | }).length; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | |
| | | placeholder="请è¾å
¥åä½" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="æ°é" |
| | | <up-form-item label="æ»æ°é" |
| | | prop="quantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ°é" |
| | | placeholder="请è¾å
¥æ»æ°é" |
| | | :disabled="processQuantityDisabled" /> |
| | | </up-form-item> |
| | | <up-form-item label="åæ ¼æ°é" |
| | | prop="qualifiedQuantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.qualifiedQuantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥åæ ¼æ°é" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="ä¸åæ ¼æ°é" |
| | | prop="unqualifiedQuantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.unqualifiedQuantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥ä¸åæ ¼æ°é" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµåä½" |
| | | prop="checkCompany" |
| | |
| | | <up-input v-model="form.checkCompany" |
| | | placeholder="请è¾å
¥æ£æµåä½" |
| | | clearable /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµç»æ" |
| | | prop="checkResult" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.checkResult" |
| | | placeholder="è¯·éæ©æ£æµç»æ" |
| | | readonly |
| | | @click="showResultSheet" /> |
| | | <template #right> |
| | | <up-icon @click="showResultSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="æ£éªå" |
| | | prop="checkName" |
| | |
| | | @select="selectModel" |
| | | @close="showModelSheet = false" |
| | | title="éæ©è§æ ¼åå·" /> |
| | | <!-- æ£æµç»æéæ© --> |
| | | <up-action-sheet :show="showResultSheet" |
| | | :actions="resultSheetOptions" |
| | | @select="selectResult" |
| | | @close="showResultSheet = false" |
| | | title="éæ©æ£æµç»æ" /> |
| | | <!-- æ£éªåéæ© --> |
| | | <up-action-sheet :show="showInspectorSheet" |
| | | :actions="userSheetOptions" |
| | |
| | | const showProductTree = ref(false); |
| | | // è§æ ¼åå·éæ© |
| | | const showModelSheet = ref(false); |
| | | // æ£æµç»æéæ© |
| | | const showResultSheet = ref(false); |
| | | // æ£éªåéæ© |
| | | const showInspectorSheet = ref(false); |
| | | // ææ éæ© |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }); |
| | |
| | | const modelOptions = ref([]); |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | // æ£æµç»æé项 |
| | | const resultOptions = ref([ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]); |
| | | // ææ é项 |
| | | const testStandardOptions = ref([]); |
| | | // å½å产åID |
| | |
| | | return modelOptions.value.map(item => ({ |
| | | name: item.model, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | |
| | | const resultSheetOptions = computed(() => { |
| | | return resultOptions.value.map(item => ({ |
| | | name: item.label, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | |
| | | ], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [ |
| | | { required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // æ¯å¦ä¸ºç¼è¾æ¨¡å¼ |
| | |
| | | modelOptions.value.find(item => item.id == value)?.model || ""; |
| | | form.value.unit = |
| | | modelOptions.value.find(item => item.id == value)?.unit || ""; |
| | | }; |
| | | |
| | | // éæ©æ£æµç»æ |
| | | const selectResult = e => { |
| | | form.value.checkResult = e.value; |
| | | showResultSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | |
| | | return; |
| | | } |
| | | if (!form.value.quantity) { |
| | | showToast("请è¾å
¥æ°é"); |
| | | showToast("请è¾å
¥æ»æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.qualifiedQuantity && form.value.qualifiedQuantity !== 0) { |
| | | showToast("请è¾å
¥åæ ¼æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.unqualifiedQuantity && form.value.unqualifiedQuantity !== 0) { |
| | | showToast("请è¾å
¥ä¸åæ ¼æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.productId) { |
| | |
| | | showToast("è¯·éæ©ææ "); |
| | | return; |
| | | } |
| | | if (!form.value.checkResult) { |
| | | showToast("è¯·éæ©æ£æµç»æ"); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | |
| | | form.value.inspectType = 1; |
| | |
| | | |
| | | const data = { ...form.value, qualityInspectParams: tableData.value }; |
| | | data.quantity = Number(data.quantity); |
| | | data.qualifiedQuantity = Number(data.qualifiedQuantity); |
| | | data.unqualifiedQuantity = Number(data.unqualifiedQuantity); |
| | | if (isEdit.value) { |
| | | const res = await qualityInspectUpdate(data); |
| | | showToast("ä¿åæå"); |
| | |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | unit: "kg", |
| | | quantity: 1000, |
| | | checkCompany: "ç¬¬ä¸æ¹æ£æµæºæ", |
| | | checkResult: "åæ ¼", |
| | | qualifiedQuantity: 0, |
| | | unqualifiedQuantity: 0, |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | |
| | | </view> |
| | | <text class="header-title">{{ detailData.productName || '-' }}</text> |
| | | <view class="status-tags"> |
| | | <u-tag v-if="detailData.checkResult" |
| | | :type="getTagType(detailData.checkResult)" |
| | | <u-tag v-if="detailData.passRate != null && detailData.passRate !== ''" |
| | | type="primary" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.checkResult }} |
| | | åæ ¼ç {{ formatPassRate(detailData.passRate) }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(detailData.inspectState)" |
| | | size="small" |
| | |
| | | <text class="detail-value">{{ detailData.unit || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ detailData.quantity || 0 }}</text> |
| | | <text class="detail-label">æ»æ°é</text> |
| | | <text class="detail-value">{{ detailData.quantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åæ ¼æ°é</text> |
| | | <text class="detail-value">{{ detailData.qualifiedQuantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¸åæ ¼æ°é</text> |
| | | <text class="detail-value">{{ detailData.unqualifiedQuantity ?? 0 }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åæ ¼ç</text> |
| | | <text class="detail-value">{{ formatPassRate(detailData.passRate) }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = result => { |
| | | switch (result) { |
| | | case "åæ ¼": |
| | | return "success"; |
| | | case "ä¸åæ ¼": |
| | | return "error"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | const formatPassRate = rate => { |
| | | if (rate === null || rate === undefined || rate === "") return "-"; |
| | | const num = Number(rate); |
| | | if (isNaN(num)) return rate; |
| | | if (num <= 1) return `${(num * 100).toFixed(2)}%`; |
| | | return `${num}%`; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | |
| | | </view> |
| | | </view> |
| | | <view class="status-tags"> |
| | | <u-tag v-if="item.checkResult" |
| | | :type="getTagType(item.checkResult)" |
| | | <u-tag v-if="item.passRate != null && item.passRate !== ''" |
| | | type="primary" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.checkResult }} |
| | | åæ ¼ç {{ formatPassRate(item.passRate) }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(item.inspectState)" |
| | | size="mini" |
| | |
| | | return inspectState ? "checkmark-circle" : "time"; |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = checkResult => { |
| | | if (checkResult === "åæ ¼") return "success"; |
| | | if (checkResult === "ä¸åæ ¼") return "error"; |
| | | return "default"; |
| | | // æ ¼å¼ååæ ¼ç |
| | | const formatPassRate = rate => { |
| | | if (rate === null || rate === undefined || rate === "") return "-"; |
| | | const num = Number(rate); |
| | | if (isNaN(num)) return rate; |
| | | if (num <= 1) return `${(num * 100).toFixed(2)}%`; |
| | | return `${num}%`; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | |
| | | pendingCount.value = inspectionList.value.filter( |
| | | item => !item.inspectState |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter( |
| | | item => item.checkResult === "åæ ¼" |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter(item => { |
| | | const rate = Number(item.passRate); |
| | | if (isNaN(rate)) return false; |
| | | return rate <= 1 ? rate >= 1 : rate >= 100; |
| | | }).length; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |