Răsfoiți Sursa

Merge branch 'master' of http://git.yujin.shuziyunyao.com/yujin/forestry-wx

lisy 5 zile în urmă
părinte
comite
cf3fd7a958

+ 15 - 13
src/components/ut-album/ut-album.vue

@@ -1,7 +1,7 @@
 <template>
     <view class="ut-album-card" :style="albumStyle">
         <template v-for="(item, index) in displayItems" :key="index">
-            <view class="card-item" :style="{ width: itemSize, height: itemSize }" @click="onPreview(index)">
+            <view class="card-item" :style="{ width: itemSize, height: itemSize }" @click="onItemClick(item, index)">
                 <!-- 图片缩略图 -->
                 <template v-if="item.type === 'image'">
                     <image class="thumb" :src="getThumb(item)" mode="aspectFill" />
@@ -11,8 +11,17 @@
                 <template v-else-if="item.type === 'video'">
                     <view class="video-box">
                         <image v-if="item.coverUrl" class="thumb" :src="item.coverUrl" mode="aspectFill" />
-                        <view v-else class="video-placeholder">VIDEO</view>
-                        <view class="play-mask">
+                        <video
+                            v-else
+                            class="thumb native-video"
+                            :src="item.url"
+                            object-fit="cover"
+                            :controls="false"
+                            :show-center-play-btn="true"
+                            :show-play-btn="false"
+                            :enable-play-gesture="true"
+                        />
+                        <view v-if="item.coverUrl" class="play-mask">
                             <up-icon name="play-circle" color="#fff" size="48rpx"></up-icon>
                         </view>
                     </view>
@@ -141,9 +150,9 @@ const formatSize = (bytes) => {
 };
 
 // 预览逻辑:图片/视频/文件
-const onPreview = (displayIndex) => {
-    const item = displayItems.value[displayIndex];
+const onItemClick = (item, displayIndex) => {
     if (!item) return;
+    if (item.type === 'video' && !item.coverUrl) return;
     if (item.type === 'image') return previewImages(item);
     if (item.type === 'video') return previewVideo(item, displayIndex);
     return previewFile(item);
@@ -222,15 +231,8 @@ const previewFile = (item) => {
             width: 100%;
             height: 100%;
             position: relative;
-            .video-placeholder {
-                width: 100%;
-                height: 100%;
-                color: #fff;
+            .native-video {
                 background: #000;
-                display: flex;
-                align-items: center;
-                justify-content: center;
-                font-size: 28rpx;
             }
             .play-mask {
                 position: absolute;

+ 15 - 0
src/pages.json

@@ -694,6 +694,14 @@
                     "style": {
                         "navigationBarTitleText": "选择追溯码码段"
                     }
+                },
+                // 打开预览view-web-view
+                {
+                    "path": "webview/index",
+                    "style": {
+                        "navigationBarTitleText": "预览webview页面",
+                        "navigationStyle": "custom"
+                    }
                 }
             ]
         },
@@ -725,6 +733,13 @@
                         "navigationBarTitleText": "码生成及管理"
                     }
                 },
