feat(quality): 新增质量管理模块的API接口和移动端页面
- 新增基础数据API:工序分页查询
- 新增质量管理API:检测项维护、附件管理、检验标准绑定、检验参数、临期退货、不合格管理、原材料检验、指标维护、原材料检验完整功能
- 新增原生能力工具类:扫码、拍照、蓝牙等功能封装
- 新增移动端页面:指标绑定管理、质量看板可视化、检测项维护、原材料检验、近效期退货台账、指标维护详情
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // å·¥åºå表å页æ¥è¯¢ |
| | | export function productProcessListPage(query) { |
| | | return request({ |
| | | url: '/productProcess/listPage', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | export function qualityInspectItemListPage(data) { |
| | | return request({ |
| | | url: "/qualityInspectItem/listPage", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function qualityInspectItemSave(data) { |
| | | return request({ |
| | | url: "/qualityInspectItem/save", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function qualityInspectItemDelete(data) { |
| | | return request({ |
| | | url: "/qualityInspectItem/delete", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢åæææ£éªå表 |
| | | export function qualityInspectListPage(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspect/listPage', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢åæææ£éª |
| | | export function qualityInspectAdd(data) { |
| | | return request({ |
| | | url: '/quality/qualityInspect/add', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹åæææ£éª |
| | | export function qualityInspectUpdate(data) { |
| | | return request({ |
| | | url: '/quality/qualityInspect/update', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // å é¤åæææ£éª |
| | | export function qualityInspectDel(data) { |
| | | return request({ |
| | | url: '/quality/rawMaterialInspection/delete', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // æäº¤åæææ£éª |
| | | export function submitQualityInspect(data) { |
| | | return request({ |
| | | url: '/quality/qualityInspect/submit', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // ä¸è½½åæææ£éªæ¥å |
| | | export function downloadQualityInspect(data) { |
| | | return request({ |
| | | url: '/quality/rawMaterialInspection/export', |
| | | method: 'post', |
| | | data: data, |
| | | responseType: "blob", |
| | | }) |
| | | } |
| | | |
| | | // è·åä¾åºåå表 |
| | | export function getSupplierList() { |
| | | return request({ |
| | | url: '/basic/supplier/list', |
| | | method: 'get', |
| | | }) |
| | | } |
| | | |
| | | // è·å产åå表 |
| | | export function getProductList() { |
| | | return request({ |
| | | url: '/basic/product/list', |
| | | method: 'get', |
| | | }) |
| | | } |
| | | |
| | | // è·å产ååå·å表 |
| | | export function getProductModelList(productId) { |
| | | return request({ |
| | | url: '/basic/productModel/list', |
| | | method: 'get', |
| | | params: { productId }, |
| | | }) |
| | | } |
| | | |
| | | // è·åæ£éªåå表 |
| | | export function getUserList() { |
| | | return request({ |
| | | url: '/system/user/list', |
| | | method: 'get', |
| | | }) |
| | | } |
| | | |
| | | |
| | | // æ¥è¯¢æ£éªææ |
| | | export function qualityInspectParamInfo(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectParam/' + query, |
| | | method: 'get', |
| | | data: query, |
| | | }) |
| | | } |
| | | // æäº¤æ£éª |
| | | export function qualityInspectParamUpdate(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectParam/update', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | } |
| | | |
| | | // å 餿£éªè®°å½ |
| | | export function qualityInspectParamDel(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectParam/del', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | } |
| | | // å 餿æ å表 |
| | | export function qualityInspectDetailByProductId(params) { |
| | | return request({ |
| | | url: "/qualityTestStandard/getQualityTestStandardByProductId", |
| | | method: "get", |
| | | params: params, |
| | | }); |
| | | } |
| | | // æ ¹æ®æ åIDè·åæ ååæ° |
| | | export function getQualityTestStandardParamByTestStandardId(testStandardId) { |
| | | return request({ |
| | | url: "/qualityTestStandard/getQualityTestStandardParamByTestStandardId", |
| | | method: "get", |
| | | params: { testStandardId }, |
| | | }); |
| | | } |
| | | |
| | | // æ¥è¯¢éä»¶å表 |
| | | export function qualityInspectFileListPage(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectFile/listPage', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | // ä¿åéä»¶å表 |
| | | export function qualityInspectFileAdd(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectFile/add', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | } |
| | | // å é¤éä»¶å表 |
| | | export function qualityInspectFileDel(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectFile/del', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | } |
| | | |
| | | |
| | | // å·¥åºæ¥è¯¢ |
| | | export function list() { |
| | | return request({ |
| | | url: "/productProcess/list", |
| | | method: "get", |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢ææ å表 |
| | | export function qualityTestStandardListPage(query) { |
| | | return request({ |
| | | url: "/qualityTestStandard/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢ææ å表 |
| | | export function qualityTestStandardAdd(query) { |
| | | return request({ |
| | | url: "/qualityTestStandard/add", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // ä¿®æ¹ææ å表 |
| | | export function qualityTestStandardUpdate(query) { |
| | | return request({ |
| | | url: "/qualityTestStandard/update", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // å 餿æ å表 |
| | | export function qualityTestStandardDel(query) { |
| | | return request({ |
| | | url: "/qualityTestStandard/del", |
| | | method: "delete", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // å 餿æ å表 |
| | | export function qualityInspectDetailByProductId(params) { |
| | | return request({ |
| | | url: "/qualityTestStandard/getQualityTestStandardByProductId", |
| | | method: "get", |
| | | params: params, |
| | | }); |
| | | } |
| | | |
| | | // å¤å¶æ ååæ° |
| | | export function qualityTestStandardCopyParam(id) { |
| | | return request({ |
| | | url: "/qualityTestStandard/copyParam", |
| | | method: "post", |
| | | data: { id }, |
| | | }); |
| | | } |
| | | |
| | | // æ¹éå®¡æ ¸ï¼ç¶æï¼1=éè¿/æ¹åï¼2=æ¤éï¼ |
| | | // ä¼ åï¼[{ id, state }] |
| | | export function qualityTestStandardAudit(data) { |
| | | return request({ |
| | | url: "/qualityTestStandard/qualityTestStandardAudit", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // æ ååæ°ï¼å表ï¼ä¸åé¡µï¼ |
| | | export function qualityTestStandardParamList(query) { |
| | | return request({ |
| | | url: "/qualityTestStandardParam/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ ååæ°ï¼æ°å¢ |
| | | export function qualityTestStandardParamAdd(data) { |
| | | return request({ |
| | | url: "/qualityTestStandardParam/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // æ ååæ°ï¼ä¿®æ¹ |
| | | export function qualityTestStandardParamUpdate(data) { |
| | | return request({ |
| | | url: "/qualityTestStandardParam/update", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // æ ååæ°ï¼å é¤ï¼ä¼ id æ°ç»ï¼ |
| | | export function qualityTestStandardParamDel(ids) { |
| | | return request({ |
| | | url: "/qualityTestStandardParam/del", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢ä¸´æéåå°è´¦å表 |
| | | export function nearExpiryReturnListPage(query) { |
| | | return request({ |
| | | url: '/quality/nearExpiryReturn/listPage', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢ä¸´æéåå°è´¦ |
| | | export function nearExpiryReturnAdd(data) { |
| | | return request({ |
| | | url: '/quality/nearExpiryReturn/add', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹ä¸´æéåå°è´¦ |
| | | export function nearExpiryReturnUpdate(data) { |
| | | return request({ |
| | | url: '/quality/nearExpiryReturn/update', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // å é¤ä¸´æéåå°è´¦ |
| | | export function nearExpiryReturnDel(ids) { |
| | | return request({ |
| | | url: '/quality/nearExpiryReturn/del', |
| | | method: 'delete', |
| | | data: ids, |
| | | }) |
| | | } |
| | | |
| | | // è·å临æéåå°è´¦è¯¦æ
|
| | | export function nearExpiryReturnDetail(id) { |
| | | return request({ |
| | | url: '/quality/nearExpiryReturn/' + id, |
| | | method: 'get', |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedListPage(query) { |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ°å¢ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedAdd(query) { |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/add", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¿®æ¹ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedUpdate(query) { |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/update", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¸åæ ¼å¤ç |
| | | export function qualityUnqualifiedDeal(query) { |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/deal", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // å é¤ä¸åæ ¼ç®¡çå表 |
| | | export function qualityUnqualifiedDel(query) { |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/del", |
| | | method: "delete", |
| | | data: query, |
| | | }); |
| | | } |
| | | // æ¥è¯¢ä¸åæ ¼ç®¡çä¿¡æ¯ |
| | | export function getQualityUnqualifiedInfo(query) { |
| | | return request({ |
| | | url: "/quality/qualityUnqualified/" + query, |
| | | method: "get", |
| | | data: query, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢éä»¶å表 |
| | | export function qualityInspectFileListPage(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectFile/listPage', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | // ä¿åéä»¶å表 |
| | | export function qualityInspectFileAdd(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectFile/add', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | } |
| | | // å é¤éä»¶å表 |
| | | export function qualityInspectFileDel(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectFile/del', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢æ£éªææ |
| | | export function qualityInspectParamInfo(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectParam/' + query, |
| | | method: 'get', |
| | | data: query, |
| | | }) |
| | | } |
| | | // æäº¤æ£éª |
| | | export function qualityInspectParamUpdate(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectParam/update', |
| | | method: 'post', |
| | | data: query, |
| | | }) |
| | | } |
| | | |
| | | // å 餿£éªè®°å½ |
| | | export function qualityInspectParamDel(query) { |
| | | return request({ |
| | | url: '/quality/qualityInspectParam/del', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // ç»å®å表ï¼ä¸åé¡µï¼ |
| | | export function qualityTestStandardBindingList(query) { |
| | | return request({ |
| | | url: "/qualityTestStandardBinding/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢ç»å®ï¼æ¯ææ¹éï¼ |
| | | export function qualityTestStandardBindingAdd(data) { |
| | | return request({ |
| | | url: "/qualityTestStandardBinding/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤ç»å®ï¼ä¼ id æ°ç»ï¼ |
| | | export function qualityTestStandardBindingDel(ids) { |
| | | return request({ |
| | | url: "/qualityTestStandardBinding/del", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢åæ£å表 |
| | | export function findRawMaterialListPage(query) { |
| | | return request({ |
| | | url: '/quality/rawMaterial/listPage', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢åæ£è¯¦æ
|
| | | export function findRawMaterialDetail(id) { |
| | | return request({ |
| | | url: '/quality/rawMaterial/detail/' + id, |
| | | method: 'get', |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢åæ£ |
| | | export function createRawMaterial(data) { |
| | | return request({ |
| | | url: '/quality/rawMaterial', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // æäº¤ |
| | | export function submitRawMaterial(id) { |
| | | return request({ |
| | | url: '/quality/rawMaterial/submit/' + id, |
| | | method: 'patch', |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹æ£éªäºº |
| | | export function updateCheckUserName(data) { |
| | | return request({ |
| | | url: '/quality/rawMaterial/updateCheckUserName', |
| | | method: 'patch', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹åæ£ |
| | | export function updateRawMaterial(data) { |
| | | return request({ |
| | | url: '/quality/rawMaterial', |
| | | method: 'put', |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | | // å é¤åæ£ |
| | | export function deleteRawMaterial(query) { |
| | | return request({ |
| | | url: '/quality/rawMaterial', |
| | | method: 'delete', |
| | | data: query, |
| | | }) |
| | | } |
| | | |
| | | export function downloadRawMaterial(data) { |
| | | return request({ |
| | | url: '/quality/rawMaterial/down', |
| | | method: 'post', |
| | | data: data, |
| | | responseType: "blob", |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="inspect-item-page"> |
| | | <PageHeader title="æ£æµé¡¹ç»´æ¤" @back="goBack" /> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="请è¾å
¥æ£æµé¡¹ç®åç§°" |
| | | v-model="searchForm.name" |
| | | @search="handleQuery" |
| | | @custom="handleQuery" |
| | | @clear="handleQuery" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | ></up-search> |
| | | </view> |
| | | |
| | | <!-- å表åºå --> |
| | | <view class="list-container" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="index" class="list-item"> |
| | | <view class="item-content" @click="openDialog('edit', item)"> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ£æµé¡¹ç®ï¼</text> |
| | | <text class="item-value">{{ item.name }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">åä½ï¼</text> |
| | | <text class="item-value">{{ item.unit || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ åå¼ï¼</text> |
| | | <text class="item-value">{{ item.standardValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">å
æ§å¼ï¼</text> |
| | | <text class="item-value">{{ item.internalControl || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="item-actions"> |
| | | <up-button type="primary" size="mini" @click.stop="openDialog('edit', item)">ç¼è¾</up-button> |
| | | <up-button type="error" size="mini" @click.stop="handleDelete(item)">å é¤</up-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å è½½æ´å¤/å页 --> |
| | | <view class="pagination-container"> |
| | | <up-loadmore :status="loadStatus" @loadmore="getList" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ æ°æ®"></up-empty> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" @click="openDialog('add')"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | |
| | | <!-- æ°å¢/ä¿®æ¹å¼¹çª --> |
| | | <up-popup v-model:show="dialogVisible" mode="center" round closeable @close="closeDialog"> |
| | | <view class="dialog-content"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">{{ operationType === 'add' ? 'æ°å¢æ£æµé¡¹ç®' : 'ä¿®æ¹æ£æµé¡¹ç®' }}</text> |
| | | </view> |
| | | <up-form :model="form" ref="formRef" label-width="100" label-position="top"> |
| | | <up-form-item label="æ£æµé¡¹ç®" prop="name" required borderBottom> |
| | | <up-input v-model="form.name" placeholder="请è¾å
¥æ£æµé¡¹ç®åç§°" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="åä½" prop="unit" required borderBottom> |
| | | <up-input v-model="form.unit" placeholder="请è¾å
¥åä½" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ åå¼" prop="standardValue" borderBottom> |
| | | <up-input v-model="form.standardValue" placeholder="请è¾å
¥æ åå¼" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="å
æ§å¼" prop="internalControl" borderBottom> |
| | | <up-input v-model="form.internalControl" placeholder="请è¾å
¥å
æ§å¼" border="surround" /> |
| | | </up-form-item> |
| | | </up-form> |
| | | <view class="dialog-footer"> |
| | | <up-button type="primary" text="确认" @click="submitForm" :loading="submitLoading"></up-button> |
| | | <up-button text="åæ¶" @click="closeDialog" customStyle="margin-top: 20rpx"></up-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue'; |
| | | import { |
| | | qualityInspectItemListPage, |
| | | qualityInspectItemSave, |
| | | qualityInspectItemDelete |
| | | } from '@/api/qualityManagement/inspectItem.js'; |
| | | import { toast, showConfirm } from '@/utils/common'; |
| | | |
| | | const searchForm = reactive({ |
| | | name: '' |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | | total: 0 |
| | | }); |
| | | const loadStatus = ref('loadmore'); |
| | | |
| | | const dialogVisible = ref(false); |
| | | const operationType = ref('add'); |
| | | const submitLoading = ref(false); |
| | | const form = reactive({ |
| | | id: null, |
| | | name: '', |
| | | unit: '', |
| | | standardValue: '', |
| | | internalControl: '' |
| | | }); |
| | | |
| | | const rules = { |
| | | name: { |
| | | type: 'string', |
| | | required: true, |
| | | message: '请è¾å
¥æ£æµé¡¹ç®åç§°', |
| | | trigger: ['blur', 'change'] |
| | | }, |
| | | unit: { |
| | | type: 'string', |
| | | required: true, |
| | | message: '请è¾å
¥åä½', |
| | | trigger: ['blur', 'change'] |
| | | } |
| | | }; |
| | | |
| | | const formRef = ref(null); |
| | | |
| | | const getList = () => { |
| | | if (loadStatus.value === 'loading' || (page.total > 0 && tableData.value.length >= page.total)) return; |
| | | |
| | | loadStatus.value = 'loading'; |
| | | const params = { |
| | | name: searchForm.name || null, |
| | | current: page.current, |
| | | size: page.size |
| | | }; |
| | | |
| | | qualityInspectItemListPage(params).then(res => { |
| | | const records = res?.data?.records || []; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | } |
| | | page.total = res?.data?.total || 0; |
| | | |
| | | if (tableData.value.length >= page.total) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | | loadStatus.value = 'loadmore'; |
| | | page.current++; |
| | | } |
| | | }).catch(() => { |
| | | loadStatus.value = 'loadmore'; |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | page.total = 0; |
| | | tableData.value = []; |
| | | loadStatus.value = 'loadmore'; |
| | | getList(); |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | if (type === 'edit' && row) { |
| | | Object.assign(form, { |
| | | id: row.id, |
| | | name: row.name, |
| | | unit: row.unit, |
| | | standardValue: row.standardValue, |
| | | internalControl: row.internalControl |
| | | }); |
| | | } else { |
| | | Object.assign(form, { |
| | | id: null, |
| | | name: '', |
| | | unit: '', |
| | | standardValue: '', |
| | | internalControl: '' |
| | | }); |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate().then(res => { |
| | | submitLoading.value = true; |
| | | qualityInspectItemSave(form).then(() => { |
| | | toast(operationType.value === 'add' ? 'æ°å¢æå' : 'ä¿®æ¹æå'); |
| | | dialogVisible.value = false; |
| | | handleQuery(); |
| | | }).finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }).catch(errors => { |
| | | console.log('éªè¯å¤±è´¥', errors); |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | showConfirm('确认å é¤è¯¥æ£æµé¡¹ç®åï¼').then(res => { |
| | | if (res.confirm) { |
| | | qualityInspectItemDelete({ id: row.id }).then(() => { |
| | | toast('å 餿å'); |
| | | handleQuery(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | handleQuery(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .inspect-item-page { |
| | | padding-bottom: 120rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .item-content { |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .item-row { |
| | | display: flex; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-label { |
| | | color: #909399; |
| | | width: 160rpx; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 20rpx; |
| | | border-top: 1rpx solid #ebeef5; |
| | | padding-top: 20rpx; |
| | | } |
| | | |
| | | .pagination-container { |
| | | padding: 20rpx 0; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | |
| | | .fab-button { |
| | | position: fixed; |
| | | right: 40rpx; |
| | | bottom: 60rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background-color: #3c9cff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(60, 156, 255, 0.4); |
| | | z-index: 99; |
| | | } |
| | | |
| | | .dialog-content { |
| | | width: 600rpx; |
| | | padding: 40rpx; |
| | | background-color: #ffffff; |
| | | border-radius: 24rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .dialog-header { |
| | | margin-bottom: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | margin-top: 40rpx; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-add"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="isEdit ? 'ç¼è¾åºåæ£éª' : 'æ°å¢åºåæ£éª'" |
| | | @back="goBack" /> |
| | | <!-- 表åå
容 --> |
| | | <up-form :model="form" |
| | | ref="formRef" |
| | | label-width="110" |
| | | :rules="rules"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <!-- <up-form-item label="å·¥åº" |
| | | prop="process" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.process" |
| | | placeholder="è¯·éæ©å·¥åº" |
| | | readonly |
| | | :disabled="processQuantityDisabled" /> |
| | | <template #right> |
| | | <up-icon @click="showprocessSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> --> |
| | | <up-form-item label="产ååç§°" |
| | | prop="productId" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.productName" |
| | | placeholder="è¯·éæ©äº§å" |
| | | readonly |
| | | @click="showProductTree = true" |
| | | :disabled="isEdit" /> |
| | | <template #right> |
| | | <up-icon @click="showProductTree = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="è§æ ¼åå·" |
| | | prop="productModelId" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.model" |
| | | placeholder="è¯·éæ©è§æ ¼åå·" |
| | | readonly |
| | | :disabled="isEdit" /> |
| | | <template #right> |
| | | <up-icon @click="showModelSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="ææ éæ©" |
| | | prop="testStandardId" |
| | | border-bottom> |
| | | <up-input v-model="testStandardDisplay" |
| | | placeholder="è¯·éæ©ææ " |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon @click="openTestStandardSheet" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="åä½" |
| | | prop="unit" |
| | | border-bottom> |
| | | <up-input v-model="form.unit" |
| | | placeholder="请è¾å
¥åä½" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="æ°é" |
| | | prop="quantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ°é" |
| | | :disabled="processQuantityDisabled" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµåä½" |
| | | prop="checkCompany" |
| | | border-bottom> |
| | | <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" |
| | | border-bottom> |
| | | <up-input v-model="form.checkName" |
| | | placeholder="è¯·éæ©æ£éªå" |
| | | readonly |
| | | @click="showInspectorSheet" /> |
| | | <template #right> |
| | | <up-icon @click="showInspectorSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµæ¥æ" |
| | | prop="checkTime" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.checkTime" |
| | | placeholder="è¯·éæ©æ£æµæ¥æ" |
| | | readonly /> |
| | | <!-- <template #right> |
| | | <up-icon name="calendar" |
| | | @click="showDatePicker"></up-icon> |
| | | </template> --> |
| | | </up-form-item> |
| | | <!-- æ£éªé¡¹ç® --> |
| | | <view class="inspection-items-container"> |
| | | <view class="steps-header"> |
| | | <text class="steps-title">æ£éªé¡¹ç®</text> |
| | | <text class="steps-count">å
± {{ tableData.length }} 个项ç®</text> |
| | | </view> |
| | | <view class="steps-list"> |
| | | <view v-for="(item, index) in tableData" |
| | | :key="index" |
| | | class="exec-step-item"> |
| | | <view class="step-number"> |
| | | {{ index + 1 }} |
| | | </view> |
| | | <view class="step-content"> |
| | | <view class="step-row"> |
| | | <text class="step-label">ææ ï¼</text> |
| | | <text class="step-value">{{ item.parameterItem }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">åä½ï¼</text> |
| | | <text class="step-value">{{ item.unit }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">æ åå¼ï¼</text> |
| | | <text class="step-value">{{ item.standardValue }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">å
æ§å¼ï¼</text> |
| | | <text class="step-value">{{ item.controlValue }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">æ£éªå¼ï¼</text> |
| | | <up-input v-model="item.testValue" |
| | | placeholder="请è¾å
¥æ£éªå¼" |
| | | clearable |
| | | border-bottom |
| | | class="step-input" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="tableData.length === 0" |
| | | class="empty-data"> |
| | | <text>请å
éæ©ææ </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-form> |
| | | <!-- åºé¨æé® --> |
| | | <view class="bottom-buttons"> |
| | | <up-button type="default" |
| | | size="default" |
| | | @click="goBack" |
| | | class="bottom-btn"> |
| | | åæ¶ |
| | | </up-button> |
| | | <up-button type="primary" |
| | | size="default" |
| | | @click="submitForm" |
| | | :loading="loading" |
| | | class="bottom-btn"> |
| | | {{ isEdit ? 'ä¿å' : 'æäº¤' }} |
| | | </up-button> |
| | | </view> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup v-model:show="showDate" |
| | | mode="date" |
| | | :start-year="2020" |
| | | :end-year="2030" |
| | | @confirm="confirmDate" /> |
| | | <!-- å·¥åºéæ© --> |
| | | <up-action-sheet :show="showprocessSheet" |
| | | :actions="processOptions" |
| | | @select="selectprocess" |
| | | @close="showprocessSheet = false" |
| | | title="鿩工åº" /> |
| | | <!-- 产åéæ© --> |
| | | <up-action-sheet :show="showProductSheet" |
| | | :actions="productSheetOptions" |
| | | @select="selectProduct" |
| | | @close="showProductSheet = false" |
| | | title="éæ©äº§å" /> |
| | | <!-- è§æ ¼åå·éæ© --> |
| | | <up-action-sheet :show="showModelSheet" |
| | | :actions="modelSheetOptions" |
| | | @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" |
| | | @select="selectInspector" |
| | | @close="showInspectorSheet = false" |
| | | title="éæ©æ£éªå" /> |
| | | <!-- ææ éæ© --> |
| | | <up-action-sheet :show="showTestStandardSheet" |
| | | :actions="testStandardSheetOptions" |
| | | @select="selectTestStandard" |
| | | @close="showTestStandardSheet = false" |
| | | title="éæ©ææ " /> |
| | | <!-- äº§åæ å½¢éæ©å¨ --> |
| | | <up-popup v-model:show="showProductTree" |
| | | position="bottom" |
| | | :round="true" |
| | | :closeable="true" |
| | | @close="showProductTree = false"> |
| | | <view class="tree-selector"> |
| | | <view class="tree-header"> |
| | | <text class="tree-title">éæ©äº§å</text> |
| | | </view> |
| | | <view class="tree-content"> |
| | | <view class="tree-node" |
| | | v-for="(node, index) in productOptions" |
| | | :key="index"> |
| | | <view v-if="node.children && node.children.length > 0" |
| | | class="tree-node-header" |
| | | @click="toggleNode(node)"> |
| | | <up-icon :name="node.expanded ? 'arrow-down' : 'arrow-right'" |
| | | class="tree-node-icon" /> |
| | | <text class="tree-node-label">{{ node.label }}</text> |
| | | </view> |
| | | <view v-else |
| | | class="tree-node-header" |
| | | @click="selectTreeNode(node)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ node.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == node.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | <view v-if="node.children && node.children.length > 0 && node.expanded" |
| | | class="tree-node-children"> |
| | | <view class="tree-node" |
| | | v-for="(child, childIndex) in node.children" |
| | | :key="childIndex"> |
| | | <view v-if="child.children && child.children.length > 0" |
| | | class="tree-node-header" |
| | | @click="toggleNode(child)"> |
| | | <up-icon :name="child.expanded ? 'arrow-down' : 'arrow-right'" |
| | | class="tree-node-icon" /> |
| | | <text class="tree-node-label">{{ child.label }}</text> |
| | | </view> |
| | | <view v-else |
| | | class="tree-node-header" |
| | | @click="selectTreeNode(child)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ child.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == child.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | <view v-if="child.children && child.children.length > 0 && child.expanded" |
| | | class="tree-node-children"> |
| | | <view class="tree-node" |
| | | v-for="(grandchild, grandchildIndex) in child.children" |
| | | :key="grandchildIndex"> |
| | | <view class="tree-node-header" |
| | | @click="selectTreeNode(grandchild)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ grandchild.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == grandchild.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, nextTick } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { |
| | | qualityInspectAdd, |
| | | qualityInspectUpdate, |
| | | qualityInspectParamInfo, |
| | | qualityInspectDetailByProductId, |
| | | getQualityTestStandardParamByTestStandardId, |
| | | list, |
| | | } from "@/api/qualityManagement/materialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | // å è½½ç¶æ |
| | | const loading = ref(false); |
| | | // æ¥æéæ©å¨ |
| | | const showDate = ref(false); |
| | | // å·¥åºéæ© |
| | | const showprocessSheet = ref(false); |
| | | // 产åéæ© |
| | | const showProductSheet = ref(false); |
| | | // äº§åæ å½¢éæ©å¨ |
| | | const showProductTree = ref(false); |
| | | // è§æ ¼åå·éæ© |
| | | const showModelSheet = ref(false); |
| | | // æ£æµç»æéæ© |
| | | const showResultSheet = ref(false); |
| | | // æ£éªåéæ© |
| | | const showInspectorSheet = ref(false); |
| | | // ææ éæ© |
| | | const showTestStandardSheet = ref(false); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const form = ref({ |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }); |
| | | |
| | | // æ¾ç¤ºç¨çåé |
| | | const testStandardDisplay = ref(""); |
| | | |
| | | // æ£éªé¡¹ç® |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // å·¥åºå表 |
| | | const processList = ref([]); |
| | | // 产åé项 |
| | | const productOptions = ref([]); |
| | | // åå·é项 |
| | | const modelOptions = ref([]); |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | // æ£æµç»æé项 |
| | | const resultOptions = ref([ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]); |
| | | // ææ é项 |
| | | const testStandardOptions = ref([]); |
| | | // å½å产åID |
| | | const currentProductId = ref(0); |
| | | |
| | | // ActionSheeté项 |
| | | const processOptions = computed(() => { |
| | | return processList.value.map(item => ({ |
| | | name: item.name, |
| | | value: item.name, |
| | | })); |
| | | }); |
| | | |
| | | const productSheetOptions = computed(() => { |
| | | return productOptions.value.map(item => ({ |
| | | name: item.label, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | | const modelSheetOptions = computed(() => { |
| | | 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, |
| | | })); |
| | | }); |
| | | |
| | | const userSheetOptions = computed(() => { |
| | | return userList.value.map(item => ({ |
| | | name: item.nickName, |
| | | value: item.nickName, |
| | | })); |
| | | }); |
| | | |
| | | const testStandardSheetOptions = computed(() => { |
| | | return testStandardOptions.value.map(item => ({ |
| | | name: item.standardName || item.standardNo, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | process: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [ |
| | | { required: true, message: "è¯·éæ©äº§ååå·", trigger: "change" }, |
| | | ], |
| | | testStandardId: [ |
| | | { required: false, message: "è¯·éæ©ææ ", trigger: "change" }, |
| | | ], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [ |
| | | { required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // æ¯å¦ä¸ºç¼è¾æ¨¡å¼ |
| | | const isEdit = computed(() => { |
| | | const id = getPageId(); |
| | | return !!id; |
| | | }); |
| | | |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åå·¥åºãæ°éç½®ç° |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | return currentPage.options.id; |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const confirmDate = e => { |
| | | form.value.checkTime = dayjs(e.value).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // éæ©å·¥åº |
| | | const selectprocess = e => { |
| | | form.value.process = e.value; |
| | | showprocessSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©äº§å |
| | | const selectProduct = e => { |
| | | form.value.productId = e.value; |
| | | form.value.productName = e.name; |
| | | showProductSheet.value = false; |
| | | getModels(e.value); |
| | | }; |
| | | |
| | | // 忢æ å½¢èç¹å±å¼/æå |
| | | const toggleNode = node => { |
| | | node.expanded = !node.expanded; |
| | | }; |
| | | |
| | | // éæ©æ å½¢èç¹ |
| | | const selectTreeNode = node => { |
| | | // ç¡®ä¿åªéæ©æ«ç«¯èç¹ |
| | | if (!node.children || node.children.length == 0) { |
| | | form.value.productId = node.value; |
| | | form.value.productName = node.label; |
| | | showProductTree.value = false; |
| | | getModels(node.value); |
| | | } |
| | | }; |
| | | |
| | | // 转æ¢äº§åæ ç»æ |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | |
| | | // æ ¹æ®IDæ¥æ¾èç¹ |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | |
| | | // éæ©è§æ ¼åå· |
| | | const selectModel = e => { |
| | | form.value.productModelId = e.value; |
| | | showModelSheet.value = false; |
| | | handleChangeModel(e.value); |
| | | }; |
| | | |
| | | // å¤çåå·åå |
| | | const handleChangeModel = value => { |
| | | form.value.model = |
| | | 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; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | | const selectInspector = e => { |
| | | form.value.checkName = e.value; |
| | | showInspectorSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©ææ |
| | | const selectTestStandard = e => { |
| | | form.value.testStandardId = e.value; |
| | | testStandardDisplay.value = e.name; |
| | | showTestStandardSheet.value = false; |
| | | handleTestStandardChange(e.value); |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = testStandardId => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | tableData.value = res.data || []; |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ ååæ°å¤±è´¥:", error); |
| | | tableData.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | const openTestStandardSheet = () => { |
| | | console.log("openTestStandardSheet"); |
| | | showTestStandardSheet.value = true; |
| | | }; |
| | | |
| | | // è·åå·¥åºå表 |
| | | const getprocessList = () => { |
| | | list().then(res => { |
| | | processList.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // è·å产åé项 |
| | | const getProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return productOptions.value; |
| | | }); |
| | | }; |
| | | |
| | | // è·åç¨æ·å表 |
| | | const getUserList = async () => { |
| | | try { |
| | | const userRes = await userListNoPage(); |
| | | userList.value = userRes.data || []; |
| | | } catch (e) { |
| | | console.error("å è½½æ£éªåå表失败", e); |
| | | userList.value = []; |
| | | } |
| | | }; |
| | | |
| | | // è·ååå·å表 |
| | | const getModels = value => { |
| | | form.value.productModelId = ""; |
| | | form.value.unit = ""; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value; |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res; |
| | | }); |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | // è·åææ å表 |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2, |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // ä¿å䏿æ¡éé¡¹æ°æ® |
| | | testStandardOptions.value = res.data || []; |
| | | // æ¸
ç©ºè¡¨æ ¼æ°æ®ï¼çå¾
ç¨æ·éæ©ææ |
| | | tableData.value = []; |
| | | // æ¸
ç©ºææ éæ© |
| | | form.value.testStandardId = ""; |
| | | testStandardDisplay.value = ""; |
| | | }); |
| | | }; |
| | | |
| | | // è·åæ£éªåæ°å表ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const getQualityInspectParamList = id => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | console.log("submitForm", form.value, tableData.value); |
| | | try { |
| | | // await formRef.value.validate(); |
| | | if (!form.value.productModelId) { |
| | | showToast("è¯·éæ©è§æ ¼åå·"); |
| | | return; |
| | | } |
| | | // if (!form.value.process) { |
| | | // showToast("è¯·éæ©å·¥åº"); |
| | | // return; |
| | | // } |
| | | if (!form.value.quantity) { |
| | | showToast("请è¾å
¥æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.productId) { |
| | | showToast("è¯·éæ©äº§å"); |
| | | return; |
| | | } |
| | | if (!form.value.checkResult) { |
| | | showToast("è¯·éæ©æ£æµç»æ"); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | |
| | | form.value.inspectType = 2; |
| | | if (isEdit.value) { |
| | | tableData.value.forEach(item => { |
| | | delete item.id; |
| | | }); |
| | | } |
| | | |
| | | const data = { ...form.value, qualityInspectParams: tableData.value }; |
| | | data.quantity = Number(data.quantity); |
| | | if (isEdit.value) { |
| | | const res = await qualityInspectUpdate(data); |
| | | showToast("ä¿åæå"); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | const res = await qualityInspectAdd(data); |
| | | showToast("æäº¤æå"); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } |
| | | } catch (error) { |
| | | console.error("表åéªè¯å¤±è´¥:", error); |
| | | showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // åå§å表å |
| | | const initForm = async () => { |
| | | const id = getPageId(); |
| | | if (id) { |
| | | // ç¼è¾æ¨¡å¼ï¼å è½½æ°æ® |
| | | // å
éç½®è¡¨åæ°æ® |
| | | form.value = { |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | // å
ç¡®ä¿äº§åæ å·²å è½½ï¼å¦åç¼è¾æ¶äº§å/è§æ ¼åå·æ æ³åæ¾ |
| | | await getProductOptions(); |
| | | // 仿¬å°åå¨è·åç¼è¾æ°æ® |
| | | const row = uni.getStorageSync("finalInspectionEditData") || { |
| | | id: id, |
| | | checkTime: "2026-03-03", |
| | | process: "䏿µ·é屿ææéå
¬å¸", |
| | | checkName: "å¼ ä¸", |
| | | productName: "ä¸é颿¿æ", |
| | | productId: 1, |
| | | productModelId: 1, |
| | | model: "304", |
| | | testStandardId: "1", |
| | | unit: "kg", |
| | | quantity: 1000, |
| | | checkCompany: "ç¬¬ä¸æ¹æ£æµæºæ", |
| | | checkResult: "åæ ¼", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | form.value = { ...row }; |
| | | currentProductId.value = row.productId || 0; |
| | | // å
³é®ï¼ç¼è¾æ¶å è½½è§æ ¼åå·ä¸æéé¡¹ï¼æè½åæ¾ productModelId |
| | | if (currentProductId.value) { |
| | | try { |
| | | const res = await modelList({ id: currentProductId.value }); |
| | | modelOptions.value = res || []; |
| | | // 忥åå¡« model / unit |
| | | if (form.value.productModelId) { |
| | | handleChangeModel(form.value.productModelId); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½è§æ ¼åå·å¤±è´¥", e); |
| | | modelOptions.value = []; |
| | | } |
| | | } |
| | | // ç¼è¾æ¨¡å¼ä¸ï¼å
å è½½ææ é项ï¼ç¶åå è½½åæ°å表 |
| | | if (currentProductId.value) { |
| | | // å
å è½½ææ é项 |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2, |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | testStandardOptions.value = res.data || []; |
| | | // ä½¿ç¨ nextTick ç¡®ä¿éé¡¹å·²ç»æ¸²æ |
| | | nextTick(() => { |
| | | // 妿ç¼è¾æ°æ®ä¸æ testStandardIdï¼å设置并å 载对åºçåæ° |
| | | if (savedTestStandardId) { |
| | | // ç¡®ä¿ç±»åå¹é
|
| | | const matchedOption = testStandardOptions.value.find( |
| | | item => |
| | | item.id == savedTestStandardId || |
| | | String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // ç¡®ä¿ä½¿ç¨å¹é
项ç id |
| | | form.value.testStandardId = matchedOption.id; |
| | | testStandardDisplay.value = |
| | | matchedOption.standardName || matchedOption.standardNo; |
| | | // ç¼è¾ä¿ç忣éªå¼ï¼ç´æ¥æåååæ°æ°æ® |
| | | getQualityInspectParamList(row.id); |
| | | } else { |
| | | // 妿æ¾ä¸å°å¹é
项ï¼å°è¯ç´æ¥ä½¿ç¨åå¼ |
| | | console.warn( |
| | | "æªæ¾å°å¹é
çææ é项ï¼testStandardId:", |
| | | savedTestStandardId |
| | | ); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } else { |
| | | // å¦åä½¿ç¨æ§çé»è¾ |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | // å±å¼äº§åæ å°å½åéä¸çèç¹ |
| | | expandProductTree(productOptions.value, row.productId); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼åå§å表å |
| | | form.value = { |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | } |
| | | }; |
| | | |
| | | // å±å¼äº§åæ å°æå®èç¹ |
| | | const expandProductTree = (nodes, targetId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | const node = nodes[i]; |
| | | if (node.value === targetId) { |
| | | return true; // æ¾å°ç®æ èç¹ |
| | | } |
| | | if (node.children && node.children.length > 0) { |
| | | const found = expandProductTree(node.children, targetId); |
| | | if (found) { |
| | | node.expanded = true; // å±å¼ç¶èç¹ |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getprocessList(); |
| | | getProductOptions(); |
| | | getUserList(); |
| | | initForm(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | initForm(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .material-inspection-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100px; |
| | | } |
| | | |
| | | // æ£éªé¡¹ç®å®¹å¨ |
| | | .inspection-items-container { |
| | | padding: 20px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .steps-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | padding-bottom: 12px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .steps-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .steps-count { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .steps-list { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .exec-step-item { |
| | | position: relative; |
| | | display: flex; |
| | | margin-bottom: 16px; |
| | | padding: 16px; |
| | | background-color: #ffffff; |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .exec-step-item:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); |
| | | border-color: #409eff; |
| | | transform: translateY(-1px); |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: -25rpx; |
| | | right: -25rpx; |
| | | width: 50rpx; |
| | | height: 50rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | text-align: center; |
| | | font-size: 20px; |
| | | border-radius: 50%; |
| | | background-color: red; |
| | | border: none; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .delete-btn:hover { |
| | | transform: scale(1.1); |
| | | box-shadow: 0 3px 6px rgba(245, 108, 108, 0.4); |
| | | } |
| | | |
| | | .step-number { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 32px; |
| | | height: 32px; |
| | | margin-right: 16px; |
| | | background-color: #ecf5ff; |
| | | color: #409eff; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | border-radius: 50%; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .step-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .step-row { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .step-row:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .step-label { |
| | | display: inline-block; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-right: 12px; |
| | | flex-shrink: 0; |
| | | line-height: 36px; |
| | | } |
| | | |
| | | .step-input { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .step-input input { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .add-step-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | height: 44px; |
| | | line-height: 44px; |
| | | font-size: 14px; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .add-step-btn:hover { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3); |
| | | } |
| | | |
| | | .add-step-btn text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | // åºé¨æé® |
| | | .bottom-buttons { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | display: flex; |
| | | padding: 16px 20px; |
| | | background: #ffffff; |
| | | border-top: 1px solid #f0f0f0; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .bottom-btn { |
| | | flex: 1; |
| | | } |
| | | |
| | | // æ 形鿩卿 ·å¼ |
| | | .tree-selector { |
| | | width: 100%; |
| | | max-height: 70vh; |
| | | background: #ffffff; |
| | | border-radius: 16px 16px 0 0; |
| | | } |
| | | |
| | | .tree-header { |
| | | padding: 16px 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | text-align: center; |
| | | } |
| | | |
| | | .tree-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .tree-content { |
| | | padding: 10px 0; |
| | | max-height: calc(70vh - 60px); |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .tree-node { |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | .tree-node-header { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px 0; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .tree-node-icon { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 8px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .tree-node-icon-placeholder { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .tree-node-label { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .tree-node-check { |
| | | width: 20px; |
| | | height: 20px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .tree-node-children { |
| | | margin-left: 28px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="åºåæ£éªè¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- 详æ
å
容 --> |
| | | <view class="detail-section" |
| | | v-if="detailData"> |
| | | <view class="detail-card"> |
| | | <view class="card-header"> |
| | | <view class="header-icon"> |
| | | <up-icon name="file-text" |
| | | size="20" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="header-title">{{ detailData.productName || '-' }}</text> |
| | | <view class="status-tags"> |
| | | <u-tag :type="getTagType(detailData.checkResult)" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.checkResult || '-' }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(detailData.inspectState)" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.inspectState ? 'å·²æäº¤' : 'æªæäº¤' }} |
| | | </u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-content"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµæ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(detailData.checkTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç产工åå·</text> |
| | | <text class="detail-value">{{ detailData.workOrderNo || '-' }}</text> |
| | | </view> |
| | | <!-- <view class="detail-row"> |
| | | <text class="detail-label">å·¥åº</text> |
| | | <text class="detail-value">{{ detailData.process || '-' }}</text> |
| | | </view> --> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£éªå</text> |
| | | <text class="detail-value">{{ detailData.checkName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产ååç§°</text> |
| | | <text class="detail-value">{{ detailData.productName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | | <text class="detail-value">{{ detailData.model || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åä½</text> |
| | | <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> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | | <text class="detail-value">{{ detailData.checkCompany || '-' }}</text> |
| | | </view> |
| | | <!-- <view class="detail-row"> |
| | | <text class="detail-label">æ£éªæ å</text> |
| | | <text class="detail-value">{{ detailData.testStandardName || '-' }}</text> |
| | | </view> --> |
| | | </view> |
| | | </view> |
| | | <!-- æ£éªé¡¹ç® --> |
| | | <view class="detail-card" |
| | | v-if="inspectionItems.length > 0"> |
| | | <view class="card-header"> |
| | | <view class="header-icon secondary"> |
| | | <up-icon name="list" |
| | | size="20" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="header-title">æ£éªé¡¹ç®</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="inspection-items"> |
| | | <view v-for="(item, index) in inspectionItems" |
| | | :key="index" |
| | | class="inspection-item"> |
| | | <text class="item-name">{{ item.parameterItem || 'æ£éªé¡¹ç®' }}</text> |
| | | <view class="item-details"> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">åä½:</text> |
| | | <text class="item-detail-value">{{ item.unit || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">æ åå¼:</text> |
| | | <text class="item-detail-value">{{ item.standardValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">å
æ§å¼:</text> |
| | | <text class="item-detail-value">{{ item.controlValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">æ£éªå¼:</text> |
| | | <text class="item-detail-value result" |
| | | :class="getResultClass(item.testValue, item.standardValue)"> |
| | | {{ item.testValue || '-' }} |
| | | </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <!-- <view class="action-buttons"> |
| | | <u-button type="primary" |
| | | class="action-btn" |
| | | @click="downloadReport"> |
| | | ä¸è½½æ¥å |
| | | </u-button> |
| | | </view> --> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <up-empty mode="data" |
| | | text="ææ æ£éªè¯¦æ
"></up-empty> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { qualityInspectParamInfo } from "@/api/qualityManagement/materialInspection.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 详æ
æ°æ® |
| | | const detailData = ref(null); |
| | | // æ£éªé¡¹ç® |
| | | const inspectionItems = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = date => { |
| | | if (!date) return ""; |
| | | return dayjs(date).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = result => { |
| | | switch (result) { |
| | | case "åæ ¼": |
| | | return "success"; |
| | | case "ä¸åæ ¼": |
| | | return "error"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | const getStateTagType = state => { |
| | | return state ? "success" : "warning"; |
| | | }; |
| | | |
| | | // è·åç»ææ ·å¼ |
| | | const getResultClass = (testValue, standardValue) => { |
| | | // ç®åçç»æå¤æé»è¾ï¼å®é
项ç®ä¸å¯è½éè¦æ´å¤æç夿 |
| | | if (testValue === "åæ ¼") { |
| | | return "result-passed"; |
| | | } else if (testValue === "ä¸åæ ¼") { |
| | | return "result-rejected"; |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | // ä¸è½½æ¥å |
| | | const downloadReport = () => { |
| | | uni.showToast({ |
| | | title: "æ¥åä¸è½½ä¸...", |
| | | icon: "loading", |
| | | }); |
| | | |
| | | // 模æä¸è½½ |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: "æ¥åä¸è½½æå", |
| | | icon: "success", |
| | | }); |
| | | }, 1500); |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | return currentPage.options.id; |
| | | }; |
| | | |
| | | // è·å详æ
æ°æ® |
| | | const getDetail = () => { |
| | | const id = getPageId(); |
| | | if (!id) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | |
| | | // 仿¬å°åå¨è·å详æ
æ°æ® |
| | | try { |
| | | const detailDataFromStorage = uni.getStorageSync("finalInspectionEditData"); |
| | | if (detailDataFromStorage) { |
| | | detailData.value = detailDataFromStorage; |
| | | console.log(detailData.value, "detailData.value"); |
| | | // 使ç¨qualityInspectParamInfoè·åæ£éªé¡¹ç® |
| | | qualityInspectParamInfo(id) |
| | | .then(res => { |
| | | if (res.data && res.data.length > 0) { |
| | | inspectionItems.value = res.data; |
| | | } else if ( |
| | | detailDataFromStorage.qualityInspectParams && |
| | | detailDataFromStorage.qualityInspectParams.length > 0 |
| | | ) { |
| | | // 妿æ¥å£æ²¡æè¿åæ°æ®ï¼ä½¿ç¨æ¬å°åå¨ä¸çæ°æ® |
| | | inspectionItems.value = detailDataFromStorage.qualityInspectParams; |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ£éªé¡¹ç®å¤±è´¥:", error); |
| | | // æ¥å£è°ç¨å¤±è´¥æ¶ï¼ä½¿ç¨æ¬å°åå¨ä¸çæ°æ®ææ¨¡ææ°æ® |
| | | if ( |
| | | detailDataFromStorage.qualityInspectParams && |
| | | detailDataFromStorage.qualityInspectParams.length > 0 |
| | | ) { |
| | | inspectionItems.value = detailDataFromStorage.qualityInspectParams; |
| | | } |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("å 载详æ
æ°æ®å¤±è´¥:", error); |
| | | showToast("å 载详æ
æ°æ®å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | onShow(() => { |
| | | getDetail(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | getDetail(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .material-inspection-detail { |
| | | min-height: 100vh; |
| | | background: #f5f5f5; |
| | | padding-bottom: 20px; |
| | | } |
| | | |
| | | .detail-section { |
| | | padding: 15px; |
| | | } |
| | | |
| | | .detail-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | margin-bottom: 12px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .header-icon { |
| | | width: 40px; |
| | | height: 40px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 12px; |
| | | } |
| | | |
| | | .header-icon.secondary { |
| | | background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); |
| | | } |
| | | |
| | | .header-icon.tertiary { |
| | | background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); |
| | | } |
| | | |
| | | .header-title { |
| | | flex: 1; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .status-tag { |
| | | margin-left: 20rpx; |
| | | } |
| | | |
| | | .detail-content { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 10px 0; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .detail-row:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | min-width: 100px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | text-align: right; |
| | | flex: 1; |
| | | } |
| | | |
| | | // æ£éªé¡¹ç® |
| | | .inspection-items { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .inspection-item { |
| | | padding: 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .inspection-item:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .item-name { |
| | | display: block; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 8px; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .item-details { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .item-detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 6px; |
| | | } |
| | | |
| | | .item-detail-row:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .item-detail-label { |
| | | font-size: 12px; |
| | | color: #666; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .item-detail-value { |
| | | font-size: 12px; |
| | | color: #333; |
| | | text-align: right; |
| | | flex: 1; |
| | | } |
| | | |
| | | .item-detail-value.result { |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .result-passed { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .result-rejected { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | // ç©ºç¶æ |
| | | .no-data { |
| | | padding: 60px 20px; |
| | | text-align: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="file-list-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader title="é件管ç" |
| | | @back="goBack" /> |
| | | <!-- éä»¶å表 --> |
| | | <view class="file-list-container"> |
| | | <view v-if="fileList.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in fileList" |
| | | :key="file.id || index" |
| | | class="file-item"> |
| | | <!-- æä»¶å¾æ --> |
| | | <!-- <view class="file-icon" |
| | | :class="getFileIconClass(file.fileType)"> |
| | | <up-icon :name="getFileIcon(file.fileType)" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | </view> --> |
| | | <!-- æä»¶ä¿¡æ¯ --> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.name }}</text> |
| | | <!-- <text class="file-meta">{{ formatFileSize(file.fileSize) }} · {{ file.uploadTime || file.createTime }}</text> --> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="file-actions"> |
| | | <!-- <u-button size="small" |
| | | type="primary" |
| | | plain |
| | | @click="previewFile(file)">é¢è§</u-button> --> |
| | | <u-button size="small" |
| | | type="info" |
| | | plain |
| | | @click="downloadFile(file)">ä¸è½½å¹¶é¢è§</u-button> |
| | | <u-button size="small" |
| | | type="error" |
| | | plain |
| | | @click="confirmDelete(file, index)">å é¤</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- ç©ºç¶æ --> |
| | | <view v-else |
| | | class="empty-state"> |
| | | <up-icon name="document" |
| | | size="64" |
| | | color="#c0c4cc" /> |
| | | <text class="empty-text">ææ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | <!-- <a rel="nofollow" |
| | | id="downloadLink" |
| | | href="#" |
| | | style="display:none;">ä¸è½½ææ¬æä»¶</a> --> |
| | | <!-- ä¸ä¼ æé® --> |
| | | <view class="upload-button" |
| | | @click="chooseFile"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | <text class="upload-text">ä¸ä¼ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | // import { saveAs } from "file-saver"; |
| | | import { |
| | | listRuleFiles, |
| | | delRuleFile, |
| | | } from "@/api/managementMeetings/rulesRegulationsManagement"; |
| | | import { |
| | | qualityInspectFileAdd, |
| | | qualityInspectFileListPage, |
| | | qualityInspectFileDel, |
| | | } from "@/api/qualityManagement/materialInspection"; |
| | | import { blobValidate } from "@/utils/ruoyi"; |
| | | |
| | | // éä»¶å表 |
| | | const fileList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // const request = axios.create({ |
| | | // baseURL: "URL.com", |
| | | // adapter: axiosAdapterUniapp, |
| | | // }); |
| | | // è·åæä»¶å¾æ |
| | | const getFileIcon = fileType => { |
| | | const iconMap = { |
| | | doc: "document", |
| | | docx: "document", |
| | | xls: "grid", |
| | | xlsx: "grid", |
| | | pdf: "document", |
| | | ppt: "copy", |
| | | pptx: "copy", |
| | | txt: "document", |
| | | jpg: "image", |
| | | jpeg: "image", |
| | | png: "image", |
| | | gif: "image", |
| | | zip: "folder", |
| | | rar: "folder", |
| | | }; |
| | | return iconMap[fileType.toLowerCase()] || "document"; |
| | | }; |
| | | |
| | | // è·åæä»¶å¾æ æ ·å¼ç±» |
| | | const getFileIconClass = fileType => { |
| | | const colorMap = { |
| | | doc: "blue", |
| | | docx: "blue", |
| | | xls: "green", |
| | | xlsx: "green", |
| | | pdf: "red", |
| | | ppt: "orange", |
| | | pptx: "orange", |
| | | txt: "gray", |
| | | jpg: "purple", |
| | | jpeg: "purple", |
| | | png: "purple", |
| | | gif: "purple", |
| | | zip: "yellow", |
| | | rar: "yellow", |
| | | }; |
| | | return colorMap[fileType.toLowerCase()] || "gray"; |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = bytes => { |
| | | if (bytes === 0) return "0 B"; |
| | | const k = 1024; |
| | | const sizes = ["B", "KB", "MB", "GB"]; |
| | | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| | | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; |
| | | }; |
| | | |
| | | // éæ©æä»¶ |
| | | const chooseFile = () => { |
| | | uni.chooseImage({ |
| | | count: 9, |
| | | sizeType: ["original", "compressed"], |
| | | sourceType: ["album", "camera"], |
| | | success: res => { |
| | | console.log(res, "éæ©å¾çæå"); |
| | | uploadFiles(res.tempFiles); |
| | | }, |
| | | fail: err => { |
| | | console.error("éæ©å¾ç失败:", err); |
| | | showToast("éæ©æä»¶å¤±è´¥"); |
| | | }, |
| | | }); |
| | | // uni.chooseFile({ |
| | | // count: 9, |
| | | // extension: [ |
| | | // ".doc", |
| | | // ".docx", |
| | | // ".xls", |
| | | // ".xlsx", |
| | | // ".pdf", |
| | | // ".ppt", |
| | | // ".pptx", |
| | | // ".txt", |
| | | // ".jpg", |
| | | // ".jpeg", |
| | | // ".png", |
| | | // ".gif", |
| | | // ".zip", |
| | | // ".rar", |
| | | // ], |
| | | // success: res => { |
| | | // console.log(res, "éæ©æä»¶æå"); |
| | | // uploadFiles(res.tempFiles); |
| | | // }, |
| | | // fail: err => { |
| | | // showToast("éæ©æä»¶å¤±è´¥"); |
| | | // }, |
| | | // }); |
| | | }; |
| | | |
| | | // ä¸ä¼ æä»¶ |
| | | const uploadFiles = tempFiles => { |
| | | console.log(tempFiles, "ä¸ä¼ æä»¶1"); |
| | | tempFiles.forEach((tempFile, index) => { |
| | | // æ¾ç¤ºä¸ä¼ ä¸æç¤º |
| | | uni.showLoading({ |
| | | title: "ä¸ä¼ ä¸...", |
| | | mask: true, |
| | | }); |
| | | console.log(tempFile, "ä¸ä¼ æä»¶2"); |
| | | // 1. ç´æ¥ä½¿ç¨ uni.uploadFile ä¸ä¼ æä»¶ |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + "/file/upload", |
| | | filePath: tempFile.path, |
| | | name: "file", |
| | | header: { |
| | | Authorization: "Bearer " + getToken(), |
| | | }, |
| | | success: uploadRes => { |
| | | uni.hideLoading(); |
| | | console.log(uploadRes, "ä¸ä¼ æä»¶3"); |
| | | |
| | | try { |
| | | const res = JSON.parse(uploadRes.data); |
| | | console.log(res, "ä¸ä¼ æä»¶4"); |
| | | if (res.code === 200) { |
| | | // 2. æåæä»¶ä¿¡æ¯ |
| | | const fileName = tempFile.name |
| | | ? tempFile.name |
| | | : tempFile.path.split("/").pop(); |
| | | // const fileType = fileName.split(".").pop(); |
| | | // 3. æé ä¿åæä»¶ä¿¡æ¯çåæ° |
| | | const saveData = { |
| | | name: fileName, |
| | | inspectId: rulesRegulationsManagementId.value, |
| | | url: res.data.tempPath || "", |
| | | }; |
| | | console.log(saveData, "ä¿åæä»¶ä¿¡æ¯åæ°"); |
| | | // 4. è°ç¨ addRuleFile æ¥å£ä¿åæä»¶ä¿¡æ¯ |
| | | qualityInspectFileAdd(saveData) |
| | | .then(addRes => { |
| | | if (addRes.code === 200) { |
| | | // 5. æ·»å å°æä»¶å表 |
| | | const newFile = { |
| | | ...addRes.data, |
| | | uploadTime: new Date().toLocaleString(), |
| | | }; |
| | | // fileList.value.push(newFile); |
| | | getFileList(); |
| | | showToast("ä¸ä¼ æå"); |
| | | } else { |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¿åæä»¶ä¿¡æ¯å¤±è´¥:", err); |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | }); |
| | | } else { |
| | | showToast("æä»¶ä¸ä¼ 失败"); |
| | | } |
| | | } catch (e) { |
| | | console.error("è§£æä¸ä¼ ç»æå¤±è´¥:", e); |
| | | showToast("ä¸ä¼ 失败"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | uni.hideLoading(); |
| | | console.error("ä¸ä¼ 失败:", err); |
| | | showToast("ä¸ä¼ 失败"); |
| | | }, |
| | | }); |
| | | }); |
| | | }; |
| | | // ä¸è½½æä»¶ |
| | | const downloadFile = file => { |
| | | var url = |
| | | config.baseUrl + |
| | | "/common/download?fileName=" + |
| | | encodeURIComponent(file.url) + |
| | | "&delete=true"; |
| | | console.log(url, "url"); |
| | | |
| | | uni |
| | | .downloadFile({ |
| | | url: url, |
| | | responseType: "blob", |
| | | header: { Authorization: "Bearer " + getToken() }, |
| | | }) |
| | | .then(res => { |
| | | let osType = uni.getStorageSync("deviceInfo").osName; |
| | | let filePath = res.tempFilePath; |
| | | if (osType === "ios") { |
| | | uni.openDocument({ |
| | | filePath: filePath, |
| | | showMenu: true, |
| | | success: res => { |
| | | resolve(res); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.openDocument--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.saveFile({ |
| | | tempFilePath: filePath, |
| | | success: fileRes => { |
| | | uni.showToast({ |
| | | icon: "none", |
| | | mask: true, |
| | | title: |
| | | "æä»¶å·²ä¿åï¼Android/data/uni.UNI720216F/apps/__UNI__720216F/" + |
| | | fileRes.savedFilePath, //ä¿åè·¯å¾ |
| | | duration: 3000, |
| | | }); |
| | | setTimeout(() => { |
| | | //æå¼ææ¡£æ¥ç |
| | | uni.openDocument({ |
| | | filePath: fileRes.savedFilePath, |
| | | success: function (res) { |
| | | resolve(fileRes); |
| | | }, |
| | | }); |
| | | }, 3000); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.save--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } |
| | | // const isBlob = blobValidate(res.data); |
| | | // if (isBlob) { |
| | | // const blob = new Blob([res.data], { type: "text/plain" }); |
| | | // const url = URL.createObjectURL(blob); |
| | | // const downloadLink = document.getElementById("downloadLink"); |
| | | // downloadLink.href = url; |
| | | // downloadLink.download = file.name; |
| | | // downloadLink.click(); |
| | | // showToast("ä¸è½½æå"); |
| | | // } else { |
| | | // showToast("ä¸è½½å¤±è´¥"); |
| | | // } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¸è½½å¤±è´¥:", err); |
| | | showToast("ä¸è½½å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // 确认å é¤ |
| | | const confirmDelete = (file, index) => { |
| | | uni.showModal({ |
| | | title: "å é¤ç¡®è®¤", |
| | | content: `ç¡®å®è¦å é¤éä»¶ "${file.name}" åï¼`, |
| | | success: res => { |
| | | if (res.confirm) { |
| | | deleteFile(file.id, index); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const deleteFile = (fileId, index) => { |
| | | uni.showLoading({ |
| | | title: "å é¤ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | qualityInspectFileDel([fileId]) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | // fileList.value.splice(index, 1); |
| | | getFileList(); |
| | | showToast("å 餿å"); |
| | | } else { |
| | | showToast("å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("å é¤å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæç¤º |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | const rulesRegulationsManagementId = ref(""); |
| | | // 页é¢å è½½æ¶ |
| | | onMounted(() => { |
| | | rulesRegulationsManagementId.value = uni.getStorageSync( |
| | | "qualityInspectFileId" |
| | | ); |
| | | // ä» API è·åéä»¶å表 |
| | | getFileList(); |
| | | // 仿¬å°åå¨è·å rulesRegulationsManagementId |
| | | }); |
| | | |
| | | // è·åéä»¶å表 |
| | | const getFileList = () => { |
| | | uni.showLoading({ |
| | | title: "å è½½ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | qualityInspectFileListPage({ |
| | | inspectId: rulesRegulationsManagementId.value, |
| | | current: -1, |
| | | size: -1, |
| | | }) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | fileList.value = res.data.records || []; |
| | | } else { |
| | | showToast("è·åéä»¶å表失败"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("è·åéä»¶å表失败"); |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .file-list-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100rpx; |
| | | } |
| | | |
| | | .file-list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .file-list { |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | } |
| | | |
| | | .file-icon { |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-right: 20rpx; |
| | | |
| | | &.blue { |
| | | background: #409eff; |
| | | } |
| | | |
| | | &.green { |
| | | background: #67c23a; |
| | | } |
| | | |
| | | &.red { |
| | | background: #f56c6c; |
| | | } |
| | | |
| | | &.orange { |
| | | background: #e6a23c; |
| | | } |
| | | |
| | | &.gray { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.purple { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.yellow { |
| | | background: #e6a23c; |
| | | } |
| | | } |
| | | |
| | | .file-info { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .file-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #303133; |
| | | margin-bottom: 8rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-meta { |
| | | display: block; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .file-actions { |
| | | display: flex; |
| | | gap: 12rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 100rpx 0; |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .upload-button { |
| | | position: fixed; |
| | | bottom: 40rpx; |
| | | right: 40rpx; |
| | | width: 130rpx; |
| | | height: 130rpx; |
| | | border-radius: 50%; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .upload-text { |
| | | font-size: 10px; |
| | | color: #ffffff; |
| | | margin-top: 4rpx; |
| | | } |
| | | |
| | | .upload-progress { |
| | | padding: 40rpx 0; |
| | | } |
| | | |
| | | .upload-progress-text { |
| | | display: block; |
| | | text-align: center; |
| | | margin-top: 20rpx; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-page"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="åºåæ£éª" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="请è¾å
¥äº§ååç§°æç´¢" |
| | | v-model="searchForm.productName" |
| | | @search="getList" |
| | | @custom="getList" |
| | | @clear="getList" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | ></up-search> |
| | | </view> |
| | | <!-- ç»è®¡ä¿¡æ¯å¡ç --> |
| | | <!-- <view class="stats-cards"> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ totalCount }}</text> |
| | | <text class="stat-label">æ»æ£éª</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ submittedCount }}</text> |
| | | <text class="stat-label">å·²æäº¤</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ pendingCount }}</text> |
| | | <text class="stat-label">å¾
æäº¤</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ qualifiedCount }}</text> |
| | | <text class="stat-label">å·²åæ ¼</text> |
| | | </view> |
| | | </view> --> |
| | | <!-- æ£éªå表 --> |
| | | <view class="inspection-list" |
| | | v-if="inspectionList.length > 0"> |
| | | <view v-for="(item, index) in inspectionList" |
| | | :key="index"> |
| | | <view class="inspection-item" |
| | | @click="viewDetail(item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <!-- <view class="material-icon" |
| | | :class="getStateClass(item.inspectState)"> |
| | | <up-icon :name="getStateIcon(item.inspectState)" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> --> |
| | | <view class="material-info"> |
| | | <text class="material-name">{{ item.productName }}</text> |
| | | <text class="material-code">{{ item.model }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="status-tags"> |
| | | <u-tag :type="getTagType(item.checkResult)" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.checkResult }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(item.inspectState)" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.inspectState ? 'å·²æäº¤' : 'æªæäº¤' }} |
| | | </u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµæ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(item.checkTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç产工åå·</text> |
| | | <text class="detail-value">{{ item.workOrderNo || '-' }}</text> |
| | | </view> |
| | | <!-- <view class="detail-row"> |
| | | <text class="detail-label">å·¥åº</text> |
| | | <text class="detail-value">{{ item.process || '-' }}</text> |
| | | </view> --> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£éªå</text> |
| | | <text class="detail-value">{{ item.checkName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | | <text class="detail-value">{{ item.checkCompany || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="action-buttons"> |
| | | <!-- <u-button type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState" |
| | | @click.stop="startInspection(item)"> |
| | | ç¼è¾ |
| | | </u-button> --> |
| | | <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="viewDetail(item)"> |
| | | 详æ
|
| | | </u-button> |
| | | <!-- <u-button type="success" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState" |
| | | @click.stop="submitInspection(item)"> |
| | | æäº¤ |
| | | </u-button> --> |
| | | </view> |
| | | <view class="action-buttons"> |
| | | <!-- <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="viewFileList(item)"> |
| | | éä»¶ |
| | | </u-button> |
| | | <u-button type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState || item.checkName !== ''" |
| | | @click.stop="assignInspector(item)"> |
| | | åé
æ£éªå |
| | | </u-button> --> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <up-empty mode="data" |
| | | text="ææ æ£éªä»»å¡"></up-empty> |
| | | </view> |
| | | <!-- å页ç»ä»¶ --> |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <!-- <view class="fab-button" |
| | | @click="addInspection"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> --> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup v-model:show="showDate" |
| | | mode="date" |
| | | :start-year="2020" |
| | | :end-year="2030" |
| | | :range="true" |
| | | @confirm="confirmDate" /> |
| | | <!-- åé
æ£éªåå¼¹çª --> |
| | | <up-popup v-model:show="showAssignDialog" |
| | | mode="center" |
| | | round |
| | | style="width: 80%"> |
| | | <view class="assign-dialog"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">åé
æ£éªå</text> |
| | | <up-icon name="close" |
| | | size="20" |
| | | color="#999" |
| | | @click="showAssignDialog = false"></up-icon> |
| | | </view> |
| | | <view class="dialog-content"> |
| | | <up-form-item label="æ£éªå" |
| | | prop="checkName" |
| | | :label-width="60" |
| | | required> |
| | | <up-input v-model="assignForm.checkName" |
| | | placeholder="è¯·éæ©æ£éªå" |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon @click="showInspectorSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | | <view class="dialog-footer"> |
| | | <u-button type="default" |
| | | class="footer-btn" |
| | | @click="showAssignDialog = false"> |
| | | åæ¶ |
| | | </u-button> |
| | | <u-button type="primary" |
| | | class="footer-btn" |
| | | @click="submitAssign"> |
| | | ç¡®å® |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | <!-- æ£éªåéæ© --> |
| | | <up-action-sheet :show="showInspectorSheet" |
| | | :actions="userSheetOptions" |
| | | @select="selectInspector" |
| | | title="éæ©æ£éªå" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | submitQualityInspect, |
| | | qualityInspectUpdate, |
| | | qualityInspectListPage, |
| | | } from "@/api/qualityManagement/materialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = ref({ |
| | | productName: "", |
| | | entryDate: undefined, |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }); |
| | | |
| | | // æ¥æéæ©å¨ |
| | | const showDate = ref(false); |
| | | |
| | | // åé
æ£éªåå¼¹çª |
| | | const showAssignDialog = ref(false); |
| | | const showInspectorSheet = ref(false); |
| | | const assignForm = ref({ |
| | | checkName: "", |
| | | }); |
| | | const currentAssignRow = ref(null); |
| | | |
| | | // æ£éªåè¡¨æ°æ® |
| | | const inspectionList = ref([]); |
| | | |
| | | // åé¡µæ°æ® |
| | | const page = ref({ |
| | | current: -1, |
| | | size: -1, |
| | | total: 0, |
| | | }); |
| | | |
| | | // å è½½ç¶æ |
| | | const tableLoading = ref(false); |
| | | |
| | | // ç»è®¡æ°æ® |
| | | const totalCount = ref(0); |
| | | const submittedCount = ref(0); |
| | | const pendingCount = ref(0); |
| | | const qualifiedCount = ref(0); |
| | | |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | |
| | | // ActionSheeté项 |
| | | const userSheetOptions = computed(() => { |
| | | return userList.value.map(item => ({ |
| | | name: item.nickName, |
| | | value: item.nickName, |
| | | })); |
| | | }); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // è·åç¶ææ ·å¼ |
| | | const getStateClass = inspectState => { |
| | | return inspectState ? "state-submitted" : "state-pending"; |
| | | }; |
| | | |
| | | // è·åç¶æå¾æ |
| | | const getStateIcon = inspectState => { |
| | | return inspectState ? "checkmark-circle" : "time"; |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = checkResult => { |
| | | if (checkResult === "åæ ¼") return "success"; |
| | | if (checkResult === "ä¸åæ ¼") return "error"; |
| | | return "default"; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | const getStateTagType = inspectState => { |
| | | return inspectState ? "success" : "warning"; |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const confirmDate = e => { |
| | | searchForm.value.entryDate = e.value; |
| | | searchForm.value.entryDateStart = dayjs(e.value[0]).format("YYYY-MM-DD"); |
| | | searchForm.value.entryDateEnd = dayjs(e.value[1]).format("YYYY-MM-DD"); |
| | | getList(); |
| | | }; |
| | | const viewFileList = item => { |
| | | uni.setStorageSync("qualityInspectFileId", item.id); |
| | | uni.navigateTo({ |
| | | url: "/pages/qualityManagement/finalInspection/fileList", |
| | | }); |
| | | }; |
| | | |
| | | // æ¸
餿¥æèå´ |
| | | const clearDateRange = () => { |
| | | searchForm.value.entryDate = undefined; |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | getList(); |
| | | }; |
| | | |
| | | // è·åç¨æ·å表 |
| | | const getUserList = async () => { |
| | | try { |
| | | const userRes = await userListNoPage(); |
| | | userList.value = userRes.data || []; |
| | | } catch (e) { |
| | | console.error("å è½½æ£éªåå表失败", e); |
| | | userList.value = []; |
| | | } |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page.value }; |
| | | params.entryDate = undefined; |
| | | qualityInspectListPage({ ...params, inspectType: 2 }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | inspectionList.value = res.data.records || []; |
| | | page.value.total = res.data.total || 0; |
| | | totalCount.value = res.data.total || 0; |
| | | submittedCount.value = inspectionList.value.filter( |
| | | item => item.inspectState |
| | | ).length; |
| | | pendingCount.value = inspectionList.value.filter( |
| | | item => !item.inspectState |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter( |
| | | item => item.checkResult === "åæ ¼" |
| | | ).length; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | console.error("è·åå表失败:", err); |
| | | showToast("è·åå表失败ï¼è¯·éè¯"); |
| | | }); |
| | | }; |
| | | |
| | | // ç¼è¾æ£éª |
| | | const startInspection = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | console.log(item, "item"); |
| | | // åå¨å®æ´çæ£éªæ°æ® |
| | | uni.setStorageSync("finalInspectionEditData", item); |
| | | // 跳转å°ç¼è¾é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/finalInspection/add?id=${item.id}&isEdit=true`, |
| | | }); |
| | | }; |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetail = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | uni.setStorageSync("finalInspectionEditData", item); |
| | | // 跳转å°è¯¦æ
é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/finalInspection/detail?id=${item.id}`, |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢æ£éª |
| | | const addInspection = () => { |
| | | uni.navigateTo({ |
| | | url: "/pages/qualityManagement/finalInspection/add", |
| | | }); |
| | | }; |
| | | |
| | | // æäº¤æ£éª |
| | | const submitInspection = async item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | try { |
| | | const res = await submitQualityInspect({ id: item.id }); |
| | | if (res.code === 200) { |
| | | showToast("æäº¤æå"); |
| | | setTimeout(() => { |
| | | getList(); |
| | | }, 1000); |
| | | } else { |
| | | showToast("æäº¤å¤±è´¥ï¼" + (res.msg || "æªç¥é误")); |
| | | } |
| | | } catch (error) { |
| | | console.error("æäº¤å¤±è´¥:", error); |
| | | showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | // åé
æ£éªå |
| | | const assignInspector = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | currentAssignRow.value = item; |
| | | getUserList(); |
| | | showAssignDialog.value = true; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | | const selectInspector = e => { |
| | | assignForm.value.checkName = e.value; |
| | | showInspectorSheet.value = false; |
| | | }; |
| | | |
| | | // æäº¤åé
|
| | | const submitAssign = async () => { |
| | | if (!currentAssignRow.value || !assignForm.value.checkName) { |
| | | showToast("è¯·éæ©æ£éªå"); |
| | | return; |
| | | } |
| | | try { |
| | | const data = { |
| | | ...assignForm.value, |
| | | id: currentAssignRow.value.id, |
| | | }; |
| | | const res = await qualityInspectUpdate(data); |
| | | if (res.code === 200) { |
| | | showToast("åé
æå"); |
| | | showAssignDialog.value = false; |
| | | setTimeout(() => { |
| | | getList(); |
| | | }, 1000); |
| | | } else { |
| | | showToast("åé
失败ï¼" + (res.msg || "æªç¥é误")); |
| | | } |
| | | } catch (error) { |
| | | console.error("åé
失败:", error); |
| | | showToast("åé
失败ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | // å¤çå页 |
| | | const handlePagination = obj => { |
| | | page.value.current = obj.current; |
| | | page.value.size = obj.size; |
| | | getList(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | getUserList(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .material-inspection-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | // æç´¢åºå |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | // ç»è®¡å¡ç |
| | | .stats-cards { |
| | | display: flex; |
| | | padding: 15px; |
| | | gap: 10px; |
| | | background: #fff; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .stat-card { |
| | | flex: 1; |
| | | background: #2979ff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | text-align: center; |
| | | color: #fff; |
| | | box-shadow: 0 2px 8px rgba(41, 121, 255, 0.2); |
| | | } |
| | | |
| | | .stat-number { |
| | | display: block; |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 12px; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | // æ£éªå表 |
| | | .inspection-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .inspection-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | |
| | | &:active { |
| | | transform: scale(0.98); |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 16px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .material-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .state-pending { |
| | | background: #ff9900; |
| | | } |
| | | |
| | | .state-submitted { |
| | | background: #52c41a; |
| | | } |
| | | |
| | | .material-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .material-name { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .material-code { |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-left: 8px; |
| | | } |
| | | |
| | | .status-tags { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .status-tag { |
| | | margin: 0; |
| | | } |
| | | |
| | | .date-range { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-top: 10px; |
| | | padding: 8px 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .date-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | // 详æ
è¡ |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | |
| | | // æä½æé® |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 12px; |
| | | padding: 0 0 16px 0; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .action-btn { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | // ç©ºç¶æ |
| | | .no-data { |
| | | padding: 60px 20px; |
| | | text-align: center; |
| | | } |
| | | |
| | | // æµ®å¨æé® |
| | | .fab-button { |
| | | position: fixed; |
| | | bottom: 20px; |
| | | right: 20px; |
| | | width: 56px; |
| | | height: 56px; |
| | | background: #2979ff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | // åé
æ£éªåå¼¹çª |
| | | .assign-dialog { |
| | | padding: 24px; |
| | | background: #ffffff; |
| | | border-radius: 16px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .dialog-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 24px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-content { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | gap: 16px; |
| | | padding-top: 16px; |
| | | border-top: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .footer-btn { |
| | | flex: 1; |
| | | height: 44px; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | // è¾å
¥æ¡æ ·å¼ |
| | | :deep(.up-input__inner) { |
| | | border-radius: 8px; |
| | | height: 44px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | // 表åé¡¹æ ·å¼ |
| | | :deep(.up-form-item) { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | :deep(.up-form-item__label) { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | // æé®æ ·å¼ |
| | | :deep(.up-button--primary) { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | :deep(.up-button--default) { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | // å页ç»ä»¶ |
| | | .pagination { |
| | | padding: 20px; |
| | | background: #fff; |
| | | margin-top: 10px; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-add"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="isEdit ? 'ç¼è¾åæææ£éª' : 'æ°å¢åæææ£éª'" |
| | | @back="goBack" /> |
| | | <!-- 表åå
容 --> |
| | | <up-form :model="form" |
| | | ref="formRef" |
| | | label-width="110" |
| | | :rules="rules"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <up-form-item label="ä¾åºå" |
| | | prop="supplier" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.supplier" |
| | | placeholder="è¯·éæ©ä¾åºå" |
| | | readonly |
| | | :disabled="supplierQuantityDisabled" /> |
| | | <template #right> |
| | | <up-icon @click="showSupplierSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="产ååç§°" |
| | | prop="productId" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.productName" |
| | | placeholder="è¯·éæ©äº§å" |
| | | readonly |
| | | @click="showProductTree = true" |
| | | :disabled="isEdit" /> |
| | | <template #right> |
| | | <up-icon @click="showProductTree = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="è§æ ¼åå·" |
| | | prop="productModelId" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.model" |
| | | placeholder="è¯·éæ©è§æ ¼åå·" |
| | | readonly |
| | | :disabled="isEdit" /> |
| | | <template #right> |
| | | <up-icon @click="showModelSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="ææ éæ©" |
| | | prop="testStandardId" |
| | | border-bottom> |
| | | <up-input v-model="testStandardDisplay" |
| | | placeholder="è¯·éæ©ææ " |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon @click="openTestStandardSheet" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="åä½" |
| | | prop="unit" |
| | | border-bottom> |
| | | <up-input v-model="form.unit" |
| | | placeholder="请è¾å
¥åä½" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="æ°é" |
| | | prop="quantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ°é" |
| | | :disabled="supplierQuantityDisabled" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµåä½" |
| | | prop="checkCompany" |
| | | border-bottom> |
| | | <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" |
| | | border-bottom> |
| | | <up-input v-model="form.checkName" |
| | | placeholder="è¯·éæ©æ£éªå" |
| | | readonly |
| | | @click="showInspectorSheet" /> |
| | | <template #right> |
| | | <up-icon @click="showInspectorSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµæ¥æ" |
| | | prop="checkTime" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.checkTime" |
| | | placeholder="è¯·éæ©æ£æµæ¥æ" |
| | | readonly /> |
| | | <!-- <template #right> |
| | | <up-icon name="calendar" |
| | | @click="showDatePicker"></up-icon> |
| | | </template> --> |
| | | </up-form-item> |
| | | <!-- <up-form-item label="éè´è®¢åå·" |
| | | prop="purchaseContractNo" |
| | | border-bottom> |
| | | <up-input v-model="form.purchaseContractNo" |
| | | placeholder="请è¾å
¥éè´è®¢åå·" |
| | | clearable /> |
| | | </up-form-item> --> |
| | | <!-- æ£éªé¡¹ç® --> |
| | | <view class="inspection-items-container"> |
| | | <view class="steps-header"> |
| | | <text class="steps-title">æ£éªé¡¹ç®</text> |
| | | <text class="steps-count">å
± {{ tableData.length }} 个项ç®</text> |
| | | </view> |
| | | <view class="steps-list"> |
| | | <view v-for="(item, index) in tableData" |
| | | :key="index" |
| | | class="exec-step-item"> |
| | | <view class="step-number"> |
| | | {{ index + 1 }} |
| | | </view> |
| | | <view class="step-content"> |
| | | <view class="step-row"> |
| | | <text class="step-label">ææ ï¼</text> |
| | | <text class="step-value">{{ item.parameterItem }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">åä½ï¼</text> |
| | | <text class="step-value">{{ item.unit }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">æ åå¼ï¼</text> |
| | | <text class="step-value">{{ item.standardValue }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">å
æ§å¼ï¼</text> |
| | | <text class="step-value">{{ item.controlValue }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">æ£éªå¼ï¼</text> |
| | | <up-input v-model="item.testValue" |
| | | placeholder="请è¾å
¥æ£éªå¼" |
| | | clearable |
| | | border-bottom |
| | | class="step-input" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="tableData.length === 0" |
| | | class="empty-data"> |
| | | <text>请å
éæ©ææ </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-form> |
| | | <!-- åºé¨æé® --> |
| | | <view class="bottom-buttons"> |
| | | <up-button type="default" |
| | | size="default" |
| | | @click="goBack" |
| | | class="bottom-btn"> |
| | | åæ¶ |
| | | </up-button> |
| | | <up-button type="primary" |
| | | size="default" |
| | | @click="submitForm" |
| | | :loading="loading" |
| | | class="bottom-btn"> |
| | | {{ isEdit ? 'ä¿å' : 'æäº¤' }} |
| | | </up-button> |
| | | </view> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup v-model:show="showDate" |
| | | mode="date" |
| | | :start-year="2020" |
| | | :end-year="2030" |
| | | @confirm="confirmDate" /> |
| | | <!-- ä¾åºåéæ© --> |
| | | <up-action-sheet :show="showSupplierSheet" |
| | | :actions="supplierOptions" |
| | | @select="selectSupplier" |
| | | @close="showSupplierSheet = false" |
| | | title="éæ©ä¾åºå" /> |
| | | <!-- 产åéæ© --> |
| | | <up-action-sheet :show="showProductSheet" |
| | | :actions="productSheetOptions" |
| | | @select="selectProduct" |
| | | @close="showProductSheet = false" |
| | | title="éæ©äº§å" /> |
| | | <!-- è§æ ¼åå·éæ© --> |
| | | <up-action-sheet :show="showModelSheet" |
| | | :actions="modelSheetOptions" |
| | | @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" |
| | | @select="selectInspector" |
| | | @close="showInspectorSheet = false" |
| | | title="éæ©æ£éªå" /> |
| | | <!-- ææ éæ© --> |
| | | <up-action-sheet :show="showTestStandardSheet" |
| | | :actions="testStandardSheetOptions" |
| | | @select="selectTestStandard" |
| | | @close="showTestStandardSheet = false" |
| | | title="éæ©ææ " /> |
| | | <!-- äº§åæ å½¢éæ©å¨ --> |
| | | <up-popup v-model:show="showProductTree" |
| | | position="bottom" |
| | | :round="true" |
| | | :closeable="true" |
| | | @close="showProductTree = false"> |
| | | <view class="tree-selector"> |
| | | <view class="tree-header"> |
| | | <text class="tree-title">éæ©äº§å</text> |
| | | </view> |
| | | <view class="tree-content"> |
| | | <view class="tree-node" |
| | | v-for="(node, index) in productOptions" |
| | | :key="index"> |
| | | <view v-if="node.children && node.children.length > 0" |
| | | class="tree-node-header" |
| | | @click="toggleNode(node)"> |
| | | <up-icon :name="node.expanded ? 'arrow-down' : 'arrow-right'" |
| | | class="tree-node-icon" /> |
| | | <text class="tree-node-label">{{ node.label }}</text> |
| | | </view> |
| | | <view v-else |
| | | class="tree-node-header" |
| | | @click="selectTreeNode(node)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ node.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == node.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | <view v-if="node.children && node.children.length > 0 && node.expanded" |
| | | class="tree-node-children"> |
| | | <view class="tree-node" |
| | | v-for="(child, childIndex) in node.children" |
| | | :key="childIndex"> |
| | | <view v-if="child.children && child.children.length > 0" |
| | | class="tree-node-header" |
| | | @click="toggleNode(child)"> |
| | | <up-icon :name="child.expanded ? 'arrow-down' : 'arrow-right'" |
| | | class="tree-node-icon" /> |
| | | <text class="tree-node-label">{{ child.label }}</text> |
| | | </view> |
| | | <view v-else |
| | | class="tree-node-header" |
| | | @click="selectTreeNode(child)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ child.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == child.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | <view v-if="child.children && child.children.length > 0 && child.expanded" |
| | | class="tree-node-children"> |
| | | <view class="tree-node" |
| | | v-for="(grandchild, grandchildIndex) in child.children" |
| | | :key="grandchildIndex"> |
| | | <view class="tree-node-header" |
| | | @click="selectTreeNode(grandchild)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ grandchild.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == grandchild.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, nextTick } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { |
| | | qualityInspectAdd, |
| | | qualityInspectUpdate, |
| | | qualityInspectParamInfo, |
| | | qualityInspectDetailByProductId, |
| | | getQualityTestStandardParamByTestStandardId, |
| | | } from "@/api/qualityManagement/materialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | // å è½½ç¶æ |
| | | const loading = ref(false); |
| | | // æ¥æéæ©å¨ |
| | | const showDate = ref(false); |
| | | // ä¾åºåéæ© |
| | | const showSupplierSheet = ref(false); |
| | | // 产åéæ© |
| | | const showProductSheet = ref(false); |
| | | // äº§åæ å½¢éæ©å¨ |
| | | const showProductTree = ref(false); |
| | | // è§æ ¼åå·éæ© |
| | | const showModelSheet = ref(false); |
| | | // æ£æµç»æéæ© |
| | | const showResultSheet = ref(false); |
| | | // æ£éªåéæ© |
| | | const showInspectorSheet = ref(false); |
| | | // ææ éæ© |
| | | const showTestStandardSheet = ref(false); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const form = ref({ |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | supplier: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }); |
| | | |
| | | // æ¾ç¤ºç¨çåé |
| | | const testStandardDisplay = ref(""); |
| | | |
| | | // æ£éªé¡¹ç® |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // ä¾åºåå表 |
| | | const supplierList = ref([]); |
| | | // 产åé项 |
| | | const productOptions = ref([]); |
| | | // åå·é项 |
| | | const modelOptions = ref([]); |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | // æ£æµç»æé项 |
| | | const resultOptions = ref([ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]); |
| | | // ææ é项 |
| | | const testStandardOptions = ref([]); |
| | | // å½å产åID |
| | | const currentProductId = ref(0); |
| | | |
| | | // ActionSheeté项 |
| | | const supplierOptions = computed(() => { |
| | | return supplierList.value.map(item => ({ |
| | | name: item.supplierName, |
| | | value: item.supplierName, |
| | | })); |
| | | }); |
| | | |
| | | const productSheetOptions = computed(() => { |
| | | return productOptions.value.map(item => ({ |
| | | name: item.label, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | | const modelSheetOptions = computed(() => { |
| | | 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, |
| | | })); |
| | | }); |
| | | |
| | | const userSheetOptions = computed(() => { |
| | | return userList.value.map(item => ({ |
| | | name: item.nickName, |
| | | value: item.nickName, |
| | | })); |
| | | }); |
| | | |
| | | const testStandardSheetOptions = computed(() => { |
| | | return testStandardOptions.value.map(item => ({ |
| | | name: item.standardName || item.standardNo, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | supplier: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [ |
| | | { required: true, message: "è¯·éæ©äº§ååå·", trigger: "change" }, |
| | | ], |
| | | testStandardId: [ |
| | | { required: false, message: "è¯·éæ©ææ ", trigger: "change" }, |
| | | ], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [ |
| | | { required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // æ¯å¦ä¸ºç¼è¾æ¨¡å¼ |
| | | const isEdit = computed(() => { |
| | | const id = getPageId(); |
| | | return !!id; |
| | | }); |
| | | |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åä¾åºåãæ°éç½®ç° |
| | | const supplierQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | return currentPage.options.id; |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const confirmDate = e => { |
| | | form.value.checkTime = dayjs(e.value).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // éæ©ä¾åºå |
| | | const selectSupplier = e => { |
| | | form.value.supplier = e.value; |
| | | showSupplierSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©äº§å |
| | | const selectProduct = e => { |
| | | form.value.productId = e.value; |
| | | form.value.productName = e.name; |
| | | showProductSheet.value = false; |
| | | getModels(e.value); |
| | | }; |
| | | |
| | | // 忢æ å½¢èç¹å±å¼/æå |
| | | const toggleNode = node => { |
| | | node.expanded = !node.expanded; |
| | | }; |
| | | |
| | | // éæ©æ å½¢èç¹ |
| | | const selectTreeNode = node => { |
| | | // ç¡®ä¿åªéæ©æ«ç«¯èç¹ |
| | | if (!node.children || node.children.length == 0) { |
| | | form.value.productId = node.value; |
| | | form.value.productName = node.label; |
| | | showProductTree.value = false; |
| | | getModels(node.value); |
| | | } |
| | | }; |
| | | |
| | | // 转æ¢äº§åæ ç»æ |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | |
| | | // æ ¹æ®IDæ¥æ¾èç¹ |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | |
| | | // éæ©è§æ ¼åå· |
| | | const selectModel = e => { |
| | | form.value.productModelId = e.value; |
| | | showModelSheet.value = false; |
| | | handleChangeModel(e.value); |
| | | }; |
| | | |
| | | // å¤çåå·åå |
| | | const handleChangeModel = value => { |
| | | form.value.model = |
| | | 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; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | | const selectInspector = e => { |
| | | form.value.checkName = e.value; |
| | | showInspectorSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©ææ |
| | | const selectTestStandard = e => { |
| | | form.value.testStandardId = e.value; |
| | | testStandardDisplay.value = e.name; |
| | | showTestStandardSheet.value = false; |
| | | handleTestStandardChange(e.value); |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = testStandardId => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | tableData.value = res.data || []; |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ ååæ°å¤±è´¥:", error); |
| | | tableData.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | const openTestStandardSheet = () => { |
| | | console.log("openTestStandardSheet"); |
| | | showTestStandardSheet.value = true; |
| | | }; |
| | | |
| | | // è·åä¾åºåå表 |
| | | const getSuppliers = () => { |
| | | getOptions().then(res => { |
| | | supplierList.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // è·å产åé项 |
| | | const getProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return productOptions.value; |
| | | }); |
| | | }; |
| | | |
| | | // è·åç¨æ·å表 |
| | | const getUserList = async () => { |
| | | try { |
| | | const userRes = await userListNoPage(); |
| | | userList.value = userRes.data || []; |
| | | } catch (e) { |
| | | console.error("å è½½æ£éªåå表失败", e); |
| | | userList.value = []; |
| | | } |
| | | }; |
| | | |
| | | // è·ååå·å表 |
| | | const getModels = value => { |
| | | form.value.productModelId = ""; |
| | | form.value.unit = ""; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value; |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res; |
| | | }); |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | // è·åææ å表 |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 0, |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // ä¿å䏿æ¡éé¡¹æ°æ® |
| | | testStandardOptions.value = res.data || []; |
| | | // æ¸
ç©ºè¡¨æ ¼æ°æ®ï¼çå¾
ç¨æ·éæ©ææ |
| | | tableData.value = []; |
| | | // æ¸
ç©ºææ éæ© |
| | | form.value.testStandardId = ""; |
| | | testStandardDisplay.value = ""; |
| | | }); |
| | | }; |
| | | |
| | | // è·åæ£éªåæ°å表ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const getQualityInspectParamList = id => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | console.log("submitForm", form.value, tableData.value); |
| | | try { |
| | | // await formRef.value.validate(); |
| | | if (!form.value.productModelId) { |
| | | showToast("è¯·éæ©è§æ ¼åå·"); |
| | | return; |
| | | } |
| | | if (!form.value.supplier) { |
| | | showToast("è¯·éæ©ä¾åºå"); |
| | | return; |
| | | } |
| | | if (!form.value.quantity) { |
| | | showToast("请è¾å
¥æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.productId) { |
| | | showToast("è¯·éæ©äº§å"); |
| | | return; |
| | | } |
| | | if (!form.value.checkResult) { |
| | | showToast("è¯·éæ©æ£æµç»æ"); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | |
| | | form.value.inspectType = 0; |
| | | if (isEdit.value) { |
| | | tableData.value.forEach(item => { |
| | | delete item.id; |
| | | }); |
| | | } |
| | | |
| | | const data = { ...form.value, qualityInspectParams: tableData.value }; |
| | | data.quantity = Number(data.quantity); |
| | | if (isEdit.value) { |
| | | const res = await qualityInspectUpdate(data); |
| | | showToast("ä¿åæå"); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | const res = await qualityInspectAdd(data); |
| | | showToast("æäº¤æå"); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } |
| | | } catch (error) { |
| | | console.error("表åéªè¯å¤±è´¥:", error); |
| | | showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // åå§å表å |
| | | const initForm = async () => { |
| | | const id = getPageId(); |
| | | if (id) { |
| | | // ç¼è¾æ¨¡å¼ï¼å è½½æ°æ® |
| | | // å
éç½®è¡¨åæ°æ® |
| | | form.value = { |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | supplier: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | // å
ç¡®ä¿äº§åæ å·²å è½½ï¼å¦åç¼è¾æ¶äº§å/è§æ ¼åå·æ æ³åæ¾ |
| | | await getProductOptions(); |
| | | // 仿¬å°åå¨è·åç¼è¾æ°æ® |
| | | const row = uni.getStorageSync("inspectionEditData") || { |
| | | id: id, |
| | | checkTime: "2026-03-03", |
| | | supplier: "䏿µ·é屿ææéå
¬å¸", |
| | | checkName: "å¼ ä¸", |
| | | productName: "ä¸é颿¿æ", |
| | | productId: 1, |
| | | productModelId: 1, |
| | | model: "304", |
| | | testStandardId: "1", |
| | | unit: "kg", |
| | | quantity: 1000, |
| | | checkCompany: "ç¬¬ä¸æ¹æ£æµæºæ", |
| | | checkResult: "åæ ¼", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | form.value = { ...row }; |
| | | currentProductId.value = row.productId || 0; |
| | | // å
³é®ï¼ç¼è¾æ¶å è½½è§æ ¼åå·ä¸æéé¡¹ï¼æè½åæ¾ productModelId |
| | | if (currentProductId.value) { |
| | | try { |
| | | const res = await modelList({ id: currentProductId.value }); |
| | | modelOptions.value = res || []; |
| | | // 忥åå¡« model / unit |
| | | if (form.value.productModelId) { |
| | | handleChangeModel(form.value.productModelId); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½è§æ ¼åå·å¤±è´¥", e); |
| | | modelOptions.value = []; |
| | | } |
| | | } |
| | | // ç¼è¾æ¨¡å¼ä¸ï¼å
å è½½ææ é项ï¼ç¶åå è½½åæ°å表 |
| | | if (currentProductId.value) { |
| | | // å
å è½½ææ é项 |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 0, |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | testStandardOptions.value = res.data || []; |
| | | // ä½¿ç¨ nextTick ç¡®ä¿éé¡¹å·²ç»æ¸²æ |
| | | nextTick(() => { |
| | | // 妿ç¼è¾æ°æ®ä¸æ testStandardIdï¼å设置并å 载对åºçåæ° |
| | | if (savedTestStandardId) { |
| | | // ç¡®ä¿ç±»åå¹é
|
| | | const matchedOption = testStandardOptions.value.find( |
| | | item => |
| | | item.id == savedTestStandardId || |
| | | String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // ç¡®ä¿ä½¿ç¨å¹é
项ç id |
| | | form.value.testStandardId = matchedOption.id; |
| | | testStandardDisplay.value = |
| | | matchedOption.standardName || matchedOption.standardNo; |
| | | // ç¼è¾ä¿ç忣éªå¼ï¼ç´æ¥æåååæ°æ°æ® |
| | | getQualityInspectParamList(row.id); |
| | | } else { |
| | | // 妿æ¾ä¸å°å¹é
项ï¼å°è¯ç´æ¥ä½¿ç¨åå¼ |
| | | console.warn( |
| | | "æªæ¾å°å¹é
çææ é项ï¼testStandardId:", |
| | | savedTestStandardId |
| | | ); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } else { |
| | | // å¦åä½¿ç¨æ§çé»è¾ |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | // å±å¼äº§åæ å°å½åéä¸çèç¹ |
| | | expandProductTree(productOptions.value, row.productId); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼åå§å表å |
| | | form.value = { |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | supplier: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | } |
| | | }; |
| | | |
| | | // å±å¼äº§åæ å°æå®èç¹ |
| | | const expandProductTree = (nodes, targetId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | const node = nodes[i]; |
| | | if (node.value === targetId) { |
| | | return true; // æ¾å°ç®æ èç¹ |
| | | } |
| | | if (node.children && node.children.length > 0) { |
| | | const found = expandProductTree(node.children, targetId); |
| | | if (found) { |
| | | node.expanded = true; // å±å¼ç¶èç¹ |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getSuppliers(); |
| | | getProductOptions(); |
| | | getUserList(); |
| | | initForm(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | initForm(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .material-inspection-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100px; |
| | | } |
| | | |
| | | // æ£éªé¡¹ç®å®¹å¨ |
| | | .inspection-items-container { |
| | | padding: 20px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .steps-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | padding-bottom: 12px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .steps-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .steps-count { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .steps-list { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .exec-step-item { |
| | | position: relative; |
| | | display: flex; |
| | | margin-bottom: 16px; |
| | | padding: 16px; |
| | | background-color: #ffffff; |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .exec-step-item:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); |
| | | border-color: #409eff; |
| | | transform: translateY(-1px); |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: -25rpx; |
| | | right: -25rpx; |
| | | width: 50rpx; |
| | | height: 50rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | text-align: center; |
| | | font-size: 20px; |
| | | border-radius: 50%; |
| | | background-color: red; |
| | | border: none; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .delete-btn:hover { |
| | | transform: scale(1.1); |
| | | box-shadow: 0 3px 6px rgba(245, 108, 108, 0.4); |
| | | } |
| | | |
| | | .step-number { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 32px; |
| | | height: 32px; |
| | | margin-right: 16px; |
| | | background-color: #ecf5ff; |
| | | color: #409eff; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | border-radius: 50%; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .step-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .step-row { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .step-row:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .step-label { |
| | | display: inline-block; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-right: 12px; |
| | | flex-shrink: 0; |
| | | line-height: 36px; |
| | | } |
| | | |
| | | .step-input { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .step-input input { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .add-step-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | height: 44px; |
| | | line-height: 44px; |
| | | font-size: 14px; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .add-step-btn:hover { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3); |
| | | } |
| | | |
| | | .add-step-btn text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | // åºé¨æé® |
| | | .bottom-buttons { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | display: flex; |
| | | padding: 16px 20px; |
| | | background: #ffffff; |
| | | border-top: 1px solid #f0f0f0; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .bottom-btn { |
| | | flex: 1; |
| | | } |
| | | |
| | | // æ 形鿩卿 ·å¼ |
| | | .tree-selector { |
| | | width: 100%; |
| | | max-height: 70vh; |
| | | background: #ffffff; |
| | | border-radius: 16px 16px 0 0; |
| | | } |
| | | |
| | | .tree-header { |
| | | padding: 16px 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | text-align: center; |
| | | } |
| | | |
| | | .tree-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .tree-content { |
| | | padding: 10px 0; |
| | | max-height: calc(70vh - 60px); |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .tree-node { |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | .tree-node-header { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px 0; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .tree-node-icon { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 8px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .tree-node-icon-placeholder { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .tree-node-label { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .tree-node-check { |
| | | width: 20px; |
| | | height: 20px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .tree-node-children { |
| | | margin-left: 28px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="åæææ£éªè¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- 详æ
å
容 --> |
| | | <view class="detail-section" |
| | | v-if="detailData"> |
| | | <view class="detail-card"> |
| | | <view class="card-header"> |
| | | <view class="header-icon"> |
| | | <up-icon name="file-text" |
| | | size="20" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="header-title">{{ detailData.productName || '-' }}</text> |
| | | <view class="status-tags"> |
| | | <u-tag :type="getTagType(detailData.checkResult)" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.checkResult || '-' }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(detailData.inspectState)" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.inspectState ? 'å·²æäº¤' : 'æªæäº¤' }} |
| | | </u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-content"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµæ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(detailData.checkTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">éè´è®¢åå·</text> |
| | | <text class="detail-value">{{ detailData.purchaseContractNo || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¾åºå</text> |
| | | <text class="detail-value">{{ detailData.supplier || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£éªå</text> |
| | | <text class="detail-value">{{ detailData.checkName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产ååç§°</text> |
| | | <text class="detail-value">{{ detailData.productName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | | <text class="detail-value">{{ detailData.model || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åä½</text> |
| | | <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> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | | <text class="detail-value">{{ detailData.checkCompany || '-' }}</text> |
| | | </view> |
| | | <!-- <view class="detail-row"> |
| | | <text class="detail-label">æ£éªæ å</text> |
| | | <text class="detail-value">{{ detailData.testStandardName || '-' }}</text> |
| | | </view> --> |
| | | </view> |
| | | </view> |
| | | <!-- æ£éªé¡¹ç® --> |
| | | <view class="detail-card" |
| | | v-if="inspectionItems.length > 0"> |
| | | <view class="card-header"> |
| | | <view class="header-icon secondary"> |
| | | <up-icon name="list" |
| | | size="20" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="header-title">æ£éªé¡¹ç®</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="inspection-items"> |
| | | <view v-for="(item, index) in inspectionItems" |
| | | :key="index" |
| | | class="inspection-item"> |
| | | <text class="item-name">{{ item.parameterItem || 'æ£éªé¡¹ç®' }}</text> |
| | | <view class="item-details"> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">åä½:</text> |
| | | <text class="item-detail-value">{{ item.unit || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">æ åå¼:</text> |
| | | <text class="item-detail-value">{{ item.standardValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">å
æ§å¼:</text> |
| | | <text class="item-detail-value">{{ item.controlValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">æ£éªå¼:</text> |
| | | <text class="item-detail-value result" |
| | | :class="getResultClass(item.testValue, item.standardValue)"> |
| | | {{ item.testValue || '-' }} |
| | | </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <!-- <view class="action-buttons"> |
| | | <u-button type="primary" |
| | | class="action-btn" |
| | | @click="downloadReport"> |
| | | ä¸è½½æ¥å |
| | | </u-button> |
| | | </view> --> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <up-empty mode="data" |
| | | text="ææ æ£éªè¯¦æ
"></up-empty> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { qualityInspectParamInfo } from "@/api/qualityManagement/materialInspection.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 详æ
æ°æ® |
| | | const detailData = ref(null); |
| | | // æ£éªé¡¹ç® |
| | | const inspectionItems = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = date => { |
| | | if (!date) return ""; |
| | | return dayjs(date).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = result => { |
| | | switch (result) { |
| | | case "åæ ¼": |
| | | return "success"; |
| | | case "ä¸åæ ¼": |
| | | return "error"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | const getStateTagType = state => { |
| | | return state ? "success" : "warning"; |
| | | }; |
| | | |
| | | // è·åç»ææ ·å¼ |
| | | const getResultClass = (testValue, standardValue) => { |
| | | // ç®åçç»æå¤æé»è¾ï¼å®é
项ç®ä¸å¯è½éè¦æ´å¤æç夿 |
| | | if (testValue === "åæ ¼") { |
| | | return "result-passed"; |
| | | } else if (testValue === "ä¸åæ ¼") { |
| | | return "result-rejected"; |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | // ä¸è½½æ¥å |
| | | const downloadReport = () => { |
| | | uni.showToast({ |
| | | title: "æ¥åä¸è½½ä¸...", |
| | | icon: "loading", |
| | | }); |
| | | |
| | | // 模æä¸è½½ |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: "æ¥åä¸è½½æå", |
| | | icon: "success", |
| | | }); |
| | | }, 1500); |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | return currentPage.options.id; |
| | | }; |
| | | |
| | | // è·å详æ
æ°æ® |
| | | const getDetail = () => { |
| | | const id = getPageId(); |
| | | if (!id) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | |
| | | // 仿¬å°åå¨è·å详æ
æ°æ® |
| | | try { |
| | | const detailDataFromStorage = uni.getStorageSync("inspectionEditData"); |
| | | if (detailDataFromStorage) { |
| | | detailData.value = detailDataFromStorage; |
| | | // 使ç¨qualityInspectParamInfoè·åæ£éªé¡¹ç® |
| | | qualityInspectParamInfo(id) |
| | | .then(res => { |
| | | if (res.data && res.data.length > 0) { |
| | | inspectionItems.value = res.data; |
| | | } else if ( |
| | | detailDataFromStorage.qualityInspectParams && |
| | | detailDataFromStorage.qualityInspectParams.length > 0 |
| | | ) { |
| | | // 妿æ¥å£æ²¡æè¿åæ°æ®ï¼ä½¿ç¨æ¬å°åå¨ä¸çæ°æ® |
| | | inspectionItems.value = detailDataFromStorage.qualityInspectParams; |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ£éªé¡¹ç®å¤±è´¥:", error); |
| | | // æ¥å£è°ç¨å¤±è´¥æ¶ï¼ä½¿ç¨æ¬å°åå¨ä¸çæ°æ®ææ¨¡ææ°æ® |
| | | if ( |
| | | detailDataFromStorage.qualityInspectParams && |
| | | detailDataFromStorage.qualityInspectParams.length > 0 |
| | | ) { |
| | | inspectionItems.value = detailDataFromStorage.qualityInspectParams; |
| | | } |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("å 载详æ
æ°æ®å¤±è´¥:", error); |
| | | showToast("å 载详æ
æ°æ®å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | onShow(() => { |
| | | getDetail(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | getDetail(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .material-inspection-detail { |
| | | min-height: 100vh; |
| | | background: #f5f5f5; |
| | | padding-bottom: 20px; |
| | | } |
| | | |
| | | .detail-section { |
| | | padding: 15px; |
| | | } |
| | | |
| | | .detail-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | margin-bottom: 12px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .header-icon { |
| | | width: 40px; |
| | | height: 40px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 12px; |
| | | } |
| | | |
| | | .header-icon.secondary { |
| | | background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); |
| | | } |
| | | |
| | | .header-icon.tertiary { |
| | | background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); |
| | | } |
| | | |
| | | .header-title { |
| | | flex: 1; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .status-tag { |
| | | margin-left: 20rpx; |
| | | } |
| | | |
| | | .detail-content { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 10px 0; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .detail-row:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | min-width: 100px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | text-align: right; |
| | | flex: 1; |
| | | } |
| | | |
| | | // æ£éªé¡¹ç® |
| | | .inspection-items { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .inspection-item { |
| | | padding: 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .inspection-item:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .item-name { |
| | | display: block; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 8px; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .item-details { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .item-detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 6px; |
| | | } |
| | | |
| | | .item-detail-row:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .item-detail-label { |
| | | font-size: 12px; |
| | | color: #666; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .item-detail-value { |
| | | font-size: 12px; |
| | | color: #333; |
| | | text-align: right; |
| | | flex: 1; |
| | | } |
| | | |
| | | .item-detail-value.result { |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .result-passed { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .result-rejected { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | // ç©ºç¶æ |
| | | .no-data { |
| | | padding: 60px 20px; |
| | | text-align: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="file-list-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader title="é件管ç" |
| | | @back="goBack" /> |
| | | <!-- éä»¶å表 --> |
| | | <view class="file-list-container"> |
| | | <view v-if="fileList.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in fileList" |
| | | :key="file.id || index" |
| | | class="file-item"> |
| | | <!-- æä»¶å¾æ --> |
| | | <!-- <view class="file-icon" |
| | | :class="getFileIconClass(file.fileType)"> |
| | | <up-icon :name="getFileIcon(file.fileType)" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | </view> --> |
| | | <!-- æä»¶ä¿¡æ¯ --> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.name }}</text> |
| | | <!-- <text class="file-meta">{{ formatFileSize(file.fileSize) }} · {{ file.uploadTime || file.createTime }}</text> --> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="file-actions"> |
| | | <!-- <u-button size="small" |
| | | type="primary" |
| | | plain |
| | | @click="previewFile(file)">é¢è§</u-button> --> |
| | | <u-button size="small" |
| | | type="info" |
| | | plain |
| | | @click="downloadFile(file)">ä¸è½½å¹¶é¢è§</u-button> |
| | | <u-button size="small" |
| | | type="error" |
| | | plain |
| | | @click="confirmDelete(file, index)">å é¤</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- ç©ºç¶æ --> |
| | | <view v-else |
| | | class="empty-state"> |
| | | <up-icon name="document" |
| | | size="64" |
| | | color="#c0c4cc" /> |
| | | <text class="empty-text">ææ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | <!-- <a rel="nofollow" |
| | | id="downloadLink" |
| | | href="#" |
| | | style="display:none;">ä¸è½½ææ¬æä»¶</a> --> |
| | | <!-- ä¸ä¼ æé® --> |
| | | <view class="upload-button" |
| | | @click="chooseFile"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | <text class="upload-text">ä¸ä¼ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | // import { saveAs } from "file-saver"; |
| | | import { |
| | | listRuleFiles, |
| | | delRuleFile, |
| | | } from "@/api/managementMeetings/rulesRegulationsManagement"; |
| | | import { |
| | | qualityInspectFileAdd, |
| | | qualityInspectFileListPage, |
| | | qualityInspectFileDel, |
| | | } from "@/api/qualityManagement/materialInspection"; |
| | | import { blobValidate } from "@/utils/ruoyi"; |
| | | |
| | | // éä»¶å表 |
| | | const fileList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // const request = axios.create({ |
| | | // baseURL: "URL.com", |
| | | // adapter: axiosAdapterUniapp, |
| | | // }); |
| | | // è·åæä»¶å¾æ |
| | | const getFileIcon = fileType => { |
| | | const iconMap = { |
| | | doc: "document", |
| | | docx: "document", |
| | | xls: "grid", |
| | | xlsx: "grid", |
| | | pdf: "document", |
| | | ppt: "copy", |
| | | pptx: "copy", |
| | | txt: "document", |
| | | jpg: "image", |
| | | jpeg: "image", |
| | | png: "image", |
| | | gif: "image", |
| | | zip: "folder", |
| | | rar: "folder", |
| | | }; |
| | | return iconMap[fileType.toLowerCase()] || "document"; |
| | | }; |
| | | |
| | | // è·åæä»¶å¾æ æ ·å¼ç±» |
| | | const getFileIconClass = fileType => { |
| | | const colorMap = { |
| | | doc: "blue", |
| | | docx: "blue", |
| | | xls: "green", |
| | | xlsx: "green", |
| | | pdf: "red", |
| | | ppt: "orange", |
| | | pptx: "orange", |
| | | txt: "gray", |
| | | jpg: "purple", |
| | | jpeg: "purple", |
| | | png: "purple", |
| | | gif: "purple", |
| | | zip: "yellow", |
| | | rar: "yellow", |
| | | }; |
| | | return colorMap[fileType.toLowerCase()] || "gray"; |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = bytes => { |
| | | if (bytes === 0) return "0 B"; |
| | | const k = 1024; |
| | | const sizes = ["B", "KB", "MB", "GB"]; |
| | | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| | | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; |
| | | }; |
| | | |
| | | // éæ©æä»¶ |
| | | const chooseFile = () => { |
| | | uni.chooseImage({ |
| | | count: 9, |
| | | sizeType: ["original", "compressed"], |
| | | sourceType: ["album", "camera"], |
| | | success: res => { |
| | | console.log(res, "éæ©å¾çæå"); |
| | | uploadFiles(res.tempFiles); |
| | | }, |
| | | fail: err => { |
| | | console.error("éæ©å¾ç失败:", err); |
| | | showToast("éæ©æä»¶å¤±è´¥"); |
| | | }, |
| | | }); |
| | | // uni.chooseFile({ |
| | | // count: 9, |
| | | // extension: [ |
| | | // ".doc", |
| | | // ".docx", |
| | | // ".xls", |
| | | // ".xlsx", |
| | | // ".pdf", |
| | | // ".ppt", |
| | | // ".pptx", |
| | | // ".txt", |
| | | // ".jpg", |
| | | // ".jpeg", |
| | | // ".png", |
| | | // ".gif", |
| | | // ".zip", |
| | | // ".rar", |
| | | // ], |
| | | // success: res => { |
| | | // console.log(res, "éæ©æä»¶æå"); |
| | | // uploadFiles(res.tempFiles); |
| | | // }, |
| | | // fail: err => { |
| | | // showToast("éæ©æä»¶å¤±è´¥"); |
| | | // }, |
| | | // }); |
| | | }; |
| | | |
| | | // ä¸ä¼ æä»¶ |
| | | const uploadFiles = tempFiles => { |
| | | console.log(tempFiles, "ä¸ä¼ æä»¶1"); |
| | | tempFiles.forEach((tempFile, index) => { |
| | | // æ¾ç¤ºä¸ä¼ ä¸æç¤º |
| | | uni.showLoading({ |
| | | title: "ä¸ä¼ ä¸...", |
| | | mask: true, |
| | | }); |
| | | console.log(tempFile, "ä¸ä¼ æä»¶2"); |
| | | // 1. ç´æ¥ä½¿ç¨ uni.uploadFile ä¸ä¼ æä»¶ |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + "/file/upload", |
| | | filePath: tempFile.path, |
| | | name: "file", |
| | | header: { |
| | | Authorization: "Bearer " + getToken(), |
| | | }, |
| | | success: uploadRes => { |
| | | uni.hideLoading(); |
| | | console.log(uploadRes, "ä¸ä¼ æä»¶3"); |
| | | |
| | | try { |
| | | const res = JSON.parse(uploadRes.data); |
| | | console.log(res, "ä¸ä¼ æä»¶4"); |
| | | if (res.code === 200) { |
| | | // 2. æåæä»¶ä¿¡æ¯ |
| | | const fileName = tempFile.name |
| | | ? tempFile.name |
| | | : tempFile.path.split("/").pop(); |
| | | // const fileType = fileName.split(".").pop(); |
| | | // 3. æé ä¿åæä»¶ä¿¡æ¯çåæ° |
| | | const saveData = { |
| | | name: fileName, |
| | | inspectId: rulesRegulationsManagementId.value, |
| | | url: res.data.tempPath || "", |
| | | }; |
| | | console.log(saveData, "ä¿åæä»¶ä¿¡æ¯åæ°"); |
| | | // 4. è°ç¨ addRuleFile æ¥å£ä¿åæä»¶ä¿¡æ¯ |
| | | qualityInspectFileAdd(saveData) |
| | | .then(addRes => { |
| | | if (addRes.code === 200) { |
| | | // 5. æ·»å å°æä»¶å表 |
| | | const newFile = { |
| | | ...addRes.data, |
| | | uploadTime: new Date().toLocaleString(), |
| | | }; |
| | | // fileList.value.push(newFile); |
| | | getFileList(); |
| | | showToast("ä¸ä¼ æå"); |
| | | } else { |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¿åæä»¶ä¿¡æ¯å¤±è´¥:", err); |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | }); |
| | | } else { |
| | | showToast("æä»¶ä¸ä¼ 失败"); |
| | | } |
| | | } catch (e) { |
| | | console.error("è§£æä¸ä¼ ç»æå¤±è´¥:", e); |
| | | showToast("ä¸ä¼ 失败"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | uni.hideLoading(); |
| | | console.error("ä¸ä¼ 失败:", err); |
| | | showToast("ä¸ä¼ 失败"); |
| | | }, |
| | | }); |
| | | }); |
| | | }; |
| | | // ä¸è½½æä»¶ |
| | | const downloadFile = file => { |
| | | var url = |
| | | config.baseUrl + |
| | | "/common/download?fileName=" + |
| | | encodeURIComponent(file.url) + |
| | | "&delete=true"; |
| | | console.log(url, "url"); |
| | | |
| | | uni |
| | | .downloadFile({ |
| | | url: url, |
| | | responseType: "blob", |
| | | header: { Authorization: "Bearer " + getToken() }, |
| | | }) |
| | | .then(res => { |
| | | let osType = uni.getStorageSync("deviceInfo").osName; |
| | | let filePath = res.tempFilePath; |
| | | if (osType === "ios") { |
| | | uni.openDocument({ |
| | | filePath: filePath, |
| | | showMenu: true, |
| | | success: res => { |
| | | resolve(res); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.openDocument--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.saveFile({ |
| | | tempFilePath: filePath, |
| | | success: fileRes => { |
| | | uni.showToast({ |
| | | icon: "none", |
| | | mask: true, |
| | | title: |
| | | "æä»¶å·²ä¿åï¼Android/data/uni.UNI720216F/apps/__UNI__720216F/" + |
| | | fileRes.savedFilePath, //ä¿åè·¯å¾ |
| | | duration: 3000, |
| | | }); |
| | | setTimeout(() => { |
| | | //æå¼ææ¡£æ¥ç |
| | | uni.openDocument({ |
| | | filePath: fileRes.savedFilePath, |
| | | success: function (res) { |
| | | resolve(fileRes); |
| | | }, |
| | | }); |
| | | }, 3000); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.save--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } |
| | | // const isBlob = blobValidate(res.data); |
| | | // if (isBlob) { |
| | | // const blob = new Blob([res.data], { type: "text/plain" }); |
| | | // const url = URL.createObjectURL(blob); |
| | | // const downloadLink = document.getElementById("downloadLink"); |
| | | // downloadLink.href = url; |
| | | // downloadLink.download = file.name; |
| | | // downloadLink.click(); |
| | | // showToast("ä¸è½½æå"); |
| | | // } else { |
| | | // showToast("ä¸è½½å¤±è´¥"); |
| | | // } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¸è½½å¤±è´¥:", err); |
| | | showToast("ä¸è½½å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // 确认å é¤ |
| | | const confirmDelete = (file, index) => { |
| | | uni.showModal({ |
| | | title: "å é¤ç¡®è®¤", |
| | | content: `ç¡®å®è¦å é¤éä»¶ "${file.name}" åï¼`, |
| | | success: res => { |
| | | if (res.confirm) { |
| | | deleteFile(file.id, index); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const deleteFile = (fileId, index) => { |
| | | uni.showLoading({ |
| | | title: "å é¤ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | qualityInspectFileDel([fileId]) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | // fileList.value.splice(index, 1); |
| | | getFileList(); |
| | | showToast("å 餿å"); |
| | | } else { |
| | | showToast("å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("å é¤å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæç¤º |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | const rulesRegulationsManagementId = ref(""); |
| | | // 页é¢å è½½æ¶ |
| | | onMounted(() => { |
| | | rulesRegulationsManagementId.value = uni.getStorageSync( |
| | | "qualityInspectFileId" |
| | | ); |
| | | // ä» API è·åéä»¶å表 |
| | | getFileList(); |
| | | // 仿¬å°åå¨è·å rulesRegulationsManagementId |
| | | }); |
| | | |
| | | // è·åéä»¶å表 |
| | | const getFileList = () => { |
| | | uni.showLoading({ |
| | | title: "å è½½ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | qualityInspectFileListPage({ |
| | | inspectId: rulesRegulationsManagementId.value, |
| | | current: -1, |
| | | size: -1, |
| | | }) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | fileList.value = res.data.records || []; |
| | | } else { |
| | | showToast("è·åéä»¶å表失败"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("è·åéä»¶å表失败"); |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .file-list-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100rpx; |
| | | } |
| | | |
| | | .file-list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .file-list { |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | } |
| | | |
| | | .file-icon { |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-right: 20rpx; |
| | | |
| | | &.blue { |
| | | background: #409eff; |
| | | } |
| | | |
| | | &.green { |
| | | background: #67c23a; |
| | | } |
| | | |
| | | &.red { |
| | | background: #f56c6c; |
| | | } |
| | | |
| | | &.orange { |
| | | background: #e6a23c; |
| | | } |
| | | |
| | | &.gray { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.purple { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.yellow { |
| | | background: #e6a23c; |
| | | } |
| | | } |
| | | |
| | | .file-info { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .file-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #303133; |
| | | margin-bottom: 8rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-meta { |
| | | display: block; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .file-actions { |
| | | display: flex; |
| | | gap: 12rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 100rpx 0; |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .upload-button { |
| | | position: fixed; |
| | | bottom: 40rpx; |
| | | right: 40rpx; |
| | | width: 130rpx; |
| | | height: 130rpx; |
| | | border-radius: 50%; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .upload-text { |
| | | font-size: 10px; |
| | | color: #ffffff; |
| | | margin-top: 4rpx; |
| | | } |
| | | |
| | | .upload-progress { |
| | | padding: 40rpx 0; |
| | | } |
| | | |
| | | .upload-progress-text { |
| | | display: block; |
| | | text-align: center; |
| | | margin-top: 20rpx; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-page"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="åæææ£éª" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="请è¾å
¥ä¾åºåæç´¢" |
| | | v-model="searchForm.supplier" |
| | | @search="getList" |
| | | @custom="getList" |
| | | @clear="getList" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | ></up-search> |
| | | </view> |
| | | <!-- ç»è®¡ä¿¡æ¯å¡ç --> |
| | | <!-- <view class="stats-cards"> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ totalCount }}</text> |
| | | <text class="stat-label">æ»æ£éª</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ submittedCount }}</text> |
| | | <text class="stat-label">å·²æäº¤</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ pendingCount }}</text> |
| | | <text class="stat-label">å¾
æäº¤</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ qualifiedCount }}</text> |
| | | <text class="stat-label">å·²åæ ¼</text> |
| | | </view> |
| | | </view> --> |
| | | <!-- æ£éªå表 --> |
| | | <view class="inspection-list" |
| | | v-if="inspectionList.length > 0"> |
| | | <view v-for="(item, index) in inspectionList" |
| | | :key="index"> |
| | | <view class="inspection-item" |
| | | @click="viewDetail(item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <!-- <view class="material-icon" |
| | | :class="getStateClass(item.inspectState)"> |
| | | <up-icon :name="getStateIcon(item.inspectState)" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> --> |
| | | <view class="material-info"> |
| | | <text class="material-name">{{ item.productName }}</text> |
| | | <text class="material-code">{{ item.model }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="status-tags"> |
| | | <u-tag :type="getTagType(item.checkResult)" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.checkResult }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(item.inspectState)" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.inspectState ? 'å·²æäº¤' : 'æªæäº¤' }} |
| | | </u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµæ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(item.checkTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">éè´è®¢åå·</text> |
| | | <text class="detail-value">{{ item.purchaseContractNo || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ä¾åºå</text> |
| | | <text class="detail-value">{{ item.supplier || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£éªå</text> |
| | | <text class="detail-value">{{ item.checkName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | | <text class="detail-value">{{ item.checkCompany || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="action-buttons"> |
| | | <!-- <u-button type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState" |
| | | @click.stop="startInspection(item)"> |
| | | ç¼è¾ |
| | | </u-button> --> |
| | | <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="viewDetail(item)"> |
| | | 详æ
|
| | | </u-button> |
| | | <!-- <u-button type="success" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState" |
| | | @click.stop="submitInspection(item)"> |
| | | æäº¤ |
| | | </u-button> --> |
| | | </view> |
| | | <view class="action-buttons"> |
| | | <!-- <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="viewFileList(item)"> |
| | | éä»¶ |
| | | </u-button> |
| | | <u-button type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState || item.checkName !== ''" |
| | | @click.stop="assignInspector(item)"> |
| | | åé
æ£éªå |
| | | </u-button> --> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <up-empty mode="data" |
| | | text="ææ æ£éªä»»å¡"></up-empty> |
| | | </view> |
| | | <!-- å页ç»ä»¶ --> |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <!-- <view class="fab-button" |
| | | @click="addInspection"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> --> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup v-model:show="showDate" |
| | | mode="date" |
| | | :start-year="2020" |
| | | :end-year="2030" |
| | | :range="true" |
| | | @confirm="confirmDate" /> |
| | | <!-- åé
æ£éªåå¼¹çª --> |
| | | <up-popup v-model:show="showAssignDialog" |
| | | mode="center" |
| | | round |
| | | style="width: 80%"> |
| | | <view class="assign-dialog"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">åé
æ£éªå</text> |
| | | <up-icon name="close" |
| | | size="20" |
| | | color="#999" |
| | | @click="showAssignDialog = false"></up-icon> |
| | | </view> |
| | | <view class="dialog-content"> |
| | | <up-form-item label="æ£éªå" |
| | | prop="checkName" |
| | | :label-width="60" |
| | | required> |
| | | <up-input v-model="assignForm.checkName" |
| | | placeholder="è¯·éæ©æ£éªå" |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon @click="showInspectorSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | | <view class="dialog-footer"> |
| | | <u-button type="default" |
| | | class="footer-btn" |
| | | @click="showAssignDialog = false"> |
| | | åæ¶ |
| | | </u-button> |
| | | <u-button type="primary" |
| | | class="footer-btn" |
| | | @click="submitAssign"> |
| | | ç¡®å® |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | <!-- æ£éªåéæ© --> |
| | | <up-action-sheet :show="showInspectorSheet" |
| | | :actions="userSheetOptions" |
| | | @select="selectInspector" |
| | | title="éæ©æ£éªå" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | submitQualityInspect, |
| | | qualityInspectUpdate, |
| | | qualityInspectListPage, |
| | | } from "@/api/qualityManagement/materialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = ref({ |
| | | supplier: "", |
| | | entryDate: undefined, |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }); |
| | | |
| | | // æ¥æéæ©å¨ |
| | | const showDate = ref(false); |
| | | |
| | | // åé
æ£éªåå¼¹çª |
| | | const showAssignDialog = ref(false); |
| | | const showInspectorSheet = ref(false); |
| | | const assignForm = ref({ |
| | | checkName: "", |
| | | }); |
| | | const currentAssignRow = ref(null); |
| | | |
| | | // æ£éªåè¡¨æ°æ® |
| | | const inspectionList = ref([]); |
| | | |
| | | // åé¡µæ°æ® |
| | | const page = ref({ |
| | | current: -1, |
| | | size: -1, |
| | | total: 0, |
| | | }); |
| | | |
| | | // å è½½ç¶æ |
| | | const tableLoading = ref(false); |
| | | |
| | | // ç»è®¡æ°æ® |
| | | const totalCount = ref(0); |
| | | const submittedCount = ref(0); |
| | | const pendingCount = ref(0); |
| | | const qualifiedCount = ref(0); |
| | | |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | |
| | | // ActionSheeté项 |
| | | const userSheetOptions = computed(() => { |
| | | return userList.value.map(item => ({ |
| | | name: item.nickName, |
| | | value: item.nickName, |
| | | })); |
| | | }); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // è·åç¶ææ ·å¼ |
| | | const getStateClass = inspectState => { |
| | | return inspectState ? "state-submitted" : "state-pending"; |
| | | }; |
| | | |
| | | // è·åç¶æå¾æ |
| | | const getStateIcon = inspectState => { |
| | | return inspectState ? "checkmark-circle" : "time"; |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = checkResult => { |
| | | if (checkResult === "åæ ¼") return "success"; |
| | | if (checkResult === "ä¸åæ ¼") return "error"; |
| | | return "default"; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | const getStateTagType = inspectState => { |
| | | return inspectState ? "success" : "warning"; |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const confirmDate = e => { |
| | | searchForm.value.entryDate = e.value; |
| | | searchForm.value.entryDateStart = dayjs(e.value[0]).format("YYYY-MM-DD"); |
| | | searchForm.value.entryDateEnd = dayjs(e.value[1]).format("YYYY-MM-DD"); |
| | | getList(); |
| | | }; |
| | | const viewFileList = item => { |
| | | uni.setStorageSync("qualityInspectFileId", item.id); |
| | | uni.navigateTo({ |
| | | url: "/pages/qualityManagement/materialInspection/fileList", |
| | | }); |
| | | }; |
| | | |
| | | // æ¸
餿¥æèå´ |
| | | const clearDateRange = () => { |
| | | searchForm.value.entryDate = undefined; |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | getList(); |
| | | }; |
| | | |
| | | // è·åç¨æ·å表 |
| | | const getUserList = async () => { |
| | | try { |
| | | const userRes = await userListNoPage(); |
| | | userList.value = userRes.data || []; |
| | | } catch (e) { |
| | | console.error("å è½½æ£éªåå表失败", e); |
| | | userList.value = []; |
| | | } |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page.value }; |
| | | params.entryDate = undefined; |
| | | qualityInspectListPage({ ...params, inspectType: 0 }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | inspectionList.value = res.data.records || []; |
| | | page.value.total = res.data.total || 0; |
| | | totalCount.value = res.data.total || 0; |
| | | submittedCount.value = inspectionList.value.filter( |
| | | item => item.inspectState |
| | | ).length; |
| | | pendingCount.value = inspectionList.value.filter( |
| | | item => !item.inspectState |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter( |
| | | item => item.checkResult === "åæ ¼" |
| | | ).length; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | console.error("è·åå表失败:", err); |
| | | showToast("è·åå表失败ï¼è¯·éè¯"); |
| | | }); |
| | | }; |
| | | |
| | | // ç¼è¾æ£éª |
| | | const startInspection = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | // åå¨å®æ´çæ£éªæ°æ® |
| | | uni.setStorageSync("inspectionEditData", item); |
| | | // 跳转å°ç¼è¾é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/materialInspection/add?id=${item.id}&isEdit=true`, |
| | | }); |
| | | }; |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetail = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | uni.setStorageSync("inspectionEditData", item); |
| | | // 跳转å°è¯¦æ
é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/materialInspection/detail?id=${item.id}`, |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢æ£éª |
| | | const addInspection = () => { |
| | | uni.navigateTo({ |
| | | url: "/pages/qualityManagement/materialInspection/add", |
| | | }); |
| | | }; |
| | | |
| | | // æäº¤æ£éª |
| | | const submitInspection = async item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | try { |
| | | const res = await submitQualityInspect({ id: item.id }); |
| | | if (res.code === 200) { |
| | | showToast("æäº¤æå"); |
| | | setTimeout(() => { |
| | | getList(); |
| | | }, 1000); |
| | | } else { |
| | | showToast("æäº¤å¤±è´¥ï¼" + (res.msg || "æªç¥é误")); |
| | | } |
| | | } catch (error) { |
| | | console.error("æäº¤å¤±è´¥:", error); |
| | | showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | // åé
æ£éªå |
| | | const assignInspector = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | currentAssignRow.value = item; |
| | | getUserList(); |
| | | showAssignDialog.value = true; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | | const selectInspector = e => { |
| | | assignForm.value.checkName = e.value; |
| | | showInspectorSheet.value = false; |
| | | }; |
| | | |
| | | // æäº¤åé
|
| | | const submitAssign = async () => { |
| | | if (!currentAssignRow.value || !assignForm.value.checkName) { |
| | | showToast("è¯·éæ©æ£éªå"); |
| | | return; |
| | | } |
| | | try { |
| | | const data = { |
| | | ...assignForm.value, |
| | | id: currentAssignRow.value.id, |
| | | }; |
| | | const res = await qualityInspectUpdate(data); |
| | | if (res.code === 200) { |
| | | showToast("åé
æå"); |
| | | showAssignDialog.value = false; |
| | | setTimeout(() => { |
| | | getList(); |
| | | }, 1000); |
| | | } else { |
| | | showToast("åé
失败ï¼" + (res.msg || "æªç¥é误")); |
| | | } |
| | | } catch (error) { |
| | | console.error("åé
失败:", error); |
| | | showToast("åé
失败ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | // å¤çå页 |
| | | const handlePagination = obj => { |
| | | page.value.current = obj.current; |
| | | page.value.size = obj.size; |
| | | getList(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | getUserList(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .material-inspection-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | // æç´¢åºå |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | // ç»è®¡å¡ç |
| | | .stats-cards { |
| | | display: flex; |
| | | padding: 15px; |
| | | gap: 10px; |
| | | background: #fff; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .stat-card { |
| | | flex: 1; |
| | | background: #2979ff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | text-align: center; |
| | | color: #fff; |
| | | box-shadow: 0 2px 8px rgba(41, 121, 255, 0.2); |
| | | } |
| | | |
| | | .stat-number { |
| | | display: block; |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 12px; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | // æ£éªå表 |
| | | .inspection-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .inspection-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | |
| | | &:active { |
| | | transform: scale(0.98); |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 16px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .material-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .state-pending { |
| | | background: #ff9900; |
| | | } |
| | | |
| | | .state-submitted { |
| | | background: #52c41a; |
| | | } |
| | | |
| | | .material-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .material-name { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .material-code { |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-left: 8px; |
| | | } |
| | | |
| | | .status-tags { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .status-tag { |
| | | margin: 0; |
| | | } |
| | | |
| | | .date-range { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-top: 10px; |
| | | padding: 8px 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .date-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | // 详æ
è¡ |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | |
| | | // æä½æé® |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 12px; |
| | | padding: 0 0 16px 0; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .action-btn { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | // ç©ºç¶æ |
| | | .no-data { |
| | | padding: 60px 20px; |
| | | text-align: center; |
| | | } |
| | | |
| | | // æµ®å¨æé® |
| | | .fab-button { |
| | | position: fixed; |
| | | bottom: 20px; |
| | | right: 20px; |
| | | width: 56px; |
| | | height: 56px; |
| | | background: #2979ff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | // åé
æ£éªåå¼¹çª |
| | | .assign-dialog { |
| | | padding: 24px; |
| | | background: #ffffff; |
| | | border-radius: 16px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .dialog-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 24px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-content { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | gap: 16px; |
| | | padding-top: 16px; |
| | | border-top: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .footer-btn { |
| | | flex: 1; |
| | | height: 44px; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | // è¾å
¥æ¡æ ·å¼ |
| | | :deep(.up-input__inner) { |
| | | border-radius: 8px; |
| | | height: 44px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | // 表åé¡¹æ ·å¼ |
| | | :deep(.up-form-item) { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | :deep(.up-form-item__label) { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | // æé®æ ·å¼ |
| | | :deep(.up-button--primary) { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | :deep(.up-button--default) { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | // å页ç»ä»¶ |
| | | .pagination { |
| | | padding: 20px; |
| | | background: #fff; |
| | | margin-top: 10px; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="binding-detail-page"> |
| | | <PageHeader :title="'ç»å®å
³ç³»: ' + standardNo" @back="goBack" /> |
| | | |
| | | <view class="detail-info-header"> |
| | | <view class="info-row"> |
| | | <text class="info-label">æ åç¼å·ï¼</text> |
| | | <text class="info-value">{{ standardNo }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="list-container" v-if="bindingList.length > 0"> |
| | | <view v-for="(item, index) in bindingList" :key="index" class="list-item"> |
| | | <view class="item-content"> |
| | | <text class="product-name">{{ item.productName }}</text> |
| | | </view> |
| | | <view class="item-actions"> |
| | | <up-button type="error" size="mini" @click.stop="handleUnbind(item)">è§£é¤ç»å®</up-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ ç»å®å
³ç³»"></up-empty> |
| | | </view> |
| | | |
| | | <view class="fab-button" @click="openBindingDialog"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | |
| | | <!-- æ·»å ç»å®å¼¹çª --> |
| | | <up-popup v-model:show="showBindingDialog" mode="bottom" round closeable @close="showBindingDialog = false"> |
| | | <view class="binding-dialog"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">æ·»å 产åç»å®</text> |
| | | </view> |
| | | <view class="search-box"> |
| | | <up-search |
| | | placeholder="æç´¢äº§å" |
| | | v-model="productSearch" |
| | | @search="filterProducts" |
| | | @custom="filterProducts" |
| | | @clear="filterProducts" |
| | | :show-action="true" |
| | | action-text="çé" |
| | | :animation="true" |
| | | ></up-search> |
| | | </view> |
| | | <scroll-view scroll-y class="product-list"> |
| | | <up-checkbox-group v-model="selectedProductIds" placement="column"> |
| | | <up-checkbox |
| | | v-for="item in filteredProducts" |
| | | :key="item.id" |
| | | :label="item.productName" |
| | | :name="item.id" |
| | | customStyle="margin-bottom: 20rpx" |
| | | ></up-checkbox> |
| | | </up-checkbox-group> |
| | | </scroll-view> |
| | | <view class="dialog-footer"> |
| | | <up-button type="primary" text="确认ç»å®" @click="submitBinding" :loading="submitLoading"></up-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from 'vue'; |
| | | import { onLoad } from '@dcloudio/uni-app'; |
| | | import { |
| | | qualityTestStandardBindingList, |
| | | qualityTestStandardBindingAdd, |
| | | qualityTestStandardBindingDel |
| | | } from '@/api/qualityManagement/qualityTestStandardBinding.js'; |
| | | import { productTreeList } from '@/api/basicData/product.js'; |
| | | import { toast, showConfirm } from '@/utils/common'; |
| | | |
| | | const standardId = ref(null); |
| | | const standardNo = ref(''); |
| | | |
| | | const bindingList = ref([]); |
| | | const loading = ref(false); |
| | | |
| | | const showBindingDialog = ref(false); |
| | | const productSearch = ref(''); |
| | | const allProducts = ref([]); |
| | | const filteredProducts = ref([]); |
| | | const selectedProductIds = ref([]); |
| | | const submitLoading = ref(false); |
| | | |
| | | const getList = () => { |
| | | loading.value = true; |
| | | qualityTestStandardBindingList({ standardId: standardId.value }).then(res => { |
| | | bindingList.value = res.data || []; |
| | | }).finally(() => { |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const openBindingDialog = () => { |
| | | selectedProductIds.value = []; |
| | | productSearch.value = ''; |
| | | filteredProducts.value = allProducts.value; |
| | | showBindingDialog.value = true; |
| | | }; |
| | | |
| | | const filterProducts = () => { |
| | | if (!productSearch.value) { |
| | | filteredProducts.value = allProducts.value; |
| | | } else { |
| | | filteredProducts.value = allProducts.value.filter(p => |
| | | p.productName.toLowerCase().includes(productSearch.value.toLowerCase()) |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | const submitBinding = () => { |
| | | if (selectedProductIds.value.length === 0) { |
| | | toast('è¯·éæ©è¦ç»å®ç产å'); |
| | | return; |
| | | } |
| | | |
| | | submitLoading.value = true; |
| | | const data = selectedProductIds.value.map(productId => ({ |
| | | standardId: standardId.value, |
| | | productId |
| | | })); |
| | | |
| | | qualityTestStandardBindingAdd(data).then(() => { |
| | | toast('ç»å®æå'); |
| | | showBindingDialog.value = false; |
| | | getList(); |
| | | }).finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleUnbind = (row) => { |
| | | showConfirm('确认解é¤è¯¥äº§åçç»å®å
³ç³»åï¼').then(res => { |
| | | if (res.confirm) { |
| | | qualityTestStandardBindingDel([row.id]).then(() => { |
| | | toast('è§£ç»æå'); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onLoad((options) => { |
| | | standardId.value = options.id; |
| | | standardNo.value = options.standardNo; |
| | | getList(); |
| | | |
| | | productTreeList().then(res => { |
| | | allProducts.value = res.data || []; |
| | | filteredProducts.value = allProducts.value; |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .binding-detail-page { |
| | | padding-bottom: 120rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .detail-info-header { |
| | | padding: 30rpx; |
| | | background-color: #ffffff; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .info-row { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .info-label { |
| | | color: #909399; |
| | | font-size: 28rpx; |
| | | width: 160rpx; |
| | | } |
| | | |
| | | .info-value { |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .product-name { |
| | | font-size: 30rpx; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | |
| | | .fab-button { |
| | | position: fixed; |
| | | right: 40rpx; |
| | | bottom: 60rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background-color: #3c9cff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(60, 156, 255, 0.4); |
| | | z-index: 99; |
| | | } |
| | | |
| | | .binding-dialog { |
| | | background-color: #ffffff; |
| | | padding: 40rpx; |
| | | max-height: 80vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | border-radius: 24rpx 24rpx 0 0; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .dialog-header { |
| | | margin-bottom: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .search-box { |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .product-list { |
| | | flex: 1; |
| | | min-height: 400rpx; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | margin-top: 40rpx; |
| | | padding-bottom: env(safe-area-inset-bottom); |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="metric-binding-page"> |
| | | <PageHeader title="ææ ç»å®" @back="goBack" /> |
| | | |
| | | <!-- æç´¢ä¸çé --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="æ åç¼å·/æ ååç§°" |
| | | v-model="searchForm.keyword" |
| | | @search="handleQuery" |
| | | @custom="handleQuery" |
| | | @clear="handleQuery" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | customStyle="margin-bottom: 20rpx" |
| | | ></up-search> |
| | | <view class="filter-row"> |
| | | <view class="filter-item" @click="showTypeSelect = true"> |
| | | <text>{{ typeLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | <view class="filter-item" @click="showStatusSelect = true"> |
| | | <text>{{ statusLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å表åºå --> |
| | | <view class="list-container" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="index" class="list-item" @click="viewDetail(item)"> |
| | | <view class="item-header"> |
| | | <text class="standard-no">{{ item.standardNo }}</text> |
| | | <up-tag :text="getStatusText(item.state)" :type="getStatusType(item.state)" size="mini"></up-tag> |
| | | </view> |
| | | <view class="item-content"> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ ååç§°ï¼</text> |
| | | <text class="item-value">{{ item.standardName }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">ç±»å«ï¼</text> |
| | | <text class="item-value">{{ getInspectTypeText(item.inspectType) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="pagination-container"> |
| | | <up-loadmore :status="loadStatus" @loadmore="getList" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ æ°æ®"></up-empty> |
| | | </view> |
| | | |
| | | <!-- ç±»å«éæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="typeActions" |
| | | :show="showTypeSelect" |
| | | @close="showTypeSelect = false" |
| | | @select="selectType" |
| | | title="è¯·éæ©ç±»å«" |
| | | ></up-action-sheet> |
| | | |
| | | <!-- ç¶æéæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="statusActions" |
| | | :show="showStatusSelect" |
| | | @close="showStatusSelect = false" |
| | | @select="selectStatus" |
| | | title="è¯·éæ©ç¶æ" |
| | | ></up-action-sheet> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from 'vue'; |
| | | import { |
| | | qualityTestStandardListPage |
| | | } from '@/api/qualityManagement/metricMaintenance.js'; |
| | | |
| | | const searchForm = reactive({ |
| | | keyword: '', |
| | | inspectType: '', |
| | | state: '' |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | | total: 0 |
| | | }); |
| | | const loadStatus = ref('loadmore'); |
| | | |
| | | const showTypeSelect = ref(false); |
| | | const typeActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'åæææ£éª', value: '0' }, |
| | | { name: 'è¿ç¨æ£éª', value: '1' }, |
| | | { name: 'åºåæ£éª', value: '2' } |
| | | ]; |
| | | const typeLabel = computed(() => { |
| | | const action = typeActions.find(a => a.value === searchForm.inspectType); |
| | | return action ? action.name : 'å
¨é¨ç±»å«'; |
| | | }); |
| | | |
| | | const showStatusSelect = ref(false); |
| | | const statusActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'è稿', value: '0' }, |
| | | { name: 'éè¿', value: '1' }, |
| | | { name: 'æ¤é', value: '2' } |
| | | ]; |
| | | const statusLabel = computed(() => { |
| | | const action = statusActions.find(a => a.value === searchForm.state); |
| | | return action ? action.name : 'å
¨é¨ç¶æ'; |
| | | }); |
| | | |
| | | const getInspectTypeText = (type) => { |
| | | const types = { '0': 'åæææ£éª', '1': 'è¿ç¨æ£éª', '2': 'åºåæ£éª' }; |
| | | return types[type] || '-'; |
| | | }; |
| | | |
| | | const getStatusText = (state) => { |
| | | const states = { '0': 'è稿', '1': 'éè¿', '2': 'æ¤é' }; |
| | | return states[state] || 'æªç¥'; |
| | | }; |
| | | |
| | | const getStatusType = (state) => { |
| | | const types = { '0': 'info', '1': 'success', '2': 'warning' }; |
| | | return types[state] || 'info'; |
| | | }; |
| | | |
| | | const getList = () => { |
| | | if (loadStatus.value === 'loading' || (page.total > 0 && tableData.value.length >= page.total)) return; |
| | | |
| | | loadStatus.value = 'loading'; |
| | | const params = { |
| | | standardNo: searchForm.keyword || null, |
| | | inspectType: searchForm.inspectType || null, |
| | | state: searchForm.state || null, |
| | | current: page.current, |
| | | size: page.size |
| | | }; |
| | | |
| | | qualityTestStandardListPage(params).then(res => { |
| | | const records = res?.data?.records || []; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | } |
| | | page.total = res?.data?.total || 0; |
| | | |
| | | if (tableData.value.length >= page.total) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | | loadStatus.value = 'loadmore'; |
| | | page.current++; |
| | | } |
| | | }).catch(() => { |
| | | loadStatus.value = 'loadmore'; |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | page.total = 0; |
| | | tableData.value = []; |
| | | loadStatus.value = 'loadmore'; |
| | | getList(); |
| | | }; |
| | | |
| | | const selectType = (e) => { |
| | | searchForm.inspectType = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const selectStatus = (e) => { |
| | | searchForm.state = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const viewDetail = (item) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/metricBinding/detail?id=${item.id}&standardNo=${item.standardNo}` |
| | | }); |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | handleQuery(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .metric-binding-page { |
| | | padding-bottom: 20rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .filter-row { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | padding: 10rpx 0; |
| | | } |
| | | |
| | | .filter-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | font-size: 28rpx; |
| | | color: #606266; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .standard-no { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .item-content { |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-row { |
| | | display: flex; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-label { |
| | | color: #909399; |
| | | width: 160rpx; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .pagination-container { |
| | | padding: 20rpx 0; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="metric-detail-page"> |
| | | <PageHeader :title="'æ ååæ°: ' + standardNo" @back="goBack" /> |
| | | |
| | | <!-- ä¿¡æ¯å¤´é¨ --> |
| | | <view class="detail-info-header"> |
| | | <view class="info-row"> |
| | | <text class="info-label">æ åç¼å·ï¼</text> |
| | | <text class="info-value">{{ standardNo }}</text> |
| | | </view> |
| | | <view class="info-row"> |
| | | <text class="info-label">ç¶æï¼</text> |
| | | <up-tag :text="getStatusText(state)" :type="getStatusType(state)" size="mini"></up-tag> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åæ°å表 --> |
| | | <view class="list-container" v-if="paramList.length > 0"> |
| | | <view v-for="(item, index) in paramList" :key="index" class="list-item"> |
| | | <view class="item-content"> |
| | | <view class="item-header"> |
| | | <text class="param-name">{{ item.parameterItem }}</text> |
| | | <text class="param-unit">{{ item.unit || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ åå¼ï¼</text> |
| | | <text class="item-value">{{ item.standardValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">å
æ§å¼ï¼</text> |
| | | <text class="item-value">{{ item.controlValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">é»è®¤å¼ï¼</text> |
| | | <text class="item-value">{{ item.defaultValue || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="item-actions" v-if="!isReadonly"> |
| | | <up-button type="primary" size="mini" @click.stop="openParamDialog('edit', item)">ç¼è¾</up-button> |
| | | <up-button type="error" size="mini" @click.stop="handleDelete(item)">å é¤</up-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ åæ°"></up-empty> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" v-if="!isReadonly" @click="openParamDialog('add')"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | |
| | | <!-- åæ°ç¼è¾å¼¹çª --> |
| | | <up-popup v-model:show="paramDialogVisible" mode="center" round closeable @close="closeParamDialog"> |
| | | <view class="dialog-content"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">{{ operationType === 'add' ? 'æ°å¢åæ°' : 'ä¿®æ¹åæ°' }}</text> |
| | | </view> |
| | | <up-form :model="form" ref="formRef" label-width="80"> |
| | | <up-form-item label="忰项" prop="parameterItem" required> |
| | | <up-input v-model="form.parameterItem" placeholder="请è¾å
¥åæ°é¡¹" border="bottom" /> |
| | | </up-form-item> |
| | | <up-form-item label="åä½" prop="unit"> |
| | | <up-input v-model="form.unit" placeholder="请è¾å
¥åä½" border="bottom" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ åå¼" prop="standardValue"> |
| | | <up-input v-model="form.standardValue" placeholder="请è¾å
¥æ åå¼" border="bottom" /> |
| | | </up-form-item> |
| | | <up-form-item label="å
æ§å¼" prop="controlValue"> |
| | | <up-input v-model="form.controlValue" placeholder="请è¾å
¥å
æ§å¼" border="bottom" /> |
| | | </up-form-item> |
| | | <up-form-item label="é»è®¤å¼" prop="defaultValue"> |
| | | <up-input v-model="form.defaultValue" placeholder="请è¾å
¥é»è®¤å¼" border="bottom" /> |
| | | </up-form-item> |
| | | </up-form> |
| | | <view class="dialog-footer"> |
| | | <up-button type="primary" text="确认" @click="submitForm" :loading="submitLoading"></up-button> |
| | | <up-button text="åæ¶" @click="closeParamDialog" customStyle="margin-top: 20rpx"></up-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from 'vue'; |
| | | import { onLoad } from '@dcloudio/uni-app'; |
| | | import { |
| | | qualityTestStandardParamList, |
| | | qualityTestStandardParamAdd, |
| | | qualityTestStandardParamUpdate, |
| | | qualityTestStandardParamDel |
| | | } from '@/api/qualityManagement/metricMaintenance.js'; |
| | | import { toast, showConfirm } from '@/utils/common'; |
| | | |
| | | const standardId = ref(null); |
| | | const standardNo = ref(''); |
| | | const state = ref('0'); |
| | | |
| | | const paramList = ref([]); |
| | | const loading = ref(false); |
| | | |
| | | const isReadonly = computed(() => state.value === '1' || state.value === 1); |
| | | |
| | | const paramDialogVisible = ref(false); |
| | | const operationType = ref('add'); |
| | | const submitLoading = ref(false); |
| | | const form = reactive({ |
| | | id: null, |
| | | standardId: null, |
| | | parameterItem: '', |
| | | unit: '', |
| | | standardValue: '', |
| | | controlValue: '', |
| | | defaultValue: '' |
| | | }); |
| | | |
| | | const getStatusText = (s) => { |
| | | const states = { '0': 'è稿', '1': 'éè¿', '2': 'æ¤é' }; |
| | | return states[s] || 'æªç¥'; |
| | | }; |
| | | |
| | | const getStatusType = (s) => { |
| | | const types = { '0': 'info', '1': 'success', '2': 'warning' }; |
| | | return types[s] || 'info'; |
| | | }; |
| | | |
| | | const getList = () => { |
| | | loading.value = true; |
| | | qualityTestStandardParamList({ standardId: standardId.value }).then(res => { |
| | | paramList.value = res.data || []; |
| | | }).finally(() => { |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const openParamDialog = (type, row) => { |
| | | operationType.value = type; |
| | | if (type === 'edit' && row) { |
| | | Object.assign(form, { |
| | | id: row.id, |
| | | standardId: standardId.value, |
| | | parameterItem: row.parameterItem, |
| | | unit: row.unit, |
| | | standardValue: row.standardValue, |
| | | controlValue: row.controlValue, |
| | | defaultValue: row.defaultValue |
| | | }); |
| | | } else { |
| | | Object.assign(form, { |
| | | id: null, |
| | | standardId: standardId.value, |
| | | parameterItem: '', |
| | | unit: '', |
| | | standardValue: '', |
| | | controlValue: '', |
| | | defaultValue: '' |
| | | }); |
| | | } |
| | | paramDialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeParamDialog = () => { |
| | | paramDialogVisible.value = false; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | submitLoading.value = true; |
| | | const api = operationType.value === 'add' ? qualityTestStandardParamAdd : qualityTestStandardParamUpdate; |
| | | api(form).then(() => { |
| | | toast('ä¿åæå'); |
| | | paramDialogVisible.value = false; |
| | | getList(); |
| | | }).finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | showConfirm('确认å é¤è¯¥åæ°é¡¹åï¼').then(res => { |
| | | if (res.confirm) { |
| | | qualityTestStandardParamDel([row.id]).then(() => { |
| | | toast('å 餿å'); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onLoad((options) => { |
| | | standardId.value = options.id; |
| | | standardNo.value = options.standardNo; |
| | | state.value = options.state; |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .metric-detail-page { |
| | | padding-bottom: 120rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .detail-info-header { |
| | | padding: 30rpx; |
| | | background-color: #ffffff; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .info-row { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .info-label { |
| | | color: #909399; |
| | | font-size: 28rpx; |
| | | width: 160rpx; |
| | | } |
| | | |
| | | .info-value { |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .param-name { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .param-unit { |
| | | font-size: 26rpx; |
| | | color: #909399; |
| | | background-color: #f0f2f5; |
| | | padding: 4rpx 12rpx; |
| | | border-radius: 4rpx; |
| | | } |
| | | |
| | | .item-row { |
| | | display: flex; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-label { |
| | | color: #909399; |
| | | width: 160rpx; |
| | | font-size: 26rpx; |
| | | } |
| | | |
| | | .item-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | font-size: 26rpx; |
| | | } |
| | | |
| | | .item-actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 20rpx; |
| | | border-top: 1rpx solid #ebeef5; |
| | | padding-top: 20rpx; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | |
| | | .fab-button { |
| | | position: fixed; |
| | | right: 40rpx; |
| | | bottom: 60rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background-color: #3c9cff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(60, 156, 255, 0.4); |
| | | z-index: 99; |
| | | } |
| | | |
| | | .dialog-content { |
| | | width: 650rpx; |
| | | padding: 40rpx; |
| | | background-color: #ffffff; |
| | | border-radius: 24rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .dialog-header { |
| | | margin-bottom: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | margin-top: 40rpx; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="metric-maintenance-page"> |
| | | <PageHeader title="ææ ç»´æ¤" @back="goBack" /> |
| | | |
| | | <!-- æç´¢ä¸çé --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="æ åç¼å·/æ ååç§°" |
| | | v-model="searchForm.keyword" |
| | | @search="handleQuery" |
| | | @custom="handleQuery" |
| | | @clear="handleQuery" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | customStyle="margin-bottom: 20rpx" |
| | | ></up-search> |
| | | <view class="filter-row"> |
| | | <view class="filter-item" @click="showTypeSelect = true"> |
| | | <text>{{ typeLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | <view class="filter-item" @click="showStatusSelect = true"> |
| | | <text>{{ statusLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å表åºå --> |
| | | <view class="list-container" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="index" class="list-item" @click="viewDetail(item)"> |
| | | <view class="item-header"> |
| | | <text class="standard-no">{{ item.standardNo }}</text> |
| | | <up-tag :text="getStatusText(item.state)" :type="getStatusType(item.state)" size="mini"></up-tag> |
| | | </view> |
| | | <view class="item-content"> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ ååç§°ï¼</text> |
| | | <text class="item-value">{{ item.standardName }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">ç±»å«ï¼</text> |
| | | <text class="item-value">{{ getInspectTypeText(item.inspectType) }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">å·¥åºï¼</text> |
| | | <text class="item-value">{{ item.processName || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="item-actions"> |
| | | <up-button type="primary" size="mini" @click.stop="openStandardDialog('edit', item)">ç¼è¾</up-button> |
| | | <up-button v-if="item.state !== 1" type="success" size="mini" @click.stop="handleAudit(item, 1)">æ¹å</up-button> |
| | | <up-button v-if="item.state === 1" type="warning" size="mini" @click.stop="handleAudit(item, 2)">æ¤é</up-button> |
| | | <up-button type="error" size="mini" @click.stop="handleDelete(item)">å é¤</up-button> |
| | | </view> |
| | | </view> |
| | | <view class="pagination-container"> |
| | | <up-loadmore :status="loadStatus" @loadmore="getList" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ æ°æ®"></up-empty> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" @click="openStandardDialog('add')"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | |
| | | <!-- ç±»å«éæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="typeActions" |
| | | :show="showTypeSelect" |
| | | @close="showTypeSelect = false" |
| | | @select="selectType" |
| | | title="è¯·éæ©ç±»å«" |
| | | ></up-action-sheet> |
| | | |
| | | <!-- ç¶æéæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="statusActions" |
| | | :show="showStatusSelect" |
| | | @close="showStatusSelect = false" |
| | | @select="selectStatus" |
| | | title="è¯·éæ©ç¶æ" |
| | | ></up-action-sheet> |
| | | |
| | | <!-- æ åç¼è¾å¼¹çª --> |
| | | <up-popup v-model:show="standardDialogVisible" mode="center" round closeable @close="closeStandardDialog"> |
| | | <view class="dialog-content"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">{{ standardOperationType === 'add' ? 'æ°å¢æ£æµæ å' : 'ä¿®æ¹æ£æµæ å' }}</text> |
| | | </view> |
| | | <up-form :model="standardForm" ref="standardFormRef" label-width="100" label-position="top"> |
| | | <up-form-item label="æ åç¼å·" prop="standardNo" required borderBottom> |
| | | <up-input v-model="standardForm.standardNo" placeholder="请è¾å
¥æ åç¼å·" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ ååç§°" prop="standardName" required borderBottom> |
| | | <up-input v-model="standardForm.standardName" placeholder="请è¾å
¥æ ååç§°" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="ç±»å«" prop="inspectType" required borderBottom> |
| | | <up-radio-group v-model="standardForm.inspectType"> |
| | | <up-radio label="åææ" name="0"></up-radio> |
| | | <up-radio label="è¿ç¨" name="1" customStyle="margin-left: 20rpx"></up-radio> |
| | | <up-radio label="åºå" name="2" customStyle="margin-left: 20rpx"></up-radio> |
| | | </up-radio-group> |
| | | </up-form-item> |
| | | <up-form-item label="å·¥åº" prop="processId" borderBottom> |
| | | <up-input |
| | | v-model="processName" |
| | | placeholder="è¯·éæ©å·¥åº" |
| | | border="surround" |
| | | readonly |
| | | @click="showProcessSelect = true" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="夿³¨" prop="remark" borderBottom> |
| | | <up-textarea v-model="standardForm.remark" placeholder="请è¾å
¥å¤æ³¨" count border="surround" /> |
| | | </up-form-item> |
| | | </up-form> |
| | | <view class="dialog-footer"> |
| | | <up-button type="primary" text="确认" @click="submitStandardForm" :loading="submitLoading"></up-button> |
| | | <up-button text="åæ¶" @click="closeStandardDialog" customStyle="margin-top: 20rpx"></up-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | |
| | | <!-- å·¥åºéæ©å¨ --> |
| | | <up-picker |
| | | :show="showProcessSelect" |
| | | :columns="[processOptions]" |
| | | keyName="label" |
| | | @confirm="confirmProcess" |
| | | @cancel="showProcessSelect = false" |
| | | ></up-picker> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from 'vue'; |
| | | import { |
| | | qualityTestStandardListPage, |
| | | qualityTestStandardAdd, |
| | | qualityTestStandardUpdate, |
| | | qualityTestStandardDel, |
| | | qualityTestStandardAudit |
| | | } from '@/api/qualityManagement/metricMaintenance.js'; |
| | | import { productProcessListPage } from '@/api/basicData/productProcess.js'; |
| | | import { toast, showConfirm } from '@/utils/common'; |
| | | |
| | | const searchForm = reactive({ |
| | | keyword: '', |
| | | inspectType: '', |
| | | state: '' |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | | total: 0 |
| | | }); |
| | | const loadStatus = ref('loadmore'); |
| | | |
| | | const showTypeSelect = ref(false); |
| | | const typeActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'åæææ£éª', value: '0' }, |
| | | { name: 'è¿ç¨æ£éª', value: '1' }, |
| | | { name: 'åºåæ£éª', value: '2' } |
| | | ]; |
| | | const typeLabel = computed(() => { |
| | | const action = typeActions.find(a => a.value === searchForm.inspectType); |
| | | return action ? action.name : 'å
¨é¨ç±»å«'; |
| | | }); |
| | | |
| | | const showStatusSelect = ref(false); |
| | | const statusActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'è稿', value: '0' }, |
| | | { name: 'éè¿', value: '1' }, |
| | | { name: 'æ¤é', value: '2' } |
| | | ]; |
| | | const statusLabel = computed(() => { |
| | | const action = statusActions.find(a => a.value === searchForm.state); |
| | | return action ? action.name : 'å
¨é¨ç¶æ'; |
| | | }); |
| | | |
| | | const standardDialogVisible = ref(false); |
| | | const standardOperationType = ref('add'); |
| | | const submitLoading = ref(false); |
| | | const standardForm = reactive({ |
| | | id: null, |
| | | standardNo: '', |
| | | standardName: '', |
| | | inspectType: '0', |
| | | processId: null, |
| | | remark: '', |
| | | state: '0' |
| | | }); |
| | | |
| | | const processOptions = ref([]); |
| | | const showProcessSelect = ref(false); |
| | | const processName = ref(''); |
| | | |
| | | const getInspectTypeText = (type) => { |
| | | const types = { '0': 'åæææ£éª', '1': 'è¿ç¨æ£éª', '2': 'åºåæ£éª' }; |
| | | return types[type] || '-'; |
| | | }; |
| | | |
| | | const getStatusText = (state) => { |
| | | const states = { '0': 'è稿', '1': 'éè¿', '2': 'æ¤é' }; |
| | | return states[state] || 'æªç¥'; |
| | | }; |
| | | |
| | | const getStatusType = (state) => { |
| | | const types = { '0': 'info', '1': 'success', '2': 'warning' }; |
| | | return types[state] || 'info'; |
| | | }; |
| | | |
| | | const getList = () => { |
| | | if (loadStatus.value === 'loading' || (page.total > 0 && tableData.value.length >= page.total)) return; |
| | | |
| | | loadStatus.value = 'loading'; |
| | | const params = { |
| | | standardNo: searchForm.keyword || null, |
| | | inspectType: searchForm.inspectType || null, |
| | | state: searchForm.state || null, |
| | | current: page.current, |
| | | size: page.size |
| | | }; |
| | | |
| | | qualityTestStandardListPage(params).then(res => { |
| | | const records = res?.data?.records || []; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | } |
| | | page.total = res?.data?.total || 0; |
| | | |
| | | if (tableData.value.length >= page.total) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | | loadStatus.value = 'loadmore'; |
| | | page.current++; |
| | | } |
| | | }).catch(() => { |
| | | loadStatus.value = 'loadmore'; |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | page.total = 0; |
| | | tableData.value = []; |
| | | loadStatus.value = 'loadmore'; |
| | | getList(); |
| | | }; |
| | | |
| | | const selectType = (e) => { |
| | | searchForm.inspectType = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const selectStatus = (e) => { |
| | | searchForm.state = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const viewDetail = (item) => { |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/metricMaintenance/detail?id=${item.id}&standardNo=${item.standardNo}&state=${item.state}` |
| | | }); |
| | | }; |
| | | |
| | | const openStandardDialog = (type, row) => { |
| | | standardOperationType.value = type; |
| | | if (type === 'edit' && row) { |
| | | Object.assign(standardForm, { |
| | | id: row.id, |
| | | standardNo: row.standardNo, |
| | | standardName: row.standardName, |
| | | inspectType: String(row.inspectType), |
| | | processId: row.processId, |
| | | remark: row.remark, |
| | | state: String(row.state) |
| | | }); |
| | | const process = processOptions.value.find(p => p.value === row.processId); |
| | | processName.value = process ? process.label : ''; |
| | | } else { |
| | | Object.assign(standardForm, { |
| | | id: null, |
| | | standardNo: '', |
| | | standardName: '', |
| | | inspectType: '0', |
| | | processId: null, |
| | | remark: '', |
| | | state: '0' |
| | | }); |
| | | processName.value = ''; |
| | | } |
| | | standardDialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeStandardDialog = () => { |
| | | standardDialogVisible.value = false; |
| | | }; |
| | | |
| | | const submitStandardForm = () => { |
| | | submitLoading.value = true; |
| | | const api = standardOperationType.value === 'add' ? qualityTestStandardAdd : qualityTestStandardUpdate; |
| | | api(standardForm).then(() => { |
| | | toast('ä¿åæå'); |
| | | standardDialogVisible.value = false; |
| | | handleQuery(); |
| | | }).finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleAudit = (row, state) => { |
| | | const text = state === 1 ? 'æ¹å' : 'æ¤é'; |
| | | showConfirm(`确认${text}è¯¥æ£æµæ ååï¼`).then(res => { |
| | | if (res.confirm) { |
| | | qualityTestStandardAudit([{ id: row.id, state }]).then(() => { |
| | | toast(`${text}æå`); |
| | | handleQuery(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | showConfirm('确认å é¤è¯¥æ£æµæ ååï¼').then(res => { |
| | | if (res.confirm) { |
| | | qualityTestStandardDel([row.id]).then(() => { |
| | | toast('å 餿å'); |
| | | handleQuery(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const confirmProcess = (e) => { |
| | | const val = e.value[0]; |
| | | standardForm.processId = val.value; |
| | | processName.value = val.label; |
| | | showProcessSelect.value = false; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | handleQuery(); |
| | | productProcessListPage({ current: 1, size: 1000 }).then(res => { |
| | | processOptions.value = (res?.data?.records || []).map(p => ({ |
| | | label: p.processName, |
| | | value: p.id |
| | | })); |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .metric-maintenance-page { |
| | | padding-bottom: 120rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .filter-row { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | padding: 10rpx 0; |
| | | } |
| | | |
| | | .filter-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | font-size: 28rpx; |
| | | color: #606266; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .standard-no { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .item-content { |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .item-row { |
| | | display: flex; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-label { |
| | | color: #909399; |
| | | width: 160rpx; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 20rpx; |
| | | border-top: 1rpx solid #ebeef5; |
| | | padding-top: 20rpx; |
| | | } |
| | | |
| | | .pagination-container { |
| | | padding: 20rpx 0; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | |
| | | .fab-button { |
| | | position: fixed; |
| | | right: 40rpx; |
| | | bottom: 60rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background-color: #3c9cff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(60, 156, 255, 0.4); |
| | | z-index: 99; |
| | | } |
| | | |
| | | .dialog-content { |
| | | width: 650rpx; |
| | | padding: 40rpx; |
| | | background-color: #ffffff; |
| | | border-radius: 24rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .dialog-header { |
| | | margin-bottom: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | margin-top: 40rpx; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="near-expiry-return-page"> |
| | | <PageHeader title="è¿ææéè´§" @back="goBack" /> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="产ååç§°/æ¹æ¬¡å·" |
| | | v-model="searchForm.keyword" |
| | | @search="handleQuery" |
| | | @custom="handleQuery" |
| | | @clear="handleQuery" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | ></up-search> |
| | | </view> |
| | | |
| | | <!-- å表åºå --> |
| | | <view class="list-container" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="index" class="list-item"> |
| | | <view class="item-header"> |
| | | <text class="product-name">{{ item.productName }}</text> |
| | | <up-tag :text="getStatusText(item.status)" :type="getStatusType(item.status)" size="mini"></up-tag> |
| | | </view> |
| | | <view class="item-content"> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ¹æ¬¡å·ï¼</text> |
| | | <text class="item-value">{{ item.batchNumber }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">éåæ°éï¼</text> |
| | | <text class="item-value">{{ item.returnQuantity }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">é忥æï¼</text> |
| | | <text class="item-value">{{ item.returnDate }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">å°ææ¥æï¼</text> |
| | | <text class="item-value">{{ item.expiryDate }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="item-actions"> |
| | | <up-button type="primary" size="mini" @click.stop="openDialog('edit', item)">ç¼è¾</up-button> |
| | | <up-button type="error" size="mini" @click.stop="handleDelete(item)">å é¤</up-button> |
| | | </view> |
| | | </view> |
| | | <view class="pagination-container"> |
| | | <up-loadmore :status="loadStatus" @loadmore="getList" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ æ°æ®"></up-empty> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" @click="openDialog('add')"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | |
| | | <!-- æ°å¢/ç¼è¾å¼¹çª --> |
| | | <up-popup v-model:show="dialogVisible" mode="center" round closeable @close="closeDialog"> |
| | | <view class="dialog-content"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">{{ operationType === 'add' ? 'æ°å¢éè´§å°è´¦' : 'ä¿®æ¹éè´§å°è´¦' }}</text> |
| | | </view> |
| | | <up-form :model="form" ref="formRef" label-width="100" label-position="top"> |
| | | <up-form-item label="产ååç§°" prop="productName" required borderBottom> |
| | | <up-input v-model="form.productName" placeholder="请è¾å
¥äº§ååç§°" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="产åè§æ ¼" prop="productSpec" borderBottom> |
| | | <up-input v-model="form.productSpec" placeholder="请è¾å
¥äº§åè§æ ¼" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ¹æ¬¡å·" prop="batchNumber" required borderBottom> |
| | | <up-input v-model="form.batchNumber" placeholder="请è¾å
¥æ¹æ¬¡å·" border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="éåæ°é" prop="returnQuantity" required borderBottom> |
| | | <up-number-box v-model="form.returnQuantity" :min="1" /> |
| | | </up-form-item> |
| | | <up-form-item label="é忥æ" prop="returnDate" required borderBottom> |
| | | <up-input |
| | | v-model="form.returnDate" |
| | | placeholder="è¯·éæ©é忥æ" |
| | | border="surround" |
| | | readonly |
| | | @click="showDatePicker('returnDate')" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="å°ææ¥æ" prop="expiryDate" borderBottom> |
| | | <up-input |
| | | v-model="form.expiryDate" |
| | | placeholder="è¯·éæ©å°ææ¥æ" |
| | | border="surround" |
| | | readonly |
| | | @click="showDatePicker('expiryDate')" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="éååå " prop="returnReason" borderBottom> |
| | | <up-textarea v-model="form.returnReason" placeholder="请è¾å
¥éååå " count border="surround" /> |
| | | </up-form-item> |
| | | </up-form> |
| | | <view class="dialog-footer"> |
| | | <up-button type="primary" text="确认" @click="submitForm" :loading="submitLoading"></up-button> |
| | | <up-button text="åæ¶" @click="closeDialog" customStyle="margin-top: 20rpx"></up-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-datetime-picker |
| | | :show="datePickerVisible" |
| | | v-model="dateValue" |
| | | mode="date" |
| | | @confirm="confirmDate" |
| | | @cancel="datePickerVisible = false" |
| | | ></up-datetime-picker> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue'; |
| | | import { |
| | | nearExpiryReturnListPage, |
| | | nearExpiryReturnAdd, |
| | | nearExpiryReturnUpdate, |
| | | nearExpiryReturnDel |
| | | } from '@/api/qualityManagement/nearExpiryReturn.js'; |
| | | import { toast, showConfirm } from '@/utils/common'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | const searchForm = reactive({ |
| | | keyword: '' |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | | total: 0 |
| | | }); |
| | | const loadStatus = ref('loadmore'); |
| | | |
| | | const dialogVisible = ref(false); |
| | | const operationType = ref('add'); |
| | | const submitLoading = ref(false); |
| | | const form = reactive({ |
| | | id: null, |
| | | productName: '', |
| | | productSpec: '', |
| | | batchNumber: '', |
| | | returnQuantity: 1, |
| | | returnDate: '', |
| | | expiryDate: '', |
| | | returnReason: '', |
| | | status: '0' |
| | | }); |
| | | |
| | | const datePickerVisible = ref(false); |
| | | const dateValue = ref(Number(new Date())); |
| | | const currentDateField = ref(''); |
| | | |
| | | const getStatusText = (status) => { |
| | | const states = { '0': 'å¾
å¤ç', '1': 'å·²å¤ç' }; |
| | | return states[status] || 'æªç¥'; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const types = { '0': 'warning', '1': 'success' }; |
| | | return types[status] || 'info'; |
| | | }; |
| | | |
| | | const getList = () => { |
| | | if (loadStatus.value === 'loading' || (page.total > 0 && tableData.length >= page.total)) return; |
| | | |
| | | loadStatus.value = 'loading'; |
| | | const params = { |
| | | productName: searchForm.keyword || null, |
| | | current: page.current, |
| | | size: page.size |
| | | }; |
| | | |
| | | nearExpiryReturnListPage(params).then(res => { |
| | | const records = res?.data?.records || []; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | } |
| | | page.total = res?.data?.total || 0; |
| | | |
| | | if (tableData.value.length >= page.total) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | | loadStatus.value = 'loadmore'; |
| | | page.current++; |
| | | } |
| | | }).catch(() => { |
| | | loadStatus.value = 'loadmore'; |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | page.total = 0; |
| | | tableData.value = []; |
| | | loadStatus.value = 'loadmore'; |
| | | getList(); |
| | | }; |
| | | |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | if (type === 'edit' && row) { |
| | | Object.assign(form, { |
| | | id: row.id, |
| | | productName: row.productName, |
| | | productSpec: row.productSpec, |
| | | batchNumber: row.batchNumber, |
| | | returnQuantity: row.returnQuantity, |
| | | returnDate: row.returnDate, |
| | | expiryDate: row.expiryDate, |
| | | returnReason: row.returnReason, |
| | | status: String(row.status) |
| | | }); |
| | | } else { |
| | | Object.assign(form, { |
| | | id: null, |
| | | productName: '', |
| | | productSpec: '', |
| | | batchNumber: '', |
| | | returnQuantity: 1, |
| | | returnDate: dayjs().format('YYYY-MM-DD'), |
| | | expiryDate: '', |
| | | returnReason: '', |
| | | status: '0' |
| | | }); |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | submitLoading.value = true; |
| | | const api = operationType.value === 'add' ? nearExpiryReturnAdd : nearExpiryReturnUpdate; |
| | | api(form).then(() => { |
| | | toast('ä¿åæå'); |
| | | dialogVisible.value = false; |
| | | handleQuery(); |
| | | }).finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | showConfirm('确认å é¤è¯¥éè´§å°è´¦åï¼').then(res => { |
| | | if (res.confirm) { |
| | | nearExpiryReturnDel([row.id]).then(() => { |
| | | toast('å 餿å'); |
| | | handleQuery(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const showDatePicker = (field) => { |
| | | currentDateField.value = field; |
| | | dateValue.value = form[field] ? Number(new Date(form[field])) : Number(new Date()); |
| | | datePickerVisible.value = true; |
| | | }; |
| | | |
| | | const confirmDate = (e) => { |
| | | form[currentDateField.value] = dayjs(e.value).format('YYYY-MM-DD'); |
| | | datePickerVisible.value = false; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | handleQuery(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .near-expiry-return-page { |
| | | padding-bottom: 120rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .product-name { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .item-content { |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .item-row { |
| | | display: flex; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-label { |
| | | color: #909399; |
| | | width: 160rpx; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 20rpx; |
| | | border-top: 1rpx solid #ebeef5; |
| | | padding-top: 20rpx; |
| | | } |
| | | |
| | | .pagination-container { |
| | | padding: 20rpx 0; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | |
| | | .fab-button { |
| | | position: fixed; |
| | | right: 40rpx; |
| | | bottom: 60rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background-color: #3c9cff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(60, 156, 255, 0.4); |
| | | z-index: 99; |
| | | } |
| | | |
| | | .dialog-content { |
| | | width: 650rpx; |
| | | padding: 40rpx; |
| | | background-color: #ffffff; |
| | | border-radius: 24rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .dialog-header { |
| | | margin-bottom: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | margin-top: 40rpx; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="nonconforming-management-page"> |
| | | <PageHeader title="ä¸åæ ¼å管ç" @back="goBack" /> |
| | | |
| | | <!-- æç´¢ä¸çé --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="请è¾å
¥äº§ååç§°æç´¢" |
| | | v-model="searchForm.productName" |
| | | @search="handleQuery" |
| | | @custom="handleQuery" |
| | | @clear="handleQuery" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | customStyle="margin-bottom: 20rpx" |
| | | ></up-search> |
| | | <view class="filter-row"> |
| | | <view class="filter-item" @click="showTypeSelect = true"> |
| | | <text>{{ typeLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | <view class="filter-item" @click="showStatusSelect = true"> |
| | | <text>{{ statusLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å表åºå --> |
| | | <view class="list-container" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="index" class="list-item"> |
| | | <view class="item-header"> |
| | | <text class="product-name">{{ item.productName }}</text> |
| | | <up-tag :text="getStatusText(item.inspectState)" :type="getStatusType(item.inspectState)" size="mini"></up-tag> |
| | | </view> |
| | | <view class="item-content"> |
| | | <view class="item-row"> |
| | | <text class="item-label">ç±»å«ï¼</text> |
| | | <text class="item-value">{{ getInspectTypeText(item.inspectType) }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ£æµæ¥æï¼</text> |
| | | <text class="item-value">{{ item.checkTime || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">è§æ ¼åå·ï¼</text> |
| | | <text class="item-value">{{ item.model || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">ä¸åæ ¼ç°è±¡ï¼</text> |
| | | <text class="item-value text-error">{{ item.defectivePhenomena || '-' }}</text> |
| | | </view> |
| | | <view class="item-row" v-if="item.inspectState === 1"> |
| | | <text class="item-label">å¤çç»æï¼</text> |
| | | <text class="item-value text-success">{{ item.dealResult || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="item-actions"> |
| | | <up-button v-if="item.inspectState === 0" type="primary" size="mini" @click.stop="openDealDialog(item)">å¤ç</up-button> |
| | | <up-button type="error" size="mini" @click.stop="handleDelete(item)">å é¤</up-button> |
| | | </view> |
| | | </view> |
| | | <view class="pagination-container"> |
| | | <up-loadmore :status="loadStatus" @loadmore="getList" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ æ°æ®"></up-empty> |
| | | </view> |
| | | |
| | | <!-- ç±»åéæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="typeActions" |
| | | :show="showTypeSelect" |
| | | @close="showTypeSelect = false" |
| | | @select="selectType" |
| | | title="è¯·éæ©ç±»å«" |
| | | ></up-action-sheet> |
| | | |
| | | <!-- ç¶æéæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="statusActions" |
| | | :show="showStatusSelect" |
| | | @close="showStatusSelect = false" |
| | | @select="selectStatus" |
| | | title="è¯·éæ©ç¶æ" |
| | | ></up-action-sheet> |
| | | |
| | | <!-- å¤çå¼¹çª --> |
| | | <up-popup v-model:show="dealDialogVisible" mode="center" round closeable @close="dealDialogVisible = false"> |
| | | <view class="dialog-content"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">ä¸åæ ¼åå¤ç</text> |
| | | </view> |
| | | <up-form :model="dealForm" ref="dealFormRef" label-width="100" label-position="top"> |
| | | <view class="info-summary"> |
| | | <text class="summary-text">产åï¼{{ currentItem?.productName }}</text> |
| | | <text class="summary-text">ä¸åæ ¼ç°è±¡ï¼{{ currentItem?.defectivePhenomena }}</text> |
| | | </view> |
| | | <up-form-item label="å¤çç»æ" prop="dealResult" required borderBottom> |
| | | <up-textarea v-model="dealForm.dealResult" placeholder="请è¾å
¥å¤çç»æ" count border="surround" /> |
| | | </up-form-item> |
| | | <up-form-item label="å¤çæ¥æ" prop="dealTime" required borderBottom> |
| | | <up-input |
| | | v-model="dealForm.dealTime" |
| | | placeholder="è¯·éæ©å¤çæ¥æ" |
| | | border="surround" |
| | | readonly |
| | | @click="showDatePicker = true" |
| | | /> |
| | | </up-form-item> |
| | | </up-form> |
| | | <view class="dialog-footer"> |
| | | <up-button type="primary" text="æäº¤å¤ç" @click="submitDeal" :loading="submitLoading"></up-button> |
| | | <up-button text="åæ¶" @click="dealDialogVisible = false" customStyle="margin-top: 20rpx"></up-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-datetime-picker |
| | | :show="showDatePicker" |
| | | v-model="dateValue" |
| | | mode="date" |
| | | @confirm="confirmDate" |
| | | @cancel="showDatePicker = false" |
| | | ></up-datetime-picker> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from 'vue'; |
| | | import { |
| | | qualityUnqualifiedListPage, |
| | | qualityUnqualifiedDeal, |
| | | qualityUnqualifiedDel |
| | | } from '@/api/qualityManagement/nonconformingManagement.js'; |
| | | import { toast, showConfirm } from '@/utils/common'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | const searchForm = reactive({ |
| | | productName: '', |
| | | inspectType: '', |
| | | inspectState: '' |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | | total: 0 |
| | | }); |
| | | const loadStatus = ref('loadmore'); |
| | | |
| | | const showTypeSelect = ref(false); |
| | | const typeActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'å
¥åæ£', value: '0' }, |
| | | { name: 'è½¦é´æ£', value: '1' }, |
| | | { name: 'åºåæ£', value: '2' } |
| | | ]; |
| | | const typeLabel = computed(() => { |
| | | const action = typeActions.find(a => a.value === searchForm.inspectType); |
| | | return action ? action.name : 'å
¨é¨ç±»å«'; |
| | | }); |
| | | |
| | | const showStatusSelect = ref(false); |
| | | const statusActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'å¾
å¤ç', value: '0' }, |
| | | { name: 'å·²å¤ç', value: '1' } |
| | | ]; |
| | | const statusLabel = computed(() => { |
| | | const action = statusActions.find(a => a.value === searchForm.inspectState); |
| | | return action ? action.name : 'å
¨é¨ç¶æ'; |
| | | }); |
| | | |
| | | const dealDialogVisible = ref(false); |
| | | const submitLoading = ref(false); |
| | | const currentItem = ref(null); |
| | | const dealForm = reactive({ |
| | | id: null, |
| | | dealResult: '', |
| | | dealTime: dayjs().format('YYYY-MM-DD') |
| | | }); |
| | | |
| | | const showDatePicker = ref(false); |
| | | const dateValue = ref(Number(new Date())); |
| | | |
| | | const getInspectTypeText = (type) => { |
| | | const types = { '0': 'å
¥åæ£', '1': 'è½¦é´æ£', '2': 'åºåæ£' }; |
| | | return types[type] || '-'; |
| | | }; |
| | | |
| | | const getStatusText = (state) => { |
| | | return state === 1 ? 'å·²å¤ç' : 'å¾
å¤ç'; |
| | | }; |
| | | |
| | | const getStatusType = (state) => { |
| | | return state === 1 ? 'success' : 'warning'; |
| | | }; |
| | | |
| | | const getList = () => { |
| | | if (loadStatus.value === 'loading' || (page.total > 0 && tableData.value.length >= page.total)) return; |
| | | |
| | | loadStatus.value = 'loading'; |
| | | const params = { |
| | | productName: searchForm.productName || null, |
| | | inspectType: searchForm.inspectType || null, |
| | | inspectState: searchForm.inspectState || null, |
| | | current: page.current, |
| | | size: page.size |
| | | }; |
| | | |
| | | qualityUnqualifiedListPage(params).then(res => { |
| | | const records = res?.data?.records || []; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | } |
| | | page.total = res?.data?.total || 0; |
| | | |
| | | if (tableData.value.length >= page.total) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | | loadStatus.value = 'loadmore'; |
| | | page.current++; |
| | | } |
| | | }).catch(() => { |
| | | loadStatus.value = 'loadmore'; |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | page.total = 0; |
| | | tableData.value = []; |
| | | loadStatus.value = 'loadmore'; |
| | | getList(); |
| | | }; |
| | | |
| | | const selectType = (e) => { |
| | | searchForm.inspectType = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const selectStatus = (e) => { |
| | | searchForm.inspectState = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const openDealDialog = (item) => { |
| | | currentItem.value = item; |
| | | dealForm.id = item.id; |
| | | dealForm.dealResult = ''; |
| | | dealForm.dealTime = dayjs().format('YYYY-MM-DD'); |
| | | dealDialogVisible.value = true; |
| | | }; |
| | | |
| | | const submitDeal = () => { |
| | | if (!dealForm.dealResult) { |
| | | toast('请è¾å
¥å¤çç»æ'); |
| | | return; |
| | | } |
| | | submitLoading.value = true; |
| | | qualityUnqualifiedDeal(dealForm).then(() => { |
| | | toast('å¤çæå'); |
| | | dealDialogVisible.value = false; |
| | | handleQuery(); |
| | | }).finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | showConfirm('确认å é¤è¯¥ä¸åæ ¼è®°å½åï¼').then(res => { |
| | | if (res.confirm) { |
| | | qualityUnqualifiedDel([row.id]).then(() => { |
| | | toast('å 餿å'); |
| | | handleQuery(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const confirmDate = (e) => { |
| | | dealForm.dealTime = dayjs(e.value).format('YYYY-MM-DD'); |
| | | showDatePicker.value = false; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | handleQuery(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .nonconforming-management-page { |
| | | padding-bottom: 20rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .filter-row { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | padding: 10rpx 0; |
| | | } |
| | | |
| | | .filter-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | font-size: 28rpx; |
| | | color: #606266; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .product-name { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .item-content { |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .item-row { |
| | | display: flex; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-label { |
| | | color: #909399; |
| | | width: 180rpx; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .text-error { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .item-actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 20rpx; |
| | | border-top: 1rpx solid #ebeef5; |
| | | padding-top: 20rpx; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | |
| | | .dialog-content { |
| | | width: 650rpx; |
| | | padding: 40rpx; |
| | | background-color: #ffffff; |
| | | border-radius: 24rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .dialog-header { |
| | | margin-bottom: 30rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .info-summary { |
| | | background-color: #f5f7fa; |
| | | padding: 20rpx; |
| | | border-radius: 8rpx; |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .summary-text { |
| | | display: block; |
| | | font-size: 26rpx; |
| | | color: #606266; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | margin-top: 40rpx; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-add"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader :title="isEdit ? 'ç¼è¾è¿ç¨æ£éª' : 'æ°å¢è¿ç¨æ£éª'" |
| | | @back="goBack" /> |
| | | <!-- 表åå
容 --> |
| | | <up-form :model="form" |
| | | ref="formRef" |
| | | label-width="110" |
| | | :rules="rules"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <up-form-item label="å·¥åº" |
| | | prop="process" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.process" |
| | | placeholder="è¯·éæ©å·¥åº" |
| | | readonly |
| | | :disabled="processQuantityDisabled" /> |
| | | <template #right> |
| | | <up-icon @click="showprocessSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="产ååç§°" |
| | | prop="productId" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.productName" |
| | | placeholder="è¯·éæ©äº§å" |
| | | readonly |
| | | @click="showProductTree = true" |
| | | :disabled="isEdit" /> |
| | | <template #right> |
| | | <up-icon @click="showProductTree = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="è§æ ¼åå·" |
| | | prop="productModelId" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.model" |
| | | placeholder="è¯·éæ©è§æ ¼åå·" |
| | | readonly |
| | | :disabled="isEdit" /> |
| | | <template #right> |
| | | <up-icon @click="showModelSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="ææ éæ©" |
| | | prop="testStandardId" |
| | | border-bottom> |
| | | <up-input v-model="testStandardDisplay" |
| | | placeholder="è¯·éæ©ææ " |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon @click="openTestStandardSheet" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="åä½" |
| | | prop="unit" |
| | | border-bottom> |
| | | <up-input v-model="form.unit" |
| | | placeholder="请è¾å
¥åä½" |
| | | disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="æ°é" |
| | | prop="quantity" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.quantity" |
| | | type="number" |
| | | placeholder="请è¾å
¥æ°é" |
| | | :disabled="processQuantityDisabled" /> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµåä½" |
| | | prop="checkCompany" |
| | | border-bottom> |
| | | <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" |
| | | border-bottom> |
| | | <up-input v-model="form.checkName" |
| | | placeholder="è¯·éæ©æ£éªå" |
| | | readonly |
| | | @click="showInspectorSheet" /> |
| | | <template #right> |
| | | <up-icon @click="showInspectorSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | <up-form-item label="æ£æµæ¥æ" |
| | | prop="checkTime" |
| | | required |
| | | border-bottom> |
| | | <up-input v-model="form.checkTime" |
| | | placeholder="è¯·éæ©æ£æµæ¥æ" |
| | | readonly /> |
| | | <!-- <template #right> |
| | | <up-icon name="calendar" |
| | | @click="showDatePicker"></up-icon> |
| | | </template> --> |
| | | </up-form-item> |
| | | <!-- æ£éªé¡¹ç® --> |
| | | <view class="inspection-items-container"> |
| | | <view class="steps-header"> |
| | | <text class="steps-title">æ£éªé¡¹ç®</text> |
| | | <text class="steps-count">å
± {{ tableData.length }} 个项ç®</text> |
| | | </view> |
| | | <view class="steps-list"> |
| | | <view v-for="(item, index) in tableData" |
| | | :key="index" |
| | | class="exec-step-item"> |
| | | <view class="step-number"> |
| | | {{ index + 1 }} |
| | | </view> |
| | | <view class="step-content"> |
| | | <view class="step-row"> |
| | | <text class="step-label">ææ ï¼</text> |
| | | <text class="step-value">{{ item.parameterItem }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">åä½ï¼</text> |
| | | <text class="step-value">{{ item.unit }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">æ åå¼ï¼</text> |
| | | <text class="step-value">{{ item.standardValue }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">å
æ§å¼ï¼</text> |
| | | <text class="step-value">{{ item.controlValue }}</text> |
| | | </view> |
| | | <view class="step-row"> |
| | | <text class="step-label">æ£éªå¼ï¼</text> |
| | | <up-input v-model="item.testValue" |
| | | placeholder="请è¾å
¥æ£éªå¼" |
| | | clearable |
| | | border-bottom |
| | | class="step-input" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="tableData.length === 0" |
| | | class="empty-data"> |
| | | <text>请å
éæ©ææ </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-form> |
| | | <!-- åºé¨æé® --> |
| | | <view class="bottom-buttons"> |
| | | <up-button type="default" |
| | | size="default" |
| | | @click="goBack" |
| | | class="bottom-btn"> |
| | | åæ¶ |
| | | </up-button> |
| | | <up-button type="primary" |
| | | size="default" |
| | | @click="submitForm" |
| | | :loading="loading" |
| | | class="bottom-btn"> |
| | | {{ isEdit ? 'ä¿å' : 'æäº¤' }} |
| | | </up-button> |
| | | </view> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup v-model:show="showDate" |
| | | mode="date" |
| | | :start-year="2020" |
| | | :end-year="2030" |
| | | @confirm="confirmDate" /> |
| | | <!-- å·¥åºéæ© --> |
| | | <up-action-sheet :show="showprocessSheet" |
| | | :actions="processOptions" |
| | | @select="selectprocess" |
| | | @close="showprocessSheet = false" |
| | | title="鿩工åº" /> |
| | | <!-- 产åéæ© --> |
| | | <up-action-sheet :show="showProductSheet" |
| | | :actions="productSheetOptions" |
| | | @select="selectProduct" |
| | | @close="showProductSheet = false" |
| | | title="éæ©äº§å" /> |
| | | <!-- è§æ ¼åå·éæ© --> |
| | | <up-action-sheet :show="showModelSheet" |
| | | :actions="modelSheetOptions" |
| | | @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" |
| | | @select="selectInspector" |
| | | @close="showInspectorSheet = false" |
| | | title="éæ©æ£éªå" /> |
| | | <!-- ææ éæ© --> |
| | | <up-action-sheet :show="showTestStandardSheet" |
| | | :actions="testStandardSheetOptions" |
| | | @select="selectTestStandard" |
| | | @close="showTestStandardSheet = false" |
| | | title="éæ©ææ " /> |
| | | <!-- äº§åæ å½¢éæ©å¨ --> |
| | | <up-popup v-model:show="showProductTree" |
| | | position="bottom" |
| | | :round="true" |
| | | :closeable="true" |
| | | @close="showProductTree = false"> |
| | | <view class="tree-selector"> |
| | | <view class="tree-header"> |
| | | <text class="tree-title">éæ©äº§å</text> |
| | | </view> |
| | | <view class="tree-content"> |
| | | <view class="tree-node" |
| | | v-for="(node, index) in productOptions" |
| | | :key="index"> |
| | | <view v-if="node.children && node.children.length > 0" |
| | | class="tree-node-header" |
| | | @click="toggleNode(node)"> |
| | | <up-icon :name="node.expanded ? 'arrow-down' : 'arrow-right'" |
| | | class="tree-node-icon" /> |
| | | <text class="tree-node-label">{{ node.label }}</text> |
| | | </view> |
| | | <view v-else |
| | | class="tree-node-header" |
| | | @click="selectTreeNode(node)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ node.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == node.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | <view v-if="node.children && node.children.length > 0 && node.expanded" |
| | | class="tree-node-children"> |
| | | <view class="tree-node" |
| | | v-for="(child, childIndex) in node.children" |
| | | :key="childIndex"> |
| | | <view v-if="child.children && child.children.length > 0" |
| | | class="tree-node-header" |
| | | @click="toggleNode(child)"> |
| | | <up-icon :name="child.expanded ? 'arrow-down' : 'arrow-right'" |
| | | class="tree-node-icon" /> |
| | | <text class="tree-node-label">{{ child.label }}</text> |
| | | </view> |
| | | <view v-else |
| | | class="tree-node-header" |
| | | @click="selectTreeNode(child)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ child.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == child.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | <view v-if="child.children && child.children.length > 0 && child.expanded" |
| | | class="tree-node-children"> |
| | | <view class="tree-node" |
| | | v-for="(grandchild, grandchildIndex) in child.children" |
| | | :key="grandchildIndex"> |
| | | <view class="tree-node-header" |
| | | @click="selectTreeNode(grandchild)"> |
| | | <text class="tree-node-icon-placeholder"></text> |
| | | <text class="tree-node-label">{{ grandchild.label }}</text> |
| | | <up-icon name="checkmark" |
| | | v-if="form.productId == grandchild.value" |
| | | class="tree-node-check" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, nextTick } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { |
| | | qualityInspectAdd, |
| | | qualityInspectUpdate, |
| | | qualityInspectParamInfo, |
| | | qualityInspectDetailByProductId, |
| | | getQualityTestStandardParamByTestStandardId, |
| | | list, |
| | | } from "@/api/qualityManagement/materialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 表åå¼ç¨ |
| | | const formRef = ref(null); |
| | | // å è½½ç¶æ |
| | | const loading = ref(false); |
| | | // æ¥æéæ©å¨ |
| | | const showDate = ref(false); |
| | | // å·¥åºéæ© |
| | | const showprocessSheet = ref(false); |
| | | // 产åéæ© |
| | | const showProductSheet = ref(false); |
| | | // äº§åæ å½¢éæ©å¨ |
| | | const showProductTree = ref(false); |
| | | // è§æ ¼åå·éæ© |
| | | const showModelSheet = ref(false); |
| | | // æ£æµç»æéæ© |
| | | const showResultSheet = ref(false); |
| | | // æ£éªåéæ© |
| | | const showInspectorSheet = ref(false); |
| | | // ææ éæ© |
| | | const showTestStandardSheet = ref(false); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const form = ref({ |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }); |
| | | |
| | | // æ¾ç¤ºç¨çåé |
| | | const testStandardDisplay = ref(""); |
| | | |
| | | // æ£éªé¡¹ç® |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // å·¥åºå表 |
| | | const processList = ref([]); |
| | | // 产åé项 |
| | | const productOptions = ref([]); |
| | | // åå·é项 |
| | | const modelOptions = ref([]); |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | // æ£æµç»æé项 |
| | | const resultOptions = ref([ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]); |
| | | // ææ é项 |
| | | const testStandardOptions = ref([]); |
| | | // å½å产åID |
| | | const currentProductId = ref(0); |
| | | |
| | | // ActionSheeté项 |
| | | const processOptions = computed(() => { |
| | | return processList.value.map(item => ({ |
| | | name: item.name, |
| | | value: item.name, |
| | | })); |
| | | }); |
| | | |
| | | const productSheetOptions = computed(() => { |
| | | return productOptions.value.map(item => ({ |
| | | name: item.label, |
| | | value: item.value, |
| | | })); |
| | | }); |
| | | |
| | | const modelSheetOptions = computed(() => { |
| | | 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, |
| | | })); |
| | | }); |
| | | |
| | | const userSheetOptions = computed(() => { |
| | | return userList.value.map(item => ({ |
| | | name: item.nickName, |
| | | value: item.nickName, |
| | | })); |
| | | }); |
| | | |
| | | const testStandardSheetOptions = computed(() => { |
| | | return testStandardOptions.value.map(item => ({ |
| | | name: item.standardName || item.standardNo, |
| | | value: item.id, |
| | | })); |
| | | }); |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | process: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [ |
| | | { required: true, message: "è¯·éæ©äº§ååå·", trigger: "change" }, |
| | | ], |
| | | testStandardId: [ |
| | | { required: false, message: "è¯·éæ©ææ ", trigger: "change" }, |
| | | ], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [ |
| | | { required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | // æ¯å¦ä¸ºç¼è¾æ¨¡å¼ |
| | | const isEdit = computed(() => { |
| | | const id = getPageId(); |
| | | return !!id; |
| | | }); |
| | | |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åå·¥åºãæ°éç½®ç° |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | return currentPage.options.id; |
| | | }; |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const confirmDate = e => { |
| | | form.value.checkTime = dayjs(e.value).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // éæ©å·¥åº |
| | | const selectprocess = e => { |
| | | form.value.process = e.value; |
| | | showprocessSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©äº§å |
| | | const selectProduct = e => { |
| | | form.value.productId = e.value; |
| | | form.value.productName = e.name; |
| | | showProductSheet.value = false; |
| | | getModels(e.value); |
| | | }; |
| | | |
| | | // 忢æ å½¢èç¹å±å¼/æå |
| | | const toggleNode = node => { |
| | | node.expanded = !node.expanded; |
| | | }; |
| | | |
| | | // éæ©æ å½¢èç¹ |
| | | const selectTreeNode = node => { |
| | | // ç¡®ä¿åªéæ©æ«ç«¯èç¹ |
| | | if (!node.children || node.children.length == 0) { |
| | | form.value.productId = node.value; |
| | | form.value.productName = node.label; |
| | | showProductTree.value = false; |
| | | getModels(node.value); |
| | | } |
| | | }; |
| | | |
| | | // 转æ¢äº§åæ ç»æ |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | |
| | | // æ ¹æ®IDæ¥æ¾èç¹ |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | |
| | | // éæ©è§æ ¼åå· |
| | | const selectModel = e => { |
| | | form.value.productModelId = e.value; |
| | | showModelSheet.value = false; |
| | | handleChangeModel(e.value); |
| | | }; |
| | | |
| | | // å¤çåå·åå |
| | | const handleChangeModel = value => { |
| | | form.value.model = |
| | | 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; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | | const selectInspector = e => { |
| | | form.value.checkName = e.value; |
| | | showInspectorSheet.value = false; |
| | | }; |
| | | |
| | | // éæ©ææ |
| | | const selectTestStandard = e => { |
| | | form.value.testStandardId = e.value; |
| | | testStandardDisplay.value = e.name; |
| | | showTestStandardSheet.value = false; |
| | | handleTestStandardChange(e.value); |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = testStandardId => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | tableData.value = res.data || []; |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ ååæ°å¤±è´¥:", error); |
| | | tableData.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | const openTestStandardSheet = () => { |
| | | console.log("openTestStandardSheet"); |
| | | showTestStandardSheet.value = true; |
| | | }; |
| | | |
| | | // è·åå·¥åºå表 |
| | | const getprocessList = () => { |
| | | list().then(res => { |
| | | processList.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // è·å产åé项 |
| | | const getProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return productOptions.value; |
| | | }); |
| | | }; |
| | | |
| | | // è·åç¨æ·å表 |
| | | const getUserList = async () => { |
| | | try { |
| | | const userRes = await userListNoPage(); |
| | | userList.value = userRes.data || []; |
| | | } catch (e) { |
| | | console.error("å è½½æ£éªåå表失败", e); |
| | | userList.value = []; |
| | | } |
| | | }; |
| | | |
| | | // è·ååå·å表 |
| | | const getModels = value => { |
| | | form.value.productModelId = ""; |
| | | form.value.unit = ""; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value; |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res; |
| | | }); |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | // è·åææ å表 |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 1, |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // ä¿å䏿æ¡éé¡¹æ°æ® |
| | | testStandardOptions.value = res.data || []; |
| | | // æ¸
ç©ºè¡¨æ ¼æ°æ®ï¼çå¾
ç¨æ·éæ©ææ |
| | | tableData.value = []; |
| | | // æ¸
ç©ºææ éæ© |
| | | form.value.testStandardId = ""; |
| | | testStandardDisplay.value = ""; |
| | | }); |
| | | }; |
| | | |
| | | // è·åæ£éªåæ°å表ï¼ç¼è¾æ¨¡å¼ï¼ |
| | | const getQualityInspectParamList = id => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | console.log("submitForm", form.value, tableData.value); |
| | | try { |
| | | // await formRef.value.validate(); |
| | | if (!form.value.productModelId) { |
| | | showToast("è¯·éæ©è§æ ¼åå·"); |
| | | return; |
| | | } |
| | | if (!form.value.process) { |
| | | showToast("è¯·éæ©å·¥åº"); |
| | | return; |
| | | } |
| | | if (!form.value.quantity) { |
| | | showToast("请è¾å
¥æ°é"); |
| | | return; |
| | | } |
| | | if (!form.value.productId) { |
| | | showToast("è¯·éæ©äº§å"); |
| | | return; |
| | | } |
| | | if (!form.value.checkResult) { |
| | | showToast("è¯·éæ©æ£æµç»æ"); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | |
| | | form.value.inspectType = 1; |
| | | if (isEdit.value) { |
| | | tableData.value.forEach(item => { |
| | | delete item.id; |
| | | }); |
| | | } |
| | | |
| | | const data = { ...form.value, qualityInspectParams: tableData.value }; |
| | | data.quantity = Number(data.quantity); |
| | | if (isEdit.value) { |
| | | const res = await qualityInspectUpdate(data); |
| | | showToast("ä¿åæå"); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | const res = await qualityInspectAdd(data); |
| | | showToast("æäº¤æå"); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } |
| | | } catch (error) { |
| | | console.error("表åéªè¯å¤±è´¥:", error); |
| | | showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // åå§å表å |
| | | const initForm = async () => { |
| | | const id = getPageId(); |
| | | if (id) { |
| | | // ç¼è¾æ¨¡å¼ï¼å è½½æ°æ® |
| | | // å
éç½®è¡¨åæ°æ® |
| | | form.value = { |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | // å
ç¡®ä¿äº§åæ å·²å è½½ï¼å¦åç¼è¾æ¶äº§å/è§æ ¼åå·æ æ³åæ¾ |
| | | await getProductOptions(); |
| | | // 仿¬å°åå¨è·åç¼è¾æ°æ® |
| | | const row = uni.getStorageSync("processInspectionEditData") || { |
| | | id: id, |
| | | checkTime: "2026-03-03", |
| | | process: "䏿µ·é屿ææéå
¬å¸", |
| | | checkName: "å¼ ä¸", |
| | | productName: "ä¸é颿¿æ", |
| | | productId: 1, |
| | | productModelId: 1, |
| | | model: "304", |
| | | testStandardId: "1", |
| | | unit: "kg", |
| | | quantity: 1000, |
| | | checkCompany: "ç¬¬ä¸æ¹æ£æµæºæ", |
| | | checkResult: "åæ ¼", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | form.value = { ...row }; |
| | | currentProductId.value = row.productId || 0; |
| | | // å
³é®ï¼ç¼è¾æ¶å è½½è§æ ¼åå·ä¸æéé¡¹ï¼æè½åæ¾ productModelId |
| | | if (currentProductId.value) { |
| | | try { |
| | | const res = await modelList({ id: currentProductId.value }); |
| | | modelOptions.value = res || []; |
| | | // 忥åå¡« model / unit |
| | | if (form.value.productModelId) { |
| | | handleChangeModel(form.value.productModelId); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½è§æ ¼åå·å¤±è´¥", e); |
| | | modelOptions.value = []; |
| | | } |
| | | } |
| | | // ç¼è¾æ¨¡å¼ä¸ï¼å
å è½½ææ é项ï¼ç¶åå è½½åæ°å表 |
| | | if (currentProductId.value) { |
| | | // å
å è½½ææ é项 |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 1, |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | testStandardOptions.value = res.data || []; |
| | | // ä½¿ç¨ nextTick ç¡®ä¿éé¡¹å·²ç»æ¸²æ |
| | | nextTick(() => { |
| | | // 妿ç¼è¾æ°æ®ä¸æ testStandardIdï¼å设置并å 载对åºçåæ° |
| | | if (savedTestStandardId) { |
| | | // ç¡®ä¿ç±»åå¹é
|
| | | const matchedOption = testStandardOptions.value.find( |
| | | item => |
| | | item.id == savedTestStandardId || |
| | | String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // ç¡®ä¿ä½¿ç¨å¹é
项ç id |
| | | form.value.testStandardId = matchedOption.id; |
| | | testStandardDisplay.value = |
| | | matchedOption.standardName || matchedOption.standardNo; |
| | | // ç¼è¾ä¿ç忣éªå¼ï¼ç´æ¥æåååæ°æ°æ® |
| | | getQualityInspectParamList(row.id); |
| | | } else { |
| | | // 妿æ¾ä¸å°å¹é
项ï¼å°è¯ç´æ¥ä½¿ç¨åå¼ |
| | | console.warn( |
| | | "æªæ¾å°å¹é
çææ é项ï¼testStandardId:", |
| | | savedTestStandardId |
| | | ); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } else { |
| | | // å¦åä½¿ç¨æ§çé»è¾ |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | // å±å¼äº§åæ å°å½åéä¸çèç¹ |
| | | expandProductTree(productOptions.value, row.productId); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ï¼åå§å表å |
| | | form.value = { |
| | | checkTime: dayjs().format("YYYY-MM-DD"), |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | productMainId: null, |
| | | purchaseLedgerId: null, |
| | | }; |
| | | } |
| | | }; |
| | | |
| | | // å±å¼äº§åæ å°æå®èç¹ |
| | | const expandProductTree = (nodes, targetId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | const node = nodes[i]; |
| | | if (node.value === targetId) { |
| | | return true; // æ¾å°ç®æ èç¹ |
| | | } |
| | | if (node.children && node.children.length > 0) { |
| | | const found = expandProductTree(node.children, targetId); |
| | | if (found) { |
| | | node.expanded = true; // å±å¼ç¶èç¹ |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getprocessList(); |
| | | getProductOptions(); |
| | | getUserList(); |
| | | initForm(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | initForm(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .material-inspection-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100px; |
| | | } |
| | | |
| | | // æ£éªé¡¹ç®å®¹å¨ |
| | | .inspection-items-container { |
| | | padding: 20px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .steps-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | padding-bottom: 12px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .steps-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .steps-count { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .steps-list { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .exec-step-item { |
| | | position: relative; |
| | | display: flex; |
| | | margin-bottom: 16px; |
| | | padding: 16px; |
| | | background-color: #ffffff; |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .exec-step-item:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); |
| | | border-color: #409eff; |
| | | transform: translateY(-1px); |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: -25rpx; |
| | | right: -25rpx; |
| | | width: 50rpx; |
| | | height: 50rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | text-align: center; |
| | | font-size: 20px; |
| | | border-radius: 50%; |
| | | background-color: red; |
| | | border: none; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .delete-btn:hover { |
| | | transform: scale(1.1); |
| | | box-shadow: 0 3px 6px rgba(245, 108, 108, 0.4); |
| | | } |
| | | |
| | | .step-number { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 32px; |
| | | height: 32px; |
| | | margin-right: 16px; |
| | | background-color: #ecf5ff; |
| | | color: #409eff; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | border-radius: 50%; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .step-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .step-row { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .step-row:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .step-label { |
| | | display: inline-block; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-right: 12px; |
| | | flex-shrink: 0; |
| | | line-height: 36px; |
| | | } |
| | | |
| | | .step-input { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .step-input input { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .add-step-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | height: 44px; |
| | | line-height: 44px; |
| | | font-size: 14px; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .add-step-btn:hover { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3); |
| | | } |
| | | |
| | | .add-step-btn text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | // åºé¨æé® |
| | | .bottom-buttons { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | display: flex; |
| | | padding: 16px 20px; |
| | | background: #ffffff; |
| | | border-top: 1px solid #f0f0f0; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .bottom-btn { |
| | | flex: 1; |
| | | } |
| | | |
| | | // æ 形鿩卿 ·å¼ |
| | | .tree-selector { |
| | | width: 100%; |
| | | max-height: 70vh; |
| | | background: #ffffff; |
| | | border-radius: 16px 16px 0 0; |
| | | } |
| | | |
| | | .tree-header { |
| | | padding: 16px 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | text-align: center; |
| | | } |
| | | |
| | | .tree-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .tree-content { |
| | | padding: 10px 0; |
| | | max-height: calc(70vh - 60px); |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .tree-node { |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | .tree-node-header { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px 0; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .tree-node-icon { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 8px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .tree-node-icon-placeholder { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .tree-node-label { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .tree-node-check { |
| | | width: 20px; |
| | | height: 20px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .tree-node-children { |
| | | margin-left: 28px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-detail"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="è¿ç¨æ£éªè¯¦æ
" |
| | | @back="goBack" /> |
| | | <!-- 详æ
å
容 --> |
| | | <view class="detail-section" |
| | | v-if="detailData"> |
| | | <view class="detail-card"> |
| | | <view class="card-header"> |
| | | <view class="header-icon"> |
| | | <up-icon name="file-text" |
| | | size="20" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="header-title">{{ detailData.productName || '-' }}</text> |
| | | <view class="status-tags"> |
| | | <u-tag :type="getTagType(detailData.checkResult)" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.checkResult || '-' }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(detailData.inspectState)" |
| | | size="small" |
| | | class="status-tag"> |
| | | {{ detailData.inspectState ? 'å·²æäº¤' : 'æªæäº¤' }} |
| | | </u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="detail-content"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµæ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(detailData.checkTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç产工åå·</text> |
| | | <text class="detail-value">{{ detailData.workOrderNo || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å·¥åº</text> |
| | | <text class="detail-value">{{ detailData.process || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£éªå</text> |
| | | <text class="detail-value">{{ detailData.checkName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产ååç§°</text> |
| | | <text class="detail-value">{{ detailData.productName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">è§æ ¼åå·</text> |
| | | <text class="detail-value">{{ detailData.model || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">åä½</text> |
| | | <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> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | | <text class="detail-value">{{ detailData.checkCompany || '-' }}</text> |
| | | </view> |
| | | <!-- <view class="detail-row"> |
| | | <text class="detail-label">æ£éªæ å</text> |
| | | <text class="detail-value">{{ detailData.testStandardName || '-' }}</text> |
| | | </view> --> |
| | | </view> |
| | | </view> |
| | | <!-- æ£éªé¡¹ç® --> |
| | | <view class="detail-card" |
| | | v-if="inspectionItems.length > 0"> |
| | | <view class="card-header"> |
| | | <view class="header-icon secondary"> |
| | | <up-icon name="list" |
| | | size="20" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="header-title">æ£éªé¡¹ç®</text> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="inspection-items"> |
| | | <view v-for="(item, index) in inspectionItems" |
| | | :key="index" |
| | | class="inspection-item"> |
| | | <text class="item-name">{{ item.parameterItem || 'æ£éªé¡¹ç®' }}</text> |
| | | <view class="item-details"> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">åä½:</text> |
| | | <text class="item-detail-value">{{ item.unit || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">æ åå¼:</text> |
| | | <text class="item-detail-value">{{ item.standardValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">å
æ§å¼:</text> |
| | | <text class="item-detail-value">{{ item.controlValue || '-' }}</text> |
| | | </view> |
| | | <view class="item-detail-row"> |
| | | <text class="item-detail-label">æ£éªå¼:</text> |
| | | <text class="item-detail-value result" |
| | | :class="getResultClass(item.testValue, item.standardValue)"> |
| | | {{ item.testValue || '-' }} |
| | | </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <!-- <view class="action-buttons"> |
| | | <u-button type="primary" |
| | | class="action-btn" |
| | | @click="downloadReport"> |
| | | ä¸è½½æ¥å |
| | | </u-button> |
| | | </view> --> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <up-empty mode="data" |
| | | text="ææ æ£éªè¯¦æ
"></up-empty> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { qualityInspectParamInfo } from "@/api/qualityManagement/materialInspection.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 详æ
æ°æ® |
| | | const detailData = ref(null); |
| | | // æ£éªé¡¹ç® |
| | | const inspectionItems = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = date => { |
| | | if (!date) return ""; |
| | | return dayjs(date).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = result => { |
| | | switch (result) { |
| | | case "åæ ¼": |
| | | return "success"; |
| | | case "ä¸åæ ¼": |
| | | return "error"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | const getStateTagType = state => { |
| | | return state ? "success" : "warning"; |
| | | }; |
| | | |
| | | // è·åç»ææ ·å¼ |
| | | const getResultClass = (testValue, standardValue) => { |
| | | // ç®åçç»æå¤æé»è¾ï¼å®é
项ç®ä¸å¯è½éè¦æ´å¤æç夿 |
| | | if (testValue === "åæ ¼") { |
| | | return "result-passed"; |
| | | } else if (testValue === "ä¸åæ ¼") { |
| | | return "result-rejected"; |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | // ä¸è½½æ¥å |
| | | const downloadReport = () => { |
| | | uni.showToast({ |
| | | title: "æ¥åä¸è½½ä¸...", |
| | | icon: "loading", |
| | | }); |
| | | |
| | | // 模æä¸è½½ |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: "æ¥åä¸è½½æå", |
| | | icon: "success", |
| | | }); |
| | | }, 1500); |
| | | }; |
| | | |
| | | // è·å页é¢ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | return currentPage.options.id; |
| | | }; |
| | | |
| | | // è·å详æ
æ°æ® |
| | | const getDetail = () => { |
| | | const id = getPageId(); |
| | | if (!id) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | |
| | | // 仿¬å°åå¨è·å详æ
æ°æ® |
| | | try { |
| | | const detailDataFromStorage = uni.getStorageSync( |
| | | "processInspectionEditData" |
| | | ); |
| | | if (detailDataFromStorage) { |
| | | detailData.value = detailDataFromStorage; |
| | | // 使ç¨qualityInspectParamInfoè·åæ£éªé¡¹ç® |
| | | qualityInspectParamInfo(id) |
| | | .then(res => { |
| | | if (res.data && res.data.length > 0) { |
| | | inspectionItems.value = res.data; |
| | | } else if ( |
| | | detailDataFromStorage.qualityInspectParams && |
| | | detailDataFromStorage.qualityInspectParams.length > 0 |
| | | ) { |
| | | // 妿æ¥å£æ²¡æè¿åæ°æ®ï¼ä½¿ç¨æ¬å°åå¨ä¸çæ°æ® |
| | | inspectionItems.value = detailDataFromStorage.qualityInspectParams; |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ£éªé¡¹ç®å¤±è´¥:", error); |
| | | // æ¥å£è°ç¨å¤±è´¥æ¶ï¼ä½¿ç¨æ¬å°åå¨ä¸çæ°æ®ææ¨¡ææ°æ® |
| | | if ( |
| | | detailDataFromStorage.qualityInspectParams && |
| | | detailDataFromStorage.qualityInspectParams.length > 0 |
| | | ) { |
| | | inspectionItems.value = detailDataFromStorage.qualityInspectParams; |
| | | } |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error("å 载详æ
æ°æ®å¤±è´¥:", error); |
| | | showToast("å 载详æ
æ°æ®å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | onShow(() => { |
| | | getDetail(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | getDetail(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .material-inspection-detail { |
| | | min-height: 100vh; |
| | | background: #f5f5f5; |
| | | padding-bottom: 20px; |
| | | } |
| | | |
| | | .detail-section { |
| | | padding: 15px; |
| | | } |
| | | |
| | | .detail-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | margin-bottom: 12px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .header-icon { |
| | | width: 40px; |
| | | height: 40px; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 12px; |
| | | } |
| | | |
| | | .header-icon.secondary { |
| | | background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); |
| | | } |
| | | |
| | | .header-icon.tertiary { |
| | | background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); |
| | | } |
| | | |
| | | .header-title { |
| | | flex: 1; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .status-tag { |
| | | margin-left: 20rpx; |
| | | } |
| | | |
| | | .detail-content { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 10px 0; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .detail-row:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | min-width: 100px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 14px; |
| | | color: #333; |
| | | text-align: right; |
| | | flex: 1; |
| | | } |
| | | |
| | | // æ£éªé¡¹ç® |
| | | .inspection-items { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .inspection-item { |
| | | padding: 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .inspection-item:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .item-name { |
| | | display: block; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 8px; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .item-details { |
| | | padding-top: 8px; |
| | | } |
| | | |
| | | .item-detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 6px; |
| | | } |
| | | |
| | | .item-detail-row:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .item-detail-label { |
| | | font-size: 12px; |
| | | color: #666; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .item-detail-value { |
| | | font-size: 12px; |
| | | color: #333; |
| | | text-align: right; |
| | | flex: 1; |
| | | } |
| | | |
| | | .item-detail-value.result { |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .result-passed { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .result-rejected { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | // ç©ºç¶æ |
| | | .no-data { |
| | | padding: 60px 20px; |
| | | text-align: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="file-list-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader title="é件管ç" |
| | | @back="goBack" /> |
| | | <!-- éä»¶å表 --> |
| | | <view class="file-list-container"> |
| | | <view v-if="fileList.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in fileList" |
| | | :key="file.id || index" |
| | | class="file-item"> |
| | | <!-- æä»¶å¾æ --> |
| | | <!-- <view class="file-icon" |
| | | :class="getFileIconClass(file.fileType)"> |
| | | <up-icon :name="getFileIcon(file.fileType)" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | </view> --> |
| | | <!-- æä»¶ä¿¡æ¯ --> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.name }}</text> |
| | | <!-- <text class="file-meta">{{ formatFileSize(file.fileSize) }} · {{ file.uploadTime || file.createTime }}</text> --> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="file-actions"> |
| | | <!-- <u-button size="small" |
| | | type="primary" |
| | | plain |
| | | @click="previewFile(file)">é¢è§</u-button> --> |
| | | <u-button size="small" |
| | | type="info" |
| | | plain |
| | | @click="downloadFile(file)">ä¸è½½å¹¶é¢è§</u-button> |
| | | <u-button size="small" |
| | | type="error" |
| | | plain |
| | | @click="confirmDelete(file, index)">å é¤</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- ç©ºç¶æ --> |
| | | <view v-else |
| | | class="empty-state"> |
| | | <up-icon name="document" |
| | | size="64" |
| | | color="#c0c4cc" /> |
| | | <text class="empty-text">ææ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | <!-- <a rel="nofollow" |
| | | id="downloadLink" |
| | | href="#" |
| | | style="display:none;">ä¸è½½ææ¬æä»¶</a> --> |
| | | <!-- ä¸ä¼ æé® --> |
| | | <view class="upload-button" |
| | | @click="chooseFile"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff" /> |
| | | <text class="upload-text">ä¸ä¼ éä»¶</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | // import { saveAs } from "file-saver"; |
| | | import { |
| | | listRuleFiles, |
| | | delRuleFile, |
| | | } from "@/api/managementMeetings/rulesRegulationsManagement"; |
| | | import { |
| | | qualityInspectFileAdd, |
| | | qualityInspectFileListPage, |
| | | qualityInspectFileDel, |
| | | } from "@/api/qualityManagement/materialInspection"; |
| | | import { blobValidate } from "@/utils/ruoyi"; |
| | | |
| | | // éä»¶å表 |
| | | const fileList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // const request = axios.create({ |
| | | // baseURL: "URL.com", |
| | | // adapter: axiosAdapterUniapp, |
| | | // }); |
| | | // è·åæä»¶å¾æ |
| | | const getFileIcon = fileType => { |
| | | const iconMap = { |
| | | doc: "document", |
| | | docx: "document", |
| | | xls: "grid", |
| | | xlsx: "grid", |
| | | pdf: "document", |
| | | ppt: "copy", |
| | | pptx: "copy", |
| | | txt: "document", |
| | | jpg: "image", |
| | | jpeg: "image", |
| | | png: "image", |
| | | gif: "image", |
| | | zip: "folder", |
| | | rar: "folder", |
| | | }; |
| | | return iconMap[fileType.toLowerCase()] || "document"; |
| | | }; |
| | | |
| | | // è·åæä»¶å¾æ æ ·å¼ç±» |
| | | const getFileIconClass = fileType => { |
| | | const colorMap = { |
| | | doc: "blue", |
| | | docx: "blue", |
| | | xls: "green", |
| | | xlsx: "green", |
| | | pdf: "red", |
| | | ppt: "orange", |
| | | pptx: "orange", |
| | | txt: "gray", |
| | | jpg: "purple", |
| | | jpeg: "purple", |
| | | png: "purple", |
| | | gif: "purple", |
| | | zip: "yellow", |
| | | rar: "yellow", |
| | | }; |
| | | return colorMap[fileType.toLowerCase()] || "gray"; |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = bytes => { |
| | | if (bytes === 0) return "0 B"; |
| | | const k = 1024; |
| | | const sizes = ["B", "KB", "MB", "GB"]; |
| | | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| | | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; |
| | | }; |
| | | |
| | | // éæ©æä»¶ |
| | | const chooseFile = () => { |
| | | uni.chooseImage({ |
| | | count: 9, |
| | | sizeType: ["original", "compressed"], |
| | | sourceType: ["album", "camera"], |
| | | success: res => { |
| | | console.log(res, "éæ©å¾çæå"); |
| | | uploadFiles(res.tempFiles); |
| | | }, |
| | | fail: err => { |
| | | console.error("éæ©å¾ç失败:", err); |
| | | showToast("éæ©æä»¶å¤±è´¥"); |
| | | }, |
| | | }); |
| | | // uni.chooseFile({ |
| | | // count: 9, |
| | | // extension: [ |
| | | // ".doc", |
| | | // ".docx", |
| | | // ".xls", |
| | | // ".xlsx", |
| | | // ".pdf", |
| | | // ".ppt", |
| | | // ".pptx", |
| | | // ".txt", |
| | | // ".jpg", |
| | | // ".jpeg", |
| | | // ".png", |
| | | // ".gif", |
| | | // ".zip", |
| | | // ".rar", |
| | | // ], |
| | | // success: res => { |
| | | // console.log(res, "éæ©æä»¶æå"); |
| | | // uploadFiles(res.tempFiles); |
| | | // }, |
| | | // fail: err => { |
| | | // showToast("éæ©æä»¶å¤±è´¥"); |
| | | // }, |
| | | // }); |
| | | }; |
| | | |
| | | // ä¸ä¼ æä»¶ |
| | | const uploadFiles = tempFiles => { |
| | | console.log(tempFiles, "ä¸ä¼ æä»¶1"); |
| | | tempFiles.forEach((tempFile, index) => { |
| | | // æ¾ç¤ºä¸ä¼ ä¸æç¤º |
| | | uni.showLoading({ |
| | | title: "ä¸ä¼ ä¸...", |
| | | mask: true, |
| | | }); |
| | | console.log(tempFile, "ä¸ä¼ æä»¶2"); |
| | | // 1. ç´æ¥ä½¿ç¨ uni.uploadFile ä¸ä¼ æä»¶ |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + "/file/upload", |
| | | filePath: tempFile.path, |
| | | name: "file", |
| | | header: { |
| | | Authorization: "Bearer " + getToken(), |
| | | }, |
| | | success: uploadRes => { |
| | | uni.hideLoading(); |
| | | console.log(uploadRes, "ä¸ä¼ æä»¶3"); |
| | | |
| | | try { |
| | | const res = JSON.parse(uploadRes.data); |
| | | console.log(res, "ä¸ä¼ æä»¶4"); |
| | | if (res.code === 200) { |
| | | // 2. æåæä»¶ä¿¡æ¯ |
| | | const fileName = tempFile.name |
| | | ? tempFile.name |
| | | : tempFile.path.split("/").pop(); |
| | | // const fileType = fileName.split(".").pop(); |
| | | // 3. æé ä¿åæä»¶ä¿¡æ¯çåæ° |
| | | const saveData = { |
| | | name: fileName, |
| | | inspectId: rulesRegulationsManagementId.value, |
| | | url: res.data.tempPath || "", |
| | | }; |
| | | console.log(saveData, "ä¿åæä»¶ä¿¡æ¯åæ°"); |
| | | // 4. è°ç¨ addRuleFile æ¥å£ä¿åæä»¶ä¿¡æ¯ |
| | | qualityInspectFileAdd(saveData) |
| | | .then(addRes => { |
| | | if (addRes.code === 200) { |
| | | // 5. æ·»å å°æä»¶å表 |
| | | const newFile = { |
| | | ...addRes.data, |
| | | uploadTime: new Date().toLocaleString(), |
| | | }; |
| | | // fileList.value.push(newFile); |
| | | getFileList(); |
| | | showToast("ä¸ä¼ æå"); |
| | | } else { |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¿åæä»¶ä¿¡æ¯å¤±è´¥:", err); |
| | | showToast("ä¿åæä»¶ä¿¡æ¯å¤±è´¥"); |
| | | }); |
| | | } else { |
| | | showToast("æä»¶ä¸ä¼ 失败"); |
| | | } |
| | | } catch (e) { |
| | | console.error("è§£æä¸ä¼ ç»æå¤±è´¥:", e); |
| | | showToast("ä¸ä¼ 失败"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | uni.hideLoading(); |
| | | console.error("ä¸ä¼ 失败:", err); |
| | | showToast("ä¸ä¼ 失败"); |
| | | }, |
| | | }); |
| | | }); |
| | | }; |
| | | // ä¸è½½æä»¶ |
| | | const downloadFile = file => { |
| | | var url = |
| | | config.baseUrl + |
| | | "/common/download?fileName=" + |
| | | encodeURIComponent(file.url) + |
| | | "&delete=true"; |
| | | console.log(url, "url"); |
| | | |
| | | uni |
| | | .downloadFile({ |
| | | url: url, |
| | | responseType: "blob", |
| | | header: { Authorization: "Bearer " + getToken() }, |
| | | }) |
| | | .then(res => { |
| | | let osType = uni.getStorageSync("deviceInfo").osName; |
| | | let filePath = res.tempFilePath; |
| | | if (osType === "ios") { |
| | | uni.openDocument({ |
| | | filePath: filePath, |
| | | showMenu: true, |
| | | success: res => { |
| | | resolve(res); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.openDocument--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.saveFile({ |
| | | tempFilePath: filePath, |
| | | success: fileRes => { |
| | | uni.showToast({ |
| | | icon: "none", |
| | | mask: true, |
| | | title: |
| | | "æä»¶å·²ä¿åï¼Android/data/uni.UNI720216F/apps/__UNI__720216F/" + |
| | | fileRes.savedFilePath, //ä¿åè·¯å¾ |
| | | duration: 3000, |
| | | }); |
| | | setTimeout(() => { |
| | | //æå¼ææ¡£æ¥ç |
| | | uni.openDocument({ |
| | | filePath: fileRes.savedFilePath, |
| | | success: function (res) { |
| | | resolve(fileRes); |
| | | }, |
| | | }); |
| | | }, 3000); |
| | | }, |
| | | fail: err => { |
| | | console.log("uni.save--fail"); |
| | | reject(err); |
| | | }, |
| | | }); |
| | | } |
| | | // const isBlob = blobValidate(res.data); |
| | | // if (isBlob) { |
| | | // const blob = new Blob([res.data], { type: "text/plain" }); |
| | | // const url = URL.createObjectURL(blob); |
| | | // const downloadLink = document.getElementById("downloadLink"); |
| | | // downloadLink.href = url; |
| | | // downloadLink.download = file.name; |
| | | // downloadLink.click(); |
| | | // showToast("ä¸è½½æå"); |
| | | // } else { |
| | | // showToast("ä¸è½½å¤±è´¥"); |
| | | // } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¸è½½å¤±è´¥:", err); |
| | | showToast("ä¸è½½å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // 确认å é¤ |
| | | const confirmDelete = (file, index) => { |
| | | uni.showModal({ |
| | | title: "å é¤ç¡®è®¤", |
| | | content: `ç¡®å®è¦å é¤éä»¶ "${file.name}" åï¼`, |
| | | success: res => { |
| | | if (res.confirm) { |
| | | deleteFile(file.id, index); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const deleteFile = (fileId, index) => { |
| | | uni.showLoading({ |
| | | title: "å é¤ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | qualityInspectFileDel([fileId]) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | // fileList.value.splice(index, 1); |
| | | getFileList(); |
| | | showToast("å 餿å"); |
| | | } else { |
| | | showToast("å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("å é¤å¤±è´¥"); |
| | | }); |
| | | }; |
| | | |
| | | // æ¾ç¤ºæç¤º |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | const rulesRegulationsManagementId = ref(""); |
| | | // 页é¢å è½½æ¶ |
| | | onMounted(() => { |
| | | rulesRegulationsManagementId.value = uni.getStorageSync( |
| | | "qualityInspectFileId" |
| | | ); |
| | | // ä» API è·åéä»¶å表 |
| | | getFileList(); |
| | | // 仿¬å°åå¨è·å rulesRegulationsManagementId |
| | | }); |
| | | |
| | | // è·åéä»¶å表 |
| | | const getFileList = () => { |
| | | uni.showLoading({ |
| | | title: "å è½½ä¸...", |
| | | mask: true, |
| | | }); |
| | | |
| | | qualityInspectFileListPage({ |
| | | inspectId: rulesRegulationsManagementId.value, |
| | | current: -1, |
| | | size: -1, |
| | | }) |
| | | .then(res => { |
| | | uni.hideLoading(); |
| | | if (res.code === 200) { |
| | | fileList.value = res.data.records || []; |
| | | } else { |
| | | showToast("è·åéä»¶å表失败"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | uni.hideLoading(); |
| | | showToast("è·åéä»¶å表失败"); |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .file-list-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 100rpx; |
| | | } |
| | | |
| | | .file-list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .file-list { |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | } |
| | | |
| | | .file-icon { |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-right: 20rpx; |
| | | |
| | | &.blue { |
| | | background: #409eff; |
| | | } |
| | | |
| | | &.green { |
| | | background: #67c23a; |
| | | } |
| | | |
| | | &.red { |
| | | background: #f56c6c; |
| | | } |
| | | |
| | | &.orange { |
| | | background: #e6a23c; |
| | | } |
| | | |
| | | &.gray { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.purple { |
| | | background: #909399; |
| | | } |
| | | |
| | | &.yellow { |
| | | background: #e6a23c; |
| | | } |
| | | } |
| | | |
| | | .file-info { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .file-name { |
| | | display: block; |
| | | font-size: 16px; |
| | | color: #303133; |
| | | margin-bottom: 8rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-meta { |
| | | display: block; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .file-actions { |
| | | display: flex; |
| | | gap: 12rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 100rpx 0; |
| | | background: #ffffff; |
| | | border-radius: 8rpx; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .upload-button { |
| | | position: fixed; |
| | | bottom: 40rpx; |
| | | right: 40rpx; |
| | | width: 130rpx; |
| | | height: 130rpx; |
| | | border-radius: 50%; |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .upload-text { |
| | | font-size: 10px; |
| | | color: #ffffff; |
| | | margin-top: 4rpx; |
| | | } |
| | | |
| | | .upload-progress { |
| | | padding: 40rpx 0; |
| | | } |
| | | |
| | | .upload-progress-text { |
| | | display: block; |
| | | text-align: center; |
| | | margin-top: 20rpx; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="material-inspection-page"> |
| | | <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> |
| | | <PageHeader title="è¿ç¨æ£éª" |
| | | @back="goBack" /> |
| | | <!-- æç´¢åºå --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="请è¾å
¥å·¥åºæç´¢" |
| | | v-model="searchForm.process" |
| | | @search="getList" |
| | | @custom="getList" |
| | | @clear="getList" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | ></up-search> |
| | | </view> |
| | | <!-- ç»è®¡ä¿¡æ¯å¡ç --> |
| | | <!-- <view class="stats-cards"> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ totalCount }}</text> |
| | | <text class="stat-label">æ»æ£éª</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ submittedCount }}</text> |
| | | <text class="stat-label">å·²æäº¤</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ pendingCount }}</text> |
| | | <text class="stat-label">å¾
æäº¤</text> |
| | | </view> |
| | | <view class="stat-card"> |
| | | <text class="stat-number">{{ qualifiedCount }}</text> |
| | | <text class="stat-label">å·²åæ ¼</text> |
| | | </view> |
| | | </view> --> |
| | | <!-- æ£éªå表 --> |
| | | <view class="inspection-list" |
| | | v-if="inspectionList.length > 0"> |
| | | <view v-for="(item, index) in inspectionList" |
| | | :key="index"> |
| | | <view class="inspection-item" |
| | | @click="viewDetail(item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <!-- <view class="material-icon" |
| | | :class="getStateClass(item.inspectState)"> |
| | | <up-icon :name="getStateIcon(item.inspectState)" |
| | | size="16" |
| | | color="#ffffff"></up-icon> |
| | | </view> --> |
| | | <view class="material-info"> |
| | | <text class="material-name">{{ item.productName }}</text> |
| | | <text class="material-code">{{ item.model }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="status-tags"> |
| | | <u-tag :type="getTagType(item.checkResult)" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.checkResult }} |
| | | </u-tag> |
| | | <u-tag :type="getStateTagType(item.inspectState)" |
| | | size="mini" |
| | | class="status-tag"> |
| | | {{ item.inspectState ? 'å·²æäº¤' : 'æªæäº¤' }} |
| | | </u-tag> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµæ¥æ</text> |
| | | <text class="detail-value">{{ formatDateTime(item.checkTime) || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">ç产工åå·</text> |
| | | <text class="detail-value">{{ item.workOrderNo || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">å·¥åº</text> |
| | | <text class="detail-value">{{ item.process || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£éªå</text> |
| | | <text class="detail-value">{{ item.checkName || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ°é</text> |
| | | <text class="detail-value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">æ£æµåä½</text> |
| | | <text class="detail-value">{{ item.checkCompany || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- æä½æé® --> |
| | | <view class="action-buttons"> |
| | | <!-- <u-button type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState" |
| | | @click.stop="startInspection(item)"> |
| | | ç¼è¾ |
| | | </u-button> --> |
| | | <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="viewDetail(item)"> |
| | | 详æ
|
| | | </u-button> |
| | | <!-- <u-button type="success" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState" |
| | | @click.stop="submitInspection(item)"> |
| | | æäº¤ |
| | | </u-button> --> |
| | | </view> |
| | | <view class="action-buttons"> |
| | | <!-- <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | @click.stop="viewFileList(item)"> |
| | | éä»¶ |
| | | </u-button> |
| | | <u-button type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | | :disabled="item.inspectState || item.checkName !== ''" |
| | | @click.stop="assignInspector(item)"> |
| | | åé
æ£éªå |
| | | </u-button> --> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="no-data"> |
| | | <up-empty mode="data" |
| | | text="ææ æ£éªä»»å¡"></up-empty> |
| | | </view> |
| | | <!-- å页ç»ä»¶ --> |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <!-- <view class="fab-button" |
| | | @click="addInspection"> |
| | | <up-icon name="plus" |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> --> |
| | | <!-- æ¥æéæ©å¨ --> |
| | | <up-popup v-model:show="showDate" |
| | | mode="date" |
| | | :start-year="2020" |
| | | :end-year="2030" |
| | | :range="true" |
| | | @confirm="confirmDate" /> |
| | | <!-- åé
æ£éªåå¼¹çª --> |
| | | <up-popup v-model:show="showAssignDialog" |
| | | mode="center" |
| | | round |
| | | style="width: 80%"> |
| | | <view class="assign-dialog"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">åé
æ£éªå</text> |
| | | <up-icon name="close" |
| | | size="20" |
| | | color="#999" |
| | | @click="showAssignDialog = false"></up-icon> |
| | | </view> |
| | | <view class="dialog-content"> |
| | | <up-form-item label="æ£éªå" |
| | | prop="checkName" |
| | | :label-width="60" |
| | | required> |
| | | <up-input v-model="assignForm.checkName" |
| | | placeholder="è¯·éæ©æ£éªå" |
| | | readonly /> |
| | | <template #right> |
| | | <up-icon @click="showInspectorSheet = true" |
| | | name="arrow-right" /> |
| | | </template> |
| | | </up-form-item> |
| | | </view> |
| | | <view class="dialog-footer"> |
| | | <u-button type="default" |
| | | class="footer-btn" |
| | | @click="showAssignDialog = false"> |
| | | åæ¶ |
| | | </u-button> |
| | | <u-button type="primary" |
| | | class="footer-btn" |
| | | @click="submitAssign"> |
| | | ç¡®å® |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | | <!-- æ£éªåéæ© --> |
| | | <up-action-sheet :show="showInspectorSheet" |
| | | :actions="userSheetOptions" |
| | | @select="selectInspector" |
| | | title="éæ©æ£éªå" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | submitQualityInspect, |
| | | qualityInspectUpdate, |
| | | qualityInspectListPage, |
| | | } from "@/api/qualityManagement/materialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | |
| | | // æ¾ç¤ºæç¤ºä¿¡æ¯ |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = ref({ |
| | | process: "", |
| | | entryDate: undefined, |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }); |
| | | |
| | | // æ¥æéæ©å¨ |
| | | const showDate = ref(false); |
| | | |
| | | // åé
æ£éªåå¼¹çª |
| | | const showAssignDialog = ref(false); |
| | | const showInspectorSheet = ref(false); |
| | | const assignForm = ref({ |
| | | checkName: "", |
| | | }); |
| | | const currentAssignRow = ref(null); |
| | | |
| | | // æ£éªåè¡¨æ°æ® |
| | | const inspectionList = ref([]); |
| | | |
| | | // åé¡µæ°æ® |
| | | const page = ref({ |
| | | current: -1, |
| | | size: -1, |
| | | total: 0, |
| | | }); |
| | | |
| | | // å è½½ç¶æ |
| | | const tableLoading = ref(false); |
| | | |
| | | // ç»è®¡æ°æ® |
| | | const totalCount = ref(0); |
| | | const submittedCount = ref(0); |
| | | const pendingCount = ref(0); |
| | | const qualifiedCount = ref(0); |
| | | |
| | | // æ£éªåå表 |
| | | const userList = ref([]); |
| | | |
| | | // ActionSheeté项 |
| | | const userSheetOptions = computed(() => { |
| | | return userList.value.map(item => ({ |
| | | name: item.nickName, |
| | | value: item.nickName, |
| | | })); |
| | | }); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = dateStr => { |
| | | if (!dateStr) return ""; |
| | | return dayjs(dateStr).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | // è·åç¶ææ ·å¼ |
| | | const getStateClass = inspectState => { |
| | | return inspectState ? "state-submitted" : "state-pending"; |
| | | }; |
| | | |
| | | // è·åç¶æå¾æ |
| | | const getStateIcon = inspectState => { |
| | | return inspectState ? "checkmark-circle" : "time"; |
| | | }; |
| | | |
| | | // è·åæ ç¾ç±»å |
| | | const getTagType = checkResult => { |
| | | if (checkResult === "åæ ¼") return "success"; |
| | | if (checkResult === "ä¸åæ ¼") return "error"; |
| | | return "default"; |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | const getStateTagType = inspectState => { |
| | | return inspectState ? "success" : "warning"; |
| | | }; |
| | | |
| | | // æ¾ç¤ºæ¥æéæ©å¨ |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // ç¡®è®¤æ¥æéæ© |
| | | const confirmDate = e => { |
| | | searchForm.value.entryDate = e.value; |
| | | searchForm.value.entryDateStart = dayjs(e.value[0]).format("YYYY-MM-DD"); |
| | | searchForm.value.entryDateEnd = dayjs(e.value[1]).format("YYYY-MM-DD"); |
| | | getList(); |
| | | }; |
| | | const viewFileList = item => { |
| | | uni.setStorageSync("qualityInspectFileId", item.id); |
| | | uni.navigateTo({ |
| | | url: "/pages/qualityManagement/processInspection/fileList", |
| | | }); |
| | | }; |
| | | |
| | | // æ¸
餿¥æèå´ |
| | | const clearDateRange = () => { |
| | | searchForm.value.entryDate = undefined; |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | getList(); |
| | | }; |
| | | |
| | | // è·åç¨æ·å表 |
| | | const getUserList = async () => { |
| | | try { |
| | | const userRes = await userListNoPage(); |
| | | userList.value = userRes.data || []; |
| | | } catch (e) { |
| | | console.error("å è½½æ£éªåå表失败", e); |
| | | userList.value = []; |
| | | } |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page.value }; |
| | | params.entryDate = undefined; |
| | | qualityInspectListPage({ ...params, inspectType: 1 }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | inspectionList.value = res.data.records || []; |
| | | page.value.total = res.data.total || 0; |
| | | totalCount.value = res.data.total || 0; |
| | | submittedCount.value = inspectionList.value.filter( |
| | | item => item.inspectState |
| | | ).length; |
| | | pendingCount.value = inspectionList.value.filter( |
| | | item => !item.inspectState |
| | | ).length; |
| | | qualifiedCount.value = inspectionList.value.filter( |
| | | item => item.checkResult === "åæ ¼" |
| | | ).length; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | console.error("è·åå表失败:", err); |
| | | showToast("è·åå表失败ï¼è¯·éè¯"); |
| | | }); |
| | | }; |
| | | |
| | | // ç¼è¾æ£éª |
| | | const startInspection = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | // åå¨å®æ´çæ£éªæ°æ® |
| | | uni.setStorageSync("processInspectionEditData", item); |
| | | // 跳转å°ç¼è¾é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/processInspection/add?id=${item.id}&isEdit=true`, |
| | | }); |
| | | }; |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetail = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | uni.setStorageSync("processInspectionEditData", item); |
| | | // 跳转å°è¯¦æ
é¡µé¢ |
| | | uni.navigateTo({ |
| | | url: `/pages/qualityManagement/processInspection/detail?id=${item.id}`, |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢æ£éª |
| | | const addInspection = () => { |
| | | uni.navigateTo({ |
| | | url: "/pages/qualityManagement/processInspection/add", |
| | | }); |
| | | }; |
| | | |
| | | // æäº¤æ£éª |
| | | const submitInspection = async item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | try { |
| | | const res = await submitQualityInspect({ id: item.id }); |
| | | if (res.code === 200) { |
| | | showToast("æäº¤æå"); |
| | | setTimeout(() => { |
| | | getList(); |
| | | }, 1000); |
| | | } else { |
| | | showToast("æäº¤å¤±è´¥ï¼" + (res.msg || "æªç¥é误")); |
| | | } |
| | | } catch (error) { |
| | | console.error("æäº¤å¤±è´¥:", error); |
| | | showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | // åé
æ£éªå |
| | | const assignInspector = item => { |
| | | if (!item) { |
| | | showToast("åæ°é误"); |
| | | return; |
| | | } |
| | | currentAssignRow.value = item; |
| | | getUserList(); |
| | | showAssignDialog.value = true; |
| | | }; |
| | | |
| | | // éæ©æ£éªå |
| | | const selectInspector = e => { |
| | | assignForm.value.checkName = e.value; |
| | | showInspectorSheet.value = false; |
| | | }; |
| | | |
| | | // æäº¤åé
|
| | | const submitAssign = async () => { |
| | | if (!currentAssignRow.value || !assignForm.value.checkName) { |
| | | showToast("è¯·éæ©æ£éªå"); |
| | | return; |
| | | } |
| | | try { |
| | | const data = { |
| | | ...assignForm.value, |
| | | id: currentAssignRow.value.id, |
| | | }; |
| | | const res = await qualityInspectUpdate(data); |
| | | if (res.code === 200) { |
| | | showToast("åé
æå"); |
| | | showAssignDialog.value = false; |
| | | setTimeout(() => { |
| | | getList(); |
| | | }, 1000); |
| | | } else { |
| | | showToast("åé
失败ï¼" + (res.msg || "æªç¥é误")); |
| | | } |
| | | } catch (error) { |
| | | console.error("åé
失败:", error); |
| | | showToast("åé
失败ï¼è¯·éè¯"); |
| | | } |
| | | }; |
| | | |
| | | // å¤çå页 |
| | | const handlePagination = obj => { |
| | | page.value.current = obj.current; |
| | | page.value.size = obj.size; |
| | | getList(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | getUserList(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "../../../styles/sales-common.scss"; |
| | | |
| | | .material-inspection-page { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | // æç´¢åºå |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | // ç»è®¡å¡ç |
| | | .stats-cards { |
| | | display: flex; |
| | | padding: 15px; |
| | | gap: 10px; |
| | | background: #fff; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .stat-card { |
| | | flex: 1; |
| | | background: #2979ff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | text-align: center; |
| | | color: #fff; |
| | | box-shadow: 0 2px 8px rgba(41, 121, 255, 0.2); |
| | | } |
| | | |
| | | .stat-number { |
| | | display: block; |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 12px; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | // æ£éªå表 |
| | | .inspection-list { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .inspection-item { |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | margin-bottom: 16px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | padding: 0 16px; |
| | | |
| | | &:active { |
| | | transform: scale(0.98); |
| | | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | } |
| | | |
| | | .item-header { |
| | | padding: 16px 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .material-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #2979ff; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .state-pending { |
| | | background: #ff9900; |
| | | } |
| | | |
| | | .state-submitted { |
| | | background: #52c41a; |
| | | } |
| | | |
| | | .material-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .material-name { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .material-code { |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-left: 8px; |
| | | } |
| | | |
| | | .status-tags { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .status-tag { |
| | | margin: 0; |
| | | } |
| | | |
| | | .date-range { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-top: 10px; |
| | | padding: 8px 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .date-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | // 详æ
è¡ |
| | | .item-details { |
| | | padding: 16px 0; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | margin-bottom: 8px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 12px; |
| | | color: #777777; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 12px; |
| | | color: #000000; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 16px; |
| | | } |
| | | |
| | | // æä½æé® |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 12px; |
| | | padding: 0 0 16px 0; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .action-btn { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | // ç©ºç¶æ |
| | | .no-data { |
| | | padding: 60px 20px; |
| | | text-align: center; |
| | | } |
| | | |
| | | // æµ®å¨æé® |
| | | .fab-button { |
| | | position: fixed; |
| | | bottom: 20px; |
| | | right: 20px; |
| | | width: 56px; |
| | | height: 56px; |
| | | background: #2979ff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | // åé
æ£éªåå¼¹çª |
| | | .assign-dialog { |
| | | padding: 24px; |
| | | background: #ffffff; |
| | | border-radius: 16px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .dialog-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 24px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-content { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | gap: 16px; |
| | | padding-top: 16px; |
| | | border-top: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .footer-btn { |
| | | flex: 1; |
| | | height: 44px; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | // è¾å
¥æ¡æ ·å¼ |
| | | :deep(.up-input__inner) { |
| | | border-radius: 8px; |
| | | height: 44px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | // 表åé¡¹æ ·å¼ |
| | | :deep(.up-form-item) { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | :deep(.up-form-item__label) { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | // æé®æ ·å¼ |
| | | :deep(.up-button--primary) { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | :deep(.up-button--default) { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | // å页ç»ä»¶ |
| | | .pagination { |
| | | padding: 20px; |
| | | background: #fff; |
| | | margin-top: 10px; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="raw-material-page"> |
| | | <PageHeader title="åææ" @back="goBack" /> |
| | | |
| | | <!-- æç´¢ä¸çé --> |
| | | <view class="search-section"> |
| | | <up-search |
| | | placeholder="请è¾å
¥æ¹å·" |
| | | v-model="searchForm.batchNo" |
| | | @search="handleQuery" |
| | | @custom="handleQuery" |
| | | @clear="handleQuery" |
| | | :show-action="true" |
| | | action-text="æç´¢" |
| | | :animation="true" |
| | | ></up-search> |
| | | <view class="filter-row"> |
| | | <view class="filter-item" @click="showTypeSelect = true"> |
| | | <text>{{ typeLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | <view class="filter-item" @click="showStatusSelect = true"> |
| | | <text>{{ statusLabel }}</text> |
| | | <up-icon name="arrow-down" size="14" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å表åºå --> |
| | | <view class="list-container" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="index" class="list-item"> |
| | | <view class="item-header"> |
| | | <text class="product-name">{{ item.productName }}</text> |
| | | <up-tag :text="item.inspectState ? 'å·²æäº¤' : 'æªæäº¤'" :type="item.inspectState ? 'success' : 'warning'" size="mini"></up-tag> |
| | | </view> |
| | | <view class="item-content"> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ¹æ¬¡å·ï¼</text> |
| | | <text class="item-value">{{ item.batchNo }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ£éªç±»åï¼</text> |
| | | <text class="item-value">{{ item.checkTypeText || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">è§æ ¼åå·ï¼</text> |
| | | <text class="item-value">{{ item.model || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ£æµæ¥æï¼</text> |
| | | <text class="item-value">{{ item.checkTime || '-' }}</text> |
| | | </view> |
| | | <view class="item-row"> |
| | | <text class="item-label">æ£æµç»æï¼</text> |
| | | <up-tag :text="item.checkResult === 1 ? 'åæ ¼' : 'ä¸åæ ¼'" :type="item.checkResult === 1 ? 'success' : 'error'" size="mini"></up-tag> |
| | | </view> |
| | | </view> |
| | | <view class="item-actions"> |
| | | <up-button v-if="!item.inspectState" type="primary" size="mini" @click.stop="openForm('edit', item)">ç¼è¾</up-button> |
| | | <up-button v-if="!item.inspectState" type="success" size="mini" @click.stop="handleConfirmSubmit(item)">æäº¤</up-button> |
| | | <up-button type="error" size="mini" @click.stop="handleDelete(item)">å é¤</up-button> |
| | | </view> |
| | | </view> |
| | | <view class="pagination-container"> |
| | | <up-loadmore :status="loadStatus" @loadmore="getList" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <up-empty mode="data" text="ææ æ°æ®"></up-empty> |
| | | </view> |
| | | |
| | | <!-- æµ®å¨æ°å¢æé® --> |
| | | <view class="fab-button" @click="openForm('add')"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | | </view> |
| | | |
| | | <!-- ç±»åéæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="typeActions" |
| | | :show="showTypeSelect" |
| | | @close="showTypeSelect = false" |
| | | @select="selectType" |
| | | title="è¯·éæ©ç±»å«" |
| | | ></up-action-sheet> |
| | | |
| | | <!-- ç¶æéæ©å¨ --> |
| | | <up-action-sheet |
| | | :actions="statusActions" |
| | | :show="showStatusSelect" |
| | | @close="showStatusSelect = false" |
| | | @select="selectStatus" |
| | | title="è¯·éæ©æäº¤ç¶æ" |
| | | ></up-action-sheet> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from 'vue'; |
| | | import { |
| | | findRawMaterialListPage, |
| | | submitRawMaterial, |
| | | deleteRawMaterial |
| | | } from '@/api/qualityManagement/rawMaterial.js'; |
| | | import { toast, showConfirm } from '@/utils/common'; |
| | | import useUserStore from '@/store/modules/user'; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | const searchForm = reactive({ |
| | | batchNo: '', |
| | | checkType: '', |
| | | inspectState: '' |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | | total: 0 |
| | | }); |
| | | const loadStatus = ref('loadmore'); |
| | | |
| | | const showTypeSelect = ref(false); |
| | | const typeActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'å
¥åæ£', value: '0' }, |
| | | { name: 'è½¦é´æ£', value: '1' }, |
| | | { name: 'åºåæ£', value: '2' } |
| | | ]; |
| | | const typeLabel = computed(() => { |
| | | const action = typeActions.find(a => a.value === searchForm.checkType); |
| | | return action ? action.name : 'å
¨é¨ç±»å«'; |
| | | }); |
| | | |
| | | const showStatusSelect = ref(false); |
| | | const statusActions = [ |
| | | { name: 'å
¨é¨', value: '' }, |
| | | { name: 'æªæäº¤', value: '0' }, |
| | | { name: 'å·²æäº¤', value: '1' } |
| | | ]; |
| | | const statusLabel = computed(() => { |
| | | const action = statusActions.find(a => a.value === searchForm.inspectState); |
| | | return action ? action.name : 'å
¨é¨ç¶æ'; |
| | | }); |
| | | |
| | | const getList = () => { |
| | | if (loadStatus.value === 'loading' || (page.total > 0 && tableData.value.length >= page.total)) return; |
| | | |
| | | loadStatus.value = 'loading'; |
| | | const params = { |
| | | batchNo: searchForm.batchNo || null, |
| | | checkType: searchForm.checkType || null, |
| | | inspectState: searchForm.inspectState || null, |
| | | current: page.current, |
| | | size: page.size |
| | | }; |
| | | |
| | | findRawMaterialListPage(params).then(res => { |
| | | const records = res?.data?.records || []; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | } |
| | | page.total = res?.data?.total || 0; |
| | | |
| | | if (tableData.value.length >= page.total) { |
| | | loadStatus.value = 'nomore'; |
| | | } else { |
| | | loadStatus.value = 'loadmore'; |
| | | page.current++; |
| | | } |
| | | }).catch(() => { |
| | | loadStatus.value = 'loadmore'; |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | page.total = 0; |
| | | tableData.value = []; |
| | | loadStatus.value = 'loadmore'; |
| | | getList(); |
| | | }; |
| | | |
| | | const selectType = (e) => { |
| | | searchForm.checkType = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const selectStatus = (e) => { |
| | | searchForm.inspectState = e.value; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const openForm = (type, item) => { |
| | | // Mobile usually navigates to a new page for add/edit if complex |
| | | // Here we'll just show a toast for now as the actual form components are many |
| | | toast('åè½å¼åä¸ï¼è¯·å¨PC端æä½'); |
| | | }; |
| | | |
| | | const handleConfirmSubmit = (row) => { |
| | | showConfirm('确认æäº¤è¯¥æ£éªè®°å½åï¼').then(res => { |
| | | if (res.confirm) { |
| | | submitRawMaterial(row.id).then(() => { |
| | | toast('æäº¤æå'); |
| | | handleQuery(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | showConfirm('确认å é¤è¯¥è®°å½åï¼').then(res => { |
| | | if (res.confirm) { |
| | | deleteRawMaterial({ id: row.id }).then(() => { |
| | | toast('å 餿å'); |
| | | handleQuery(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | handleQuery(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .raw-material-page { |
| | | padding-bottom: 20rpx; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .search-section { |
| | | padding: 20rpx 30rpx; |
| | | background-color: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .filter-row { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | padding: 10rpx 0; |
| | | } |
| | | |
| | | .filter-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | font-size: 28rpx; |
| | | color: #606266; |
| | | } |
| | | |
| | | .list-container { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .product-name { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .item-content { |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .item-row { |
| | | display: flex; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .item-label { |
| | | color: #909399; |
| | | width: 180rpx; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .item-actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 20rpx; |
| | | border-top: 1rpx solid #ebeef5; |
| | | padding-top: 20rpx; |
| | | } |
| | | |
| | | .no-data { |
| | | padding-top: 200rpx; |
| | | } |
| | | |
| | | .fab-button { |
| | | position: fixed; |
| | | right: 40rpx; |
| | | bottom: 60rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background-color: #3c9cff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4rpx 16rpx rgba(60, 156, 255, 0.4); |
| | | z-index: 99; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="quality-dashboard-page"> |
| | | <PageHeader title="è´¨éçæ¿" @back="goBack" /> |
| | | |
| | | <scroll-view scroll-y class="dashboard-scroll"> |
| | | <!-- æ ·åç¶æå表 --> |
| | | <view class="dashboard-card"> |
| | | <view class="card-header"> |
| | | <text class="card-title">æ£æµæ ·åå¨æç¶æ</text> |
| | | <up-switch v-model="voiceEnabled" size="18" activeText="è¯é³" inactiveText="éé³"></up-switch> |
| | | </view> |
| | | <view class="status-list"> |
| | | <view v-for="item in sampleStatus" :key="item.id" class="status-item"> |
| | | <view class="status-left"> |
| | | <view class="status-dot" :class="item.status"></view> |
| | | <text class="sample-name">{{ item.name }}</text> |
| | | </view> |
| | | <view class="status-right"> |
| | | <up-tag :text="statusLabel(item.status)" :type="statusTagType(item.status)" size="mini"></up-tag> |
| | | <text class="sample-time">{{ item.time }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åæ ¼çåæ (仪表ç) --> |
| | | <view class="dashboard-card"> |
| | | <view class="card-header"> |
| | | <text class="card-title">åæ ¼çåæ</text> |
| | | </view> |
| | | <view class="chart-box"> |
| | | <qiun-data-charts |
| | | type="gauge" |
| | | :opts="gaugeOpts" |
| | | :chartData="gaugeData" |
| | | /> |
| | | </view> |
| | | <view class="passrate-summary"> |
| | | <text>å½ååæ ¼çï¼</text> |
| | | <text class="highlight">{{ (passRate * 100).toFixed(1) }}%</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- ä»»å¡æè¡ (æ±ç¶å¾) --> |
| | | <view class="dashboard-card"> |
| | | <view class="card-header"> |
| | | <text class="card-title">ä»»å¡æè¡ (Top 10)</text> |
| | | </view> |
| | | <view class="chart-box"> |
| | | <qiun-data-charts |
| | | type="column" |
| | | :opts="columnOpts" |
| | | :chartData="columnData" |
| | | /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åå²è¶å¿ (æçº¿å¾) --> |
| | | <view class="dashboard-card"> |
| | | <view class="card-header"> |
| | | <text class="card-title">åå²è¶å¿</text> |
| | | </view> |
| | | <view class="chart-box"> |
| | | <qiun-data-charts |
| | | type="line" |
| | | :opts="lineOpts" |
| | | :chartData="lineData" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'; |
| | | |
| | | const voiceEnabled = ref(false); |
| | | let dataTimer = null; |
| | | |
| | | // 1) æ ·åå¨æç¶æ |
| | | const sampleStatus = ref([]); |
| | | const statusPool = ['processing', 'warning', 'error', 'success']; |
| | | |
| | | const statusLabel = (s) => { |
| | | const labels = { 'processing': 'æ£æµä¸', 'warning': 'é¢è¦', 'error': 'ä¸åæ ¼', 'success': 'åæ ¼' }; |
| | | return labels[s] || 'æªç¥'; |
| | | }; |
| | | |
| | | const statusTagType = (s) => { |
| | | const types = { 'processing': 'primary', 'warning': 'warning', 'error': 'error', 'success': 'success' }; |
| | | return types[s] || 'info'; |
| | | }; |
| | | |
| | | const randomSample = () => { |
| | | const id = Math.random().toString(36).slice(2, 8); |
| | | const status = statusPool[Math.floor(Math.random() * statusPool.length)]; |
| | | const name = `æ ·å-${Math.floor(Math.random() * 900 + 100)}`; |
| | | const time = new Date().toLocaleTimeString('zh-CN', { hour12: false }); |
| | | return { id, name, status, time }; |
| | | }; |
| | | |
| | | // 2) åæ ¼çåæ (仪表ç) |
| | | const passRate = ref(0.92); |
| | | const gaugeData = ref({ |
| | | categories: [{ value: 0.92, color: "#2fc25b" }], |
| | | series: [{ name: "åæ ¼ç", data: 0.92 }] |
| | | }); |
| | | const gaugeOpts = { |
| | | title: { name: "åæ ¼ç", color: "#2fc25b", fontSize: 16 }, |
| | | subtitle: { name: "92%", color: "#666666", fontSize: 12 }, |
| | | extra: { gauge: { type: "default", width: 15, labelColor: "#666666", splitLine: { fixRadius: -10 } } } |
| | | }; |
| | | |
| | | // 3) ä»»å¡æè¡ (æ±ç¶å¾) |
| | | const columnData = ref({ |
| | | categories: ["ä»»å¡1", "ä»»å¡2", "ä»»å¡3", "ä»»å¡4", "ä»»å¡5"], |
| | | series: [{ name: "宿æ°", data: [35, 36, 31, 33, 13] }] |
| | | }); |
| | | const columnOpts = { |
| | | color: ["#1890FF"], |
| | | padding: [15, 15, 0, 5], |
| | | enableScroll: false, |
| | | xAxis: { disableGrid: true }, |
| | | yAxis: { data: [{ min: 0 }] }, |
| | | extra: { column: { type: "group", width: 30, activeBgColor: "#000000", activeBgOpacity: 0.08 } } |
| | | }; |
| | | |
| | | // 4) åå²è¶å¿ (æçº¿å¾) |
| | | const lineData = ref({ |
| | | categories: ["10:00", "10:05", "10:10", "10:15", "10:20"], |
| | | series: [ |
| | | { name: "æ¥æ ·æ°", data: [35, 8, 25, 37, 4, 20] }, |
| | | { name: "宿æ°", data: [70, 40, 65, 100, 44, 68] } |
| | | ] |
| | | }); |
| | | const lineOpts = { |
| | | color: ["#1890FF", "#91CB74"], |
| | | padding: [15, 10, 0, 15], |
| | | enableScroll: false, |
| | | xAxis: { disableGrid: true }, |
| | | yAxis: { gridType: "dash", dashLength: 2 }, |
| | | legend: { position: "top" }, |
| | | extra: { line: { type: "straight", width: 2 } } |
| | | }; |
| | | |
| | | const refreshData = () => { |
| | | // æ¨¡ææ°æ®æ´æ° |
| | | const next = randomSample(); |
| | | sampleStatus.value = [next, ...sampleStatus.value].slice(0, 5); |
| | | |
| | | const delta = (Math.random() - 0.5) * 0.02; |
| | | passRate.value = Math.min(0.99, Math.max(0.6, passRate.value + delta)); |
| | | gaugeData.value = { |
| | | series: [{ name: "åæ ¼ç", data: passRate.value }] |
| | | }; |
| | | gaugeOpts.subtitle.name = (passRate.value * 100).toFixed(1) + '%'; |
| | | }; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | for (let i = 0; i < 5; i++) { |
| | | sampleStatus.value.push(randomSample()); |
| | | } |
| | | dataTimer = setInterval(refreshData, 3000); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | if (dataTimer) clearInterval(dataTimer); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .quality-dashboard-page { |
| | | background-color: #f5f7fa; |
| | | height: 100vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .dashboard-scroll { |
| | | flex: 1; |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .dashboard-card { |
| | | background-color: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 30rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 30rpx; |
| | | border-left: 8rpx solid #3c9cff; |
| | | padding-left: 20rpx; |
| | | } |
| | | |
| | | .card-title { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .status-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | .status-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 10rpx 0; |
| | | } |
| | | |
| | | .status-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 15rpx; |
| | | } |
| | | |
| | | .status-dot { |
| | | width: 12rpx; |
| | | height: 12rpx; |
| | | border-radius: 50%; |
| | | |
| | | &.processing { background-color: #3c9cff; } |
| | | &.warning { background-color: #f9ae3d; } |
| | | &.error { background-color: #f56c6c; } |
| | | &.success { background-color: #5ac725; } |
| | | } |
| | | |
| | | .sample-name { |
| | | font-size: 28rpx; |
| | | color: #303133; |
| | | } |
| | | |
| | | .status-right { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | .sample-time { |
| | | font-size: 24rpx; |
| | | color: #909399; |
| | | } |
| | | |
| | | .chart-box { |
| | | width: 100%; |
| | | height: 400rpx; |
| | | } |
| | | |
| | | .passrate-summary { |
| | | text-align: center; |
| | | margin-top: 20rpx; |
| | | font-size: 28rpx; |
| | | color: #606266; |
| | | |
| | | .highlight { |
| | | font-size: 36rpx; |
| | | font-weight: bold; |
| | | color: #3c9cff; |
| | | margin-left: 10rpx; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * åçè½åå°è£
ï¼æ¯ææ¡ä»¶ç¼è¯ï¼ |
| | | */ |
| | | |
| | | /** |
| | | * æ«ç |
| | | */ |
| | | export function scanCode(): Promise<any> { |
| | | return new Promise((resolve, reject) => { |
| | | // #ifdef APP-PLUS || MP-WEIXIN |
| | | uni.scanCode({ |
| | | success: (res) => resolve(res), |
| | | fail: (err) => reject(err) |
| | | }); |
| | | // #endif |
| | | |
| | | // #ifdef H5 |
| | | // H5端é常éè¦éè¿å¾®ä¿¡JS-SDKæç¹å®çæ«ç åº |
| | | uni.showToast({ title: 'H5端æ«ç 鿥å
¥å¾®ä¿¡JS-SDK', icon: 'none' }); |
| | | reject(new Error('H5 scan not implemented')); |
| | | // #endif |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * æç
§æéæ©å¾ç |
| | | */ |
| | | export function chooseImage(count: number = 1): Promise<any> { |
| | | return new Promise((resolve, reject) => { |
| | | uni.chooseImage({ |
| | | count, |
| | | sizeType: ['compressed'], |
| | | sourceType: ['camera', 'album'], |
| | | success: (res) => resolve(res), |
| | | fail: (err) => reject(err) |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * èçåå§åï¼ç¤ºä¾ï¼ |
| | | */ |
| | | export function initBluetooth(): Promise<any> { |
| | | return new Promise((resolve, reject) => { |
| | | // #ifdef APP-PLUS || MP-WEIXIN |
| | | uni.openBluetoothAdapter({ |
| | | success: (res) => resolve(res), |
| | | fail: (err) => reject(err) |
| | | }); |
| | | // #endif |
| | | |
| | | // #ifdef H5 |
| | | reject(new Error('H5 does not support bluetooth')); |
| | | // #endif |
| | | }); |
| | | } |