huangxw 1 týždeň pred
rodič
commit
84f20c8631

+ 45 - 40
src/components/ut-suspension/ut-suspension.vue

@@ -1,19 +1,18 @@
 <template>
-    <movable-area class="suspension-area" :style="areaStyle">
+    <movable-area v-if="inited" class="suspension-area" :style="areaStyle">
         <movable-view
             class="suspension-view"
             direction="all"
             :x="x"
             :y="y"
-            :inertia="props.inertia"
+            :inertia="inertia"
             :out-of-bounds="true"
             :disabled="false"
             :scale="false"
             :style="{
-                width: `${props.imageWidth}px`,
-                height: `${props.imageHeight}px`,
+                width: width,
+                height: height,
             }"
-            @change="onChange"
             @click="handleClick">
             <slot />
         </movable-view>
@@ -21,41 +20,54 @@
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted, computed } from 'vue';
 
+
+/**
+ * Props
+ * - width: 组件宽度字符串,支持 rpx/px,例如 '120rpx'
+ * - height: 组件高度字符串,支持 rpx/px,例如 '120rpx'
+ * - right: 距右侧偏移,支持 rpx/px,例如 '20rpx'
+ * - bottom: 距底部偏移,支持 rpx/px,例如 '200rpx'
+ * - inertia/snapThreshold/bgColor/borderRadius: 其他配置
+ * 行为:初始化后根据屏幕宽高与 right/bottom 计算 x/y,默认右下角,避免渲染闪动
+ */
 // 定义props
 interface Props {
-    imageSrc?: string;
-    imageWidth?: number; // 使用px单位,不再支持rpx
-    imageHeight?: number; // 使用px单位,不再支持rpx
-    x?: number; // x坐标(距离屏幕左边的距离,px单位)
-    y?: number; // y坐标(距离屏幕顶部的距离,px单位)
+    width?: string; // 宽度 rpx/px 字符串
+    height?: string; // 高度 rpx/px 字符串
+    right?: string; // 距右侧 rpx/px 字符串
+    bottom?: string; // 距底部 rpx/px 字符串
     inertia?: boolean;
-    snapThreshold?: number;
     bgColor?: string;
     borderRadius?: string;
 }
 
 const props = withDefaults(defineProps<Props>(), {
-    imageSrc: '/static/logo.png',
-    imageWidth: 50, // 默认宽度 50px
-    imageHeight: 50, // 默认高度 50px
-    x: 10, // 默认距离左边 10px
-    y: 200, // 默认距离顶部 200px
+    width: '120rpx', // 默认宽度
+    height: '120rpx', // 默认高度
+    right: '10rpx', // 默认右侧间距
+    bottom: '280rpx', // 默认底部间距
     inertia: false,
-    snapThreshold: 30,
     bgColor: 'transparent',
     borderRadius: '0',
 });
 
 // 响应式状态
-const x = ref<number>(props.x); // movable-view的x坐标(距离左边)
-const y = ref<number>(props.y); // movable-view的y坐标(距离顶部)
+const x = ref<number>(0); // movable-view的x坐标(距离左边)
+const y = ref<number>(0); // movable-view的y坐标(距离顶部)
+const inited = ref<boolean>(false); // 控制渲染时机,避免闪动
 
 // 屏幕尺寸
 const screenWidth = ref<number>(0);
 const screenHeight = ref<number>(0);
 