+                // 追溯码查询
+                {
+                    "path": "code-query/index",
+                    "style": {
+                        "navigationBarTitleText": "追溯码查询"
+                    }
+                },
                 {
                     "path": "code-detail-list/index",
                     "style": {

+ 2 - 2
src/pages/plant/more/index.vue

@@ -11,11 +11,11 @@
                         <image class="w-54 h-54 mb-16" src="https://yujin-szyy.oss-cn-chengdu.aliyuncs.com/szyy/images-plt/plant/more/mag_code_icon.png" mode="widthFix" />
                         <view class="f-s-26 c-#666">码生成及下载</view>
                     </view>
-                    <view class="d-flex flex-cln j-c a-c pd-10" hover-class="bg-#f7f7f7">
+                    <view @click="$u.route({ url: '/plant/code/code-query/index' })" class="d-flex flex-cln j-c a-c pd-10" hover-class="bg-#f7f7f7">
                         <image class="w-54 h-54 mb-16" src="https://yujin-szyy.oss-cn-chengdu.aliyuncs.com/szyy/images-plt/plant/more/mag_trace_icon.png" mode="widthFix" />
                         <view class="f-s-26 c-#666">追溯码查询</view>
                     </view>
-                    <view class="d-flex flex-cln j-c a-c pd-10" hover-class="bg-#f7f7f7">
+                    <view @click="$u.route({ url: '/plant/code/code-relate/index' })" class="d-flex flex-cln j-c a-c pd-10" hover-class="bg-#f7f7f7">
                         <image class="w-54 h-54 mb-16" src="https://yujin-szyy.oss-cn-chengdu.aliyuncs.com/szyy/images-plt/plant/more/mag_relate_icon.png" mode="widthFix" />
                         <view class="f-s-26 c-#666">关联关系管理</view>
                     </view>

+ 1 - 2
src/plant/code/code-list/index.vue

@@ -57,7 +57,6 @@
 
 <script setup lang="ts">
 import { useClientRequest } from '@/utils/request';
-import { exportDataFn } from '@/utils/upload';
 import CodeItem from './models/code-item.vue';
 import GenerateDialog from './models/generate-dialog.vue';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -153,7 +152,7 @@ const handleDownload = async (item: any) => {
     }
     try {
         const downloadUrl = `/plt-api/app/traceCodeLog/download/${item.id}`;
-        await exportDataFn(
+        await useClientRequest.down(
             {
                 url: downloadUrl,
                 fileName: `traceCode_${item.id}.xlsx`,

+ 3 - 0
src/plant/code/code-query/index.vue

@@ -0,0 +1,3 @@
+<template>
+    <view>xx</view>
+</template>

+ 15 - 16
src/plant/export-print/models/export-print-confirm.vue

@@ -3,23 +3,15 @@
         <up-form class="p-rtv bg-#fff" labelPosition="top" :model="form" errorType="toast" :rules="rules" labelWidth="auto" ref="upFormUpRef">
             <view class="f-s-24 c-#999">导出追溯码仅支持导出追溯码地址用于喷印或排版好追溯码PDF文件直接打印。如有疑问,请。如有疑问,请<span class="c-primary">点击此处联系客服</span>。</view>
             <slot name="fileType"></slot>
-            <template v-if="opts.id">
+            <ut-action-sheet v-model="form.extraInfo.fileType" :tabs="pt_code_export_type" @change="changeExtraFileType" mode="custom" title="请选择导出类型">
                 <up-form-item borderBottom label="导出类型" required prop="extraInfo.fileType">
                     <view v-if="form.extraInfo?.fileType" class="f-s-30 c-333 f-w-5 flex1">{{ selectDictLabel(pt_code_export_type, form.extraInfo.fileType) }}</view>
                     <view v-else class="f-s-30 c-ccc f-w-4 flex1">请选择导出类型</view>
+                    <template #right>
+                        <up-icon size="22rpx" color="#37A954" name="arrow-down-fill"></up-icon>
+                    </template>
                 </up-form-item>
-            </template>
-            <template v-else>
-                <ut-action-sheet v-model="form.extraInfo.fileType" :tabs="pt_code_export_type" @change="changeExtraFileType" mode="custom" title="请选择导出类型">
-                    <up-form-item borderBottom label="导出类型" required prop="extraInfo.fileType">
-                        <view v-if="form.extraInfo?.fileType" class="f-s-30 c-333 f-w-5 flex1">{{ selectDictLabel(pt_code_export_type, form.extraInfo.fileType) }}</view>
-                        <view v-else class="f-s-30 c-ccc f-w-4 flex1">请选择导出类型</view>
-                        <template #right>
-                            <up-icon size="22rpx" color="#37A954" name="arrow-down-fill"></up-icon>
-                        </template>
-                    </up-form-item>
-                </ut-action-sheet>
-            </template>
+            </ut-action-sheet>
 
             <template v-if="form.extraInfo.fileType == '2'">
                 <slot name="logos"></slot>
@@ -262,12 +254,19 @@ const submitForm = async () => {
     };
     const res = props.opts?.id ? await useClientRequest.post(`/plt-api/app/printLog/update/${props.opts.id}`, payload?.extraInfo) : await useClientRequest.post('/plt-api/app/printLog/printNext', payload);
     if (!res || res.code !== 200) return;
-    console.log(res);
-    
     uni.$emit('refreshPackTaskList');
     uni.$emit('refreshPackTaskDetail');
-    await useClientRequest.get(`/plt-api/app/printLog/export/${res.data?.id || props.opts?.id}`);
     uni.hideLoading();
+    if (payload.extraInfo.fileType === '1') {
+        await useClientRequest.down(
+            {
+                url: `/plt-api/app/printLog/export/${res.data?.id || props.opts?.id}`,
+            },
+            '正在导出...',
+        );
+    } else {
+        await useClientRequest.get(`/plt-api/app/printLog/exportLayout/${res.data?.id || props.opts?.id}`);
+    }
     uni.showToast({
         title: '导出成功',
         icon: 'success',

+ 5 - 1
src/plant/models/batch-info.vue

@@ -6,7 +6,7 @@
         </view>
         <view class="f-s-30 pd2-16-0 info-border-bottom">
             <view class="c-#666 mb-10">基原:<span v-if="!info?.varietyInfo" class="c-#333">-</span></view>
-            <view v-if="info?.varietyInfo" class="bg-#FBFDFB card-info-block pd-24 p-rtv">
+            <view v-if="info?.varietyInfo" class="bg-#FBFDFB pd-24 p-rtv b-radius border-#AFDDBB">
                 <view class="mb-10">
                     <span class="f-s-34 c-#333 f-w-5 mr-16">{{ info?.varietyInfo?.varietyName }}</span>
                     <span class="f-s-24 c-#666">{{ info?.varietyInfo?.latinName }}</span>
@@ -53,6 +53,10 @@
             <span class="c-#666">是否符合GAP要求:</span>
             <span class="c-#333">{{ +info?.gapFlag ? '是' : '否' }}</span>
         </view>
+        <view @click="$u.route({ url: '/tools/webview/index', params: { title: '溯源信息', url: info?.packBatchUrl } })" class="d-flex flex-cln j-c a-c pd-30">
+            <image class="w-140 h-140" src="https://yujin-szyy.oss-cn-chengdu.aliyuncs.com/szyy/images-plt/print-codes/code_logo_export.png" mode="widthFix" />
+            <view class="f-s-26 c-#333">点击查看溯源信息</view>
+        </view>
     </view>
 </template>
 <script setup lang="ts">

+ 0 - 2
src/tools/view-html/index.vue

@@ -26,8 +26,6 @@ onLoad((options) => {
         url: obj.url,
         success: (res: any) => {
             // 只要body内容
-            console.log(res);
-            
             content.value = res.data as string;
         },
     });

+ 17 - 0
src/tools/webview/index.vue

@@ -0,0 +1,17 @@
+<template>
+    <web-view :src="url"></web-view>
+</template>
+<script setup lang="ts">
+import { recursiveDecodeURIComponentSimple } from '@/utils/ruoyi';
+const content = ref<string>('');
+const title = ref('');
+const paging = ref();
+const url = ref('');
+onLoad((options) => {
+    // 设置标题
+    const obj = recursiveDecodeURIComponentSimple(options);
+    uni.setNavigationBarTitle({ title: obj?.title || '-' });
+    title.value = obj.title || '-';
+    url.value = obj.url || '';
+});
+</script>

+ 109 - 0
src/utils/request.ts

@@ -1,4 +1,5 @@
 import config from '@/config';
+import { tansParams } from '@/utils/common';
 import { useInfoStore } from '@/store';
 import { getCurrentPage } from '@/utils/public';
 import { recursiveDecodeURIComponent } from '@/utils/ruoyi';
@@ -7,6 +8,14 @@ const { clientId, appid } = config;
 // uniapp封装的请求方法
 let timeout = 60 * 1000;
 
+interface ClientDownloadConfig {
+    url: string;
+    fileName?: string;
+    params?: Record<string, any>;
+    timeout?: number;
+    header?: Record<string, any>;
+}
+
 // 获取全局请求头方法
 const getHeader = () => {
     let header = {
@@ -18,6 +27,103 @@ const getHeader = () => {
     return header;
 };
 
+const getFileType = (url: string, result?: { header?: Record<string, any> }): string => {
+    if (url) {
+        const filenameFromUrl = url.split('/').pop();
+        const urlExt = filenameFromUrl?.includes('.') ? filenameFromUrl.split('.').pop()?.split(/[?#]/)[0] : null;
+        if (urlExt) return urlExt.toLowerCase() || 'pdf';
+    }
+
+    if (result?.header?.['content-disposition']) {
+        try {
+            const disposition = result.header['content-disposition'];
+            const filenameMatch = disposition.match(/filename\*?=(?:UTF-8''|")?([^;"\n]*)/i);
+
+            if (filenameMatch && filenameMatch[1]) {
+                const filename = filenameMatch[1].replace(/['"]/g, '');
+                if (filename.includes('.')) {
+                    const ext = filename.split('.').pop()?.split(/[?#]/)[0];
+                    return ext?.toLowerCase() || 'pdf';
+                }
+            }
+        } catch (e) {
+            console.warn('解析 Content-Disposition 失败:', e);
+        }
+    }
+
+    return 'pdf';
+};
+
+const buildDownloadUrl = (url: string, params?: Record<string, any>) => {
+    const isFullUrl = url.startsWith('http://') || url.startsWith('https://');
+    let downloadUrl = (isFullUrl ? '' : import.meta.env.VITE_API_BASE_URL || 'http://192.168.1.26:8080') + url;
+
+    if (params && Object.keys(params).length) {
+        const query = tansParams(params).slice(0, -1);
+        if (query) {
+            downloadUrl += `${downloadUrl.includes('?') ? '&' : '?'}${query}`;
+        }
+    }
+
+    return downloadUrl;
+};
+
+const download = (downloadConfig: ClientDownloadConfig, showTitle?: string): Promise<void> =>
+    new Promise((resolve, reject) => {
+        const downloadUrl = buildDownloadUrl(downloadConfig.url, downloadConfig.params);
+
+        uni.showLoading({
+            title: showTitle || '正在导出数据...',
+            mask: true,
+        });
+
+        uni.downloadFile({
+            url: downloadUrl,
+            timeout: downloadConfig.timeout || timeout,
+            header: {
+                ...getHeader(),
+                ...downloadConfig.header,
+            },
+            success: (res) => {
+                console.log(res);
+                
+                uni.hideLoading();
+                if (res.statusCode === 200) {
+                    uni.openDocument({
+                        filePath: res.tempFilePath,
+                        showMenu: true,
+                        fileType: getFileType(downloadConfig.fileName || downloadConfig.url, res as any) as any,
+                        success: () => {
+                            resolve();
+                        },
+                        fail: (err) => {
+                            uni.showToast({
+                                title: '打开文件失败',
+                                icon: 'none',
+                            });
+                            reject(err);
+                        },
+                    });
+                    return;
+                }
+
+                uni.showToast({
+                    title: '下载失败',
+                    icon: 'none',
+                });
+                reject(res);
+            },
+            fail: (error) => {
+                uni.hideLoading();
+                uni.showToast({
+                    title: '下载失败',
+                    icon: 'none',
+                });
+                reject(error);
+            },
+        });
+    });
+
 // 获取host地址
 export const request = ({ url, method = 'GET', data = {}, isToken = true, header = null }: any) => {
     const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://192.168.1.26:8080';
@@ -157,4 +263,7 @@ export const useClientRequest = {
             isToken,
         }) as Promise<T>;
     },
+    down: (config: ClientDownloadConfig, showTitle?: string): Promise<void> => {
+        return download(config, showTitle);
+    },
 };