+// 将字符串尺寸转换为像素(px),使用 uView 的换算
+function toPx(val?: string | number): number {
+    if (val === undefined || val === null) return 0;
+    if (typeof val === 'number') return val;
+    return (uni as any).$u.getPx(val as string);
+}
+
 // movable-area 样式
 const areaStyle = computed(() => {
     return {
@@ -73,30 +85,23 @@ onMounted(() => {
     const systemInfo = uni.getSystemInfoSync();
     screenWidth.value = systemInfo.windowWidth;
     screenHeight.value = systemInfo.windowHeight;
+    // 根据 right/bottom 计算初始位置(默认右下角)
+    const widthPx = toPx(props.width);
+    const heightPx = toPx(props.height);
+    const rightPx = toPx(props.right);
+    const bottomPx = toPx(props.bottom);
+    x.value = Math.max(0, screenWidth.value - rightPx - widthPx);
+    y.value = Math.max(0, screenHeight.value - bottomPx - heightPx);
+    inited.value = true;
 });
-// movable-view 位置变化事件
-const onChange = (e: any) => {
-    // if (e.detail) {
-    //     // 直接更新位置
-    //     x.value = e.detail.x;
-    //     y.value = e.detail.y;
-    //     console.log(x.value, y.value);
-    // 标记为正在拖动
-    // isDragging.value = true;
-    // }
-};
-
-// 触摸结束事件(拖动结束)
-const onTouchEnd = () => {
-    // 拖动结束后进行边界检查和吸附
-    // applyBoundaryAndSnap();
-    // isDragging.value = false;
-};
+// click 事件抛出
+const emit = defineEmits<{
+    (e: 'click', payload: { x: number; y: number }): void;
+}>();
 
 // 点击事件
 const handleClick = () => {
-    // 触发点击事件
-    console.log('悬浮按钮被点击', { x: x.value, y: y.value });
+    emit('click', { x: x.value, y: y.value });
 };
 </script>
 

+ 6 - 13
src/pages/plant/base/index.vue

@@ -48,7 +48,7 @@
                             </view>
                             <view v-if="speciesArray.length" class="c-#333 f-s-24 d-flex pl-40 pr-15 pb-15 pt-15">
                                 <view class="ov-hd tx-ov w-s-no">{{ speciesArray.join('、') }}</view>
-                                <view v-if="(speciesArray.length > 4)" class="flex1 w-s-no">等{{ speciesArray.length }}个品种 </view>
+                                <view v-if="speciesArray.length > 4" class="flex1 w-s-no">等{{ speciesArray.length }}个品种 </view>
                             </view>
                             <view v-if="!speciesArray.length" @click="$u.route({ url: '/plant/species/config/index' })" class="c-primary bg-#E3F6E7 f-s-22 mg-at radius-10 w-250 h-50 d-flex a-c j-c"> 暂未配置品种,去配置{{ '>' }}</view>
                             <view v-if="!speciesArray.length" class="pd-7"></view>
@@ -81,14 +81,7 @@
                     <view class="pd4-16-24-16-24">
                         <template>
                             <up-swipe-action>
-                                <up-swipe-action-item
-                                    v-for="(item, index) in list"
-                                    :key="index"
-                                    :name="item?.id"
-                                    :options="optionsActionTemp"
-                                    @click="clickTempSwipe"
-                                    class="mb-20 b-radius"
-                                >
+                                <up-swipe-action-item v-for="(item, index) in list" :key="index" :name="item?.id" :options="optionsActionTemp" @click="clickTempSwipe" class="mb-20 b-radius">
                                     <view class="b-radius bg-#fff pd-20 p-rtv" @click.stop="$u.route({ url: '/plant/base/base-detail/index', params: { id: item.id } })">
                                         <view class="c-333 f-s-34 pd-5 f-w-5">{{ item?.baseName }}</view>
                                         <view class="c-ccc f-s-24 pd-5 pt-0">{{ item?.baseCode }}</view>
@@ -142,7 +135,7 @@
         <view class="h-210" v-if="list?.length"></view>
         <view v-if="list?.length" :style="{ height: `${safeAreaBottom}px` }"></view>
     </z-paging>
-     <ut-tabar activeTab="base"></ut-tabar>
+    <ut-tabar activeTab="base"></ut-tabar>
     <ut-confirm-dialog v-model:show="showDeleteDialog" width="80vw" title="请选择要添加到基地类型" :confirmText="'确认选择'" :cancelText="'取消'" @confirm="handlechoseConfirm" @cancel="handleDeleteCancel">
         <view class="" v-for="item in pt_base_type" :key="item?.value">
             <view style="border: 1rpx solid" :style="{ backgroundColor: item?.value == basetype ? '#EBF6EE' : '#f7f7f7', borderColor: item?.value == basetype ? '#37A954' : 'transparent' }" class="pr-30 d-flex a-c mb-20 radius-100" @click="handlechose(item.value)">
@@ -159,8 +152,8 @@
             </view>
         </view>
     </ut-confirm-dialog>
-    <ut-suspension v-if="sus?.left" :imageWidth="60" :imageHeight="60" :x="sus?.left" :y="sus?.bottom" :inertia="false" :snap-threshold="40">
-        <image src="/static/images/plant/addBase.png" mode="widthFix" class="w-120 h-120" @click="showDeleteDialog = true"></image>
+    <ut-suspension @click="showDeleteDialog = true">
+        <image src="/static/images/plant/addBase.png" mode="widthFix" class="w-120 h-120"></image>
     </ut-suspension>
 </template>
 <script setup lang="ts">
@@ -333,7 +326,7 @@ const clickTempSwipe = async (event: object) => {
                 content: '确定删除该基地吗?',
                 confirmColor: '#FF4C4C',
             });
-            if (!res.confirm) return
+            if (!res.confirm) return;
             await uni.showLoading({
                 title: '删除中...',
                 mask: true,

+ 2 - 2
src/plant/base/base-detail/index.vue

@@ -94,11 +94,11 @@
                     </view>
                 </view>
                 <view class="h-1" id="plot12345"></view>
-                <!-- 地块/圈舍/培架信息 -->
+                <!-- 地块/圈舍/培架信息 -->
                 <view ref="plotRef" class="pd-24">
                     <view class="startline-title" v-if="form?.baseInfo?.baseType == '1'">地块信息</view>
                     <view class="startline-title" v-else-if="form?.baseInfo?.baseType == '2'">圈舍信息</view>
-                    <view class="startline-title" v-else-if="form?.baseInfo?.baseType == '3'">培架信息</view>
+                    <view class="startline-title" v-else-if="form?.baseInfo?.baseType == '3'">培架信息</view>
                 </view>
                 <view class="pd-24 bg-#fff mb-10">
                     <template v-if="Array.isArray(form?.landInfoList) && form.landInfoList.length">

+ 6 - 6
src/plant/base/base-edit/index.vue

@@ -214,7 +214,7 @@
                     </up-form-item>
                 </template>
             </view>
-            <!-- 地块/圈舍/培架信息 -->
+            <!-- 地块/圈舍/培架信息 -->
             <view ref="plotRef" class="h-1" id="plot12345"></view>
             <template v-if="form.baseInfo.baseType == '1'">
                 <view class="pd-24">
@@ -273,12 +273,12 @@
             </template>
             <template v-if="form.baseInfo.baseType == '3'">
                 <view class="pd-24">
-                    <view class="startline-title">培架信息</view>
+                    <view class="startline-title">培架信息</view>
                 </view>
                 <view class="pd-24 bg-#fff">
                     <up-swipe-action>
                         <template v-for="(item, index) in form.landInfoList" :key="index">
-                            <up-swipe-action-item class="mb-20" :options="options" @click="clickPlotSwipe($event, item, index, '编辑培架信息')">
+                            <up-swipe-action-item class="mb-20" :options="options" @click="clickPlotSwipe($event, item, index, '编辑培架信息')">
                                 <view class="plot-item pd-24">
                                     <view class="d-flex mb-16">
                                         <view class="f-s-32 f-w-5 c-#333 flex1 mr-10">{{ item?.landName || '-' }}</view>
@@ -306,9 +306,9 @@
                             </up-swipe-action-item>
                         </template>
                     </up-swipe-action>
-                    <up-button @click="addPlot({ landType: form.baseInfo.baseType, areaUnit: form.baseInfo.gapInfo.areaUnit, capacityUnit: '瓶' }, '添加培架')" type="primary" text="确定" plain>
+                    <up-button @click="addPlot({ landType: form.baseInfo.baseType, areaUnit: form.baseInfo.gapInfo.areaUnit, capacityUnit: '瓶' }, '添加培架')" type="primary" text="确定" plain>
                         <up-icon class="mr-10" name="plus" color="#37A954"></up-icon>
-                        <span>添加培架信息</span>
+                        <span>添加培架信息</span>
                     </up-button>
                 </view>
             </template>
@@ -594,7 +594,7 @@ const form = ref<any>({
         },
         coordinates: [],
     }, // 基地信息
-    landInfoList: [], // 地块/圈舍/培架信息
+    landInfoList: [], // 地块/圈舍/培架信息
     environmentInfo: {}, // 基地环境信息
 });
 const options = ref([

+ 2 - 2
src/plant/base/gap-base-info/index.vue

@@ -3,10 +3,10 @@
         <template #top>
             <ut-navbar title="GAP基地信息" :fixed="false" border></ut-navbar>
             <view class="d-flex a-c pd-25">
-                <view class="min-w-170 flex1">
+                <view class="min-w-200 flex1">
                     <ut-action-sheet v-model="form.res" :tabs="tabs" @change="onRefresh" title="选择基地类型">
                         <view class="d-flex search-select-item a-c">
-                            <view class="flex1 ov-hd f-s-28 c-333 text-center f-w-5 w-s-no">{{ '全部' }}</view>
+                            <view class="flex1 ov-hd f-s-28 c-333 text-center f-w-5 w-s-no">{{ selectDictLabel(tabs, form.res) || '全部' }}</view>
                             <up-icon size="24rpx" color="#333" name="arrow-down-fill" class="mr-5"></up-icon>
                         </view>
                     </ut-action-sheet>

+ 1 - 1
src/plant/port/port-create/index.vue

@@ -7,7 +7,7 @@
             <up-form class="p-rtv" labelPosition="top" :model="form" :rules="rules" labelWidth="auto" ref="upFormRef">
                 <view class="startline-title pl-24 ml-24 mb-16">基地信息</view>
                 <view class="bg-#fff pd-24">
-                    <view class="b-radius bg-#EBF6EE c-primary f-s-24 pd-24 pb-16"> 注意:关联时请确保已添加到地块;关联时必须选到地块/圈舍/培架。</view>
+                    <view class="b-radius bg-#EBF6EE c-primary f-s-24 pd-24 pb-16"> 注意:关联时请确保已添加到地块;关联时必须选到地块/圈舍/培架。</view>
                 </view>
                 <view class="bg-#fff pd-24 mb-20 pt-0">
                     <up-form-item :borderBottom="false" label="种养殖所在基地" required prop="deawerData">

+ 2 - 2
src/plant/port/port-create/models/baseinfo.vue

@@ -22,9 +22,9 @@
         <view class="pt-16 pb-8">
             <text v-if="+baseType == 1" class="c-#666 f-s-28">地块信息:</text>
             <text v-if="+baseType == 2" class="c-#666 f-s-28">圈舍信息:</text>
-            <text v-if="+baseType == 3" class="c-#666 f-s-28">培架信息:</text>
+            <text v-if="+baseType == 3" class="c-#666 f-s-28">培架信息:</text>
             <text v-if="!modeValue?.aloneChecked" class="c-#333 f-s-28 f-w-5">{{ modeValue?.data?.length }}个</text>
-            <text v-if="modeValue?.aloneChecked" class="c-#333 f-s-28 f-w-5">该基地所有{{ baseType == '1' ? '地块' : baseType == '2' ? '圈舍' : '培架' }}</text>
+            <text v-if="modeValue?.aloneChecked" class="c-#333 f-s-28 f-w-5">该基地所有{{ baseType == '1' ? '地块' : baseType == '2' ? '圈舍' : '培架' }}</text>
         </view>
         <view v-if="!modeValue?.aloneChecked" class="pt-8 pb-8" v-for="(item, index) in modeValue?.data" :key="index">
             <text class="c-#666 f-s-28">{{ item?.landName }}:</text>

+ 187 - 2
src/plant/storage/storage-room/list/index.vue

@@ -1,3 +1,188 @@
 <template>
-    <view>库房列表</view>
-</template>
+    <z-paging ref="paging" v-model="list" bgColor="#f7f7f7" @query="query" safe-area-inset-bottom>
+        <template #top>
+            <ut-navbar title="库房管理" :fixed="false" border></ut-navbar>
+            <view class="d-flex a-c pd-25">
+                <view class="min-w-220 flex1">
+                    <ut-action-sheet v-model="form.res" :tabs="tabs" @change="onRefresh" title="选择库房类型">
+                        <view class="d-flex search-select-item a-c">
+                            <view class="flex1 ov-hd f-s-28 c-333 text-center f-w-5 w-s-no">{{ '全部类型' }}</view>
+                            <up-icon size="24rpx" color="#333" name="arrow-down-fill" class="mr-5"></up-icon>
+                        </view>
+                    </ut-action-sheet>
+                </view>
+                <view class="h-86 pl-20 w-100%">
+                    <ut-search ref="searchRef" v-model="form.keyword" @search="onRefresh" margin="0" :border="false" :placeholder="placeholder" bgColor="#fff" height="86rpx" borderRadius="10rpx"></ut-search>
+                </view>
+            </view>
+        </template>
+        <template>
+            <template>
+                <view class="pl-25 pr-25">
+                    <up-swipe-action>
+                        <up-swipe-action-item v-for="item in list" :name="item?.id" :key="item?.id" :disabled="item?.res !== '2'" :options="optionsAction" @click="clickSwipe" class="mb-20 b-radius">
+                            <view @click.stop="clickItem(item)" class="b-radius bg-#fff pd-20 p-rtv">
+                                <view class="d-flex">
+                                    <view class="flex1 ov-hd mr-20 f-s-34 c-#333 f-w-500">{{ item?.warehouseName }}</view>
+                                    <view class="f-s-24 c-#666">{{ item?.createTime }}创建</view>
+                                </view>
+                                <view class="f-s-24 c-#666 mb-10">{{ item?.sn }}</view>
+                                <view class="f-s-28 pd2-5-0">
+                                    <span class="c-#666">库房类型:</span>
+                                    <span class="c-#333 f-w-600">农资库</span>
+                                </view>
+                                <view class="f-s-28 pd2-5-0">
+                                    <span class="c-#666">负责人:</span>
+                                    <span class="c-#333 f-w-600">{{ item?.contactName }}</span>
+                                </view> 
+                                <view class="f-s-28 pd2-5-0">
+                                    <span class="c-#666">所在位置:</span>
+                                    <span class="c-#333 f-w-600">{{ item?.address }}</span>
+                                </view>
+                                <view class="f-s-28 pd2-5-0">
+                                    <span class="c-#666">货位数量:</span>
+                                    <span class="c-#333 f-w-600">{{ item?.shelvesCount }}</span>
+                                </view>
+                            </view>
+                        </up-swipe-action-item>
+                    </up-swipe-action>
+                </view>
+            </template>
+            <!-- 空数据处理 -->
+
+            <template #empty v-if="address">
+                <ut-empty class="mg-at" size="28rpx" color="#999" padding="10rpx" image="/static/images/plant/noEmptyBase.png">暂无获评GAP基地信息~</ut-empty>
+                <view class="d-flex j-c f-s-28 c-#ccc">如需认定GAP基地,可前往数字云药官网进行申报</view>
+                <view class="d-flex j-c f-s-28 c-primary pd-15">https://www.shuziyunyao.com/</view>
+                <up-button type="primary" class="b-radius" style="width: 340rpx" @click="copyText('https://www.shuziyunyao.com/')">复制网址</up-button>
+            </template>
+        </template>
+        <template #empty v-if="!address">
+            <ut-empty class="mg-at" size="28rpx" color="#999" padding="10rpx" image="/static/images/plant/noEmptyBase.png">非云南省内企业,无法获取已获评的GAP基地信息~</ut-empty>
+            <view class="d-flex j-c f-s-28 c-#ccc">可点击底部按钮上传佐证材料添加获评信息</view>
+        </template>
+        <template #bottom v-if="!address">
+            <view class="base-bottom-wrap pd-20 pb-0">
+                <up-button type="primary" @click="$u.route({ url: '/plant/base/gap-base-info-edit/index' })">
+                    <img class="w-38 h-36 mr-10" src="/static/images/plant/chooseGAP.png" alt="" mode="widthFix" />
+                    <text>添加GAP基地信息</text>
+                </up-button>
+            </view>
+        </template>
+    </z-paging>
+    <ut-suspension :imageWidth="60" :imageHeight="60" :x="sus?.left" :y="sus?.bottom" :inertia="false" :snap-threshold="40">
+        <image src="/static/images/plant/addBase.png" mode="widthFix" class="w-120 h-120"></image>
+    </ut-suspension>
+</template>
+<script setup lang="ts">
+import { copyText } from '@/utils/public';
+import { useClientRequest } from '@/utils/request';
+import { useInfoStore } from '@/store';
+const paging = ref();
+const list = ref<any[]>([]);
+const placeholder = ref('搜库房名称、编号、负责人');
+const tabs = ref([
+    { label: '全部', value: '' },
+    { label: '有效', value: '1' },
+    { label: '审核不通过', value: '2' },
+    { label: '待审核', value: '0' },
+]);
+// 判断是否是云南的企业
+const isYunnanCompany = (): boolean => {
+    const adcdCode = useInfoStore().companyInfo?.adcdCode;
+    return adcdCode?.startsWith('53') || false;
+};
+const address = computed(() => isYunnanCompany());
+const form = ref({
+    keyword: '',
+    res: '',
+});
+
+const query = async (pageNum: number, pageSize: number) => {
+    const params = {
+        pageNum,
+        pageSize,
+        ...form.value,
+    };
+    const res = await useClientRequest.get<any>('/plt-api/app/warehouse/pageList', params);
+    if (!res || res.code !== 200) return;
+    const { rows } = res;
+    paging.value.complete(rows);
+};
+const onRefresh = () => {
+    paging.value.reload();
+};
+const optionsAction = reactive([
+    {
+        text: '删除',
+        style: {
+            backgroundColor: '#f56c6c',
+        },
+    },
+]);
+const clickSwipe = async (event: object) => {
+    const { name, index } = event as any;
+    if (index === 0) {
+        // 删除
+        const res = await uni.showModal({
+            title: '提示',
+            content: '确定删除该GAP基地信息吗?',
+        });
+        console.log(res);
+
+        if (res.confirm) {
+            const delRes = await useClientRequest.get(`/plt-api/app/gapCertificationInfo/delById/${name}`);
+            if (delRes && delRes.code === 200) {
+                uni.showToast({ title: '删除成功', icon: 'none' });
+                onRefresh();
+            }
+        }
+    }
+};
+const clickItem = (item: any) => {
+    uni.$u.route({
+        type: 'navigateTo',
+        url: '/plant/base/gap-base-info-detail/index',
+        params: {
+            id: item?.id,
+        },
+    });
+};
+onMounted(() => {
+    uni.$on('gapBaseInfoUpdated', () => {
+        onRefresh();
+    });
+});
+</script>
+<style lang="scss" scoped>
+// @import '@/assets/styles/theme.scss';
+.search-select-item {
+    height: 86rpx;
+    background-color: #fff;
+    border-radius: 10rpx;
+    box-sizing: border-box;
+    padding-left: 16rpx;
+    padding-right: 16rpx;
+    padding-top: 14rpx;
+    padding-bottom: 14rpx;
+}
+.u-page {
+    padding: 0;
+}
+
+.u-demo-block__title {
+    padding: 10px 0 2px 15px;
+}
+
+.swipe-action {
+    &__content {
+        padding: 25rpx 0;
+
+        &__text {
+            font-size: 15px;
+            color: $u-main-color;
+            padding-left: 30rpx;
+        }
+    }
+}
+</style>