Преглед изворни кода

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

lisy пре 1 недеља
родитељ
комит
585c92d505
27 измењених фајлова са 740 додато и 117 уклоњено
  1. 21 10
      src/components/ut-datetime-picker/ut-datetime-picker.vue
  2. 3 3
      src/components/ut-search/ut-search.vue
  3. 1 1
      src/main.ts
  4. 40 22
      src/pages/plant/base/index.vue
  5. 1 1
      src/plant/base/base-edit/models/form-plot.vue
  6. 1 1
      src/plant/base/gap-base-info-edit/index.vue
  7. 141 2
      src/plant/storage/storage-room/detail/index.vue
  8. 76 0
      src/plant/storage/storage-room/edit/index.ts
  9. 272 2
      src/plant/storage/storage-room/edit/index.vue
  10. 101 0
      src/plant/storage/storage-room/edit/models/form-shelve.vue
  11. 83 75
      src/plant/storage/storage-room/list/index.vue
  12. BIN
      src/static/images/common/btn_add_logo.png
  13. BIN
      src/static/images/plant/port/个体标识-从种源库快速添加.png
  14. BIN
      src/static/images/plant/port/个体标识-手动输入添加.png
  15. BIN
      src/static/images/plant/port/个体标识-扫一扫添加.png
  16. BIN
      src/static/images/plant/port/任务-养殖.png
  17. BIN
      src/static/images/plant/port/任务-种植.png
  18. BIN
      src/static/images/plant/port/任务-种畜繁育.png
  19. BIN
      src/static/images/plant/port/任务-种苗繁育.png
  20. BIN
      src/static/images/plant/port/任务类型-养殖.png
  21. BIN
      src/static/images/plant/port/任务类型-种植.png
  22. BIN
      src/static/images/plant/port/任务类型-种畜繁育.png
  23. BIN
      src/static/images/plant/port/任务类型-种苗繁育.png
  24. BIN
      src/static/images/plant/port/已结束.png
  25. BIN
      src/static/images/plant/port/种源信息.png
  26. BIN
      src/static/images/plant/port/采收管理.png
  27. BIN
      src/static/images/plant/port/饲养管理.png

+ 21 - 10
src/components/ut-datetime-picker/ut-datetime-picker.vue

@@ -13,8 +13,8 @@
         </view>
         <up-datetime-picker
             v-model:show="showTime"
-            :minDate="minDate"
-            :maxDate="maxDate"
+            :minDate="minDateTs"
+            :maxDate="maxDateTs"
             ref="datetimePickerRef"
             :title="title"
             v-model="form.startTime"
@@ -36,8 +36,8 @@ interface Props {
     mode: DateTimeMode;
     border: boolean;
     hasInput: boolean;
-    minDate: number;
-    maxDate: number;
+    minDate: number | string | Date;
+    maxDate: number | string | Date;
     dateFields?: 'year' | 'month' | 'day';
     dateFormat?: string;
 }
@@ -48,8 +48,8 @@ const props = withDefaults(defineProps<Props>(), {
     mode: 'datetime' as DateTimeMode,
     border: false,
     hasInput: false,
-    minDate: new Date(2020, 0, 1).getTime(),
-    maxDate: new Date(2030, 0, 1).getTime(),
+    minDate: new Date(2000, 0, 1).getTime(),
+    maxDate: new Date(2050, 0, 1).getTime(),
     dateFields: 'day',
     // 留空以便按粒度自动输出:year→{y},month→{y}-{m},day→{y}-{m}-{d}
     dateFormat: ''
@@ -62,8 +62,17 @@ const emit = defineEmits<{
 
 const showTime = ref(false);
 const datetimePickerRef = ref(null);
+
+// 统一转换为时间戳
+const toTs = (v: number | string | Date | undefined | null): number => {
+    if (v === undefined || v === null) return Date.now();
+    if (typeof v === 'number') return v;
+    if (typeof v === 'string') return new Date(v).getTime();
+    return (v as Date).getTime();
+};
+
 const form = ref({
-    startTime: props.modelValue ? new Date(props.modelValue) : Date.now()
+    startTime: toTs(props.modelValue as any)
 });
 
 // 当使用原生 picker 的 date 模式时,格式化绑定和边界值(随粒度变化)
@@ -75,11 +84,13 @@ const fieldsToFormat = (f: 'year' | 'month' | 'day') => {
 const dateValue = computed(() => {
     const v = form.value.startTime as unknown as number | string | Date;
     if (!v) return '';
-    const ts = typeof v === 'number' ? v : new Date(v as any).getTime();
+    const ts = toTs(v);
     return parseTime(ts, fieldsToFormat(props.dateFields!));
 });
-const minDateStr = computed(() => parseTime(props.minDate, fieldsToFormat(props.dateFields!)));
-const maxDateStr = computed(() => parseTime(props.maxDate, fieldsToFormat(props.dateFields!)));
+const minDateTs = computed(() => toTs(props.minDate));
+const maxDateTs = computed(() => toTs(props.maxDate));
+const minDateStr = computed(() => parseTime(minDateTs.value, fieldsToFormat(props.dateFields!)));
+const maxDateStr = computed(() => parseTime(maxDateTs.value, fieldsToFormat(props.dateFields!)));
 
 const confirm = (value?: any) => {
     const timestamp = value?.value ?? Date.now();

+ 3 - 3
src/components/ut-search/ut-search.vue

@@ -3,7 +3,7 @@
     <view class="search-input d-flex a-c" :class="{ 'up-border': border }" :style="{ margin, background: bgColor, height, borderRadius }">
         <u-input v-model="value" ref="searchInputRef" cursor cursorColor="#333" clearable type="text" :focus="focused" border="none" @change="inputSearch" @clear="clear" @confirm="search" confirmType="search" :fontSize="fontSize" :maxlength="maxlength" :placeholder="placeholder">
             <template #suffix>
-                <view @click.stop="search" class="d-flex j-c a-c" style="padding: 0 26rpx">
+                <view @click.stop="search" class="d-flex j-c a-c" style="padding: 0 16rpx">
                     <image class="search_icon" src="/static/images/plant/search_icon.png" mode="widthFix" />
                 </view>
             </template>
@@ -32,7 +32,7 @@ const props = defineProps({
     },
     fontSize: {
         type: String,
-        default: '30rpx',
+        default: '26rpx',
     },
     height: {
         type: String,
@@ -92,7 +92,7 @@ defineExpose({
 .search-input {
     height: 86rpx;
     background-color: #fff;
-    padding-left: 30rpx;
+    padding-left: 20rpx;
 }
 
 .search_icon {

+ 1 - 1
src/main.ts

@@ -18,7 +18,7 @@ const uviewProps: any = {
     props: {
         form: {
             labelStyle: {
-                color: '#666',
+                color: '#333',
                 fontSize: '30rpx',
             },
         },

+ 40 - 22
src/pages/plant/base/index.vue

@@ -56,29 +56,33 @@
                     </view>
                 </view>
                 <view class="b-radius pt-0 bg-#f7f7f7" style="border: 1rpx solid #fff; border-bottom-color: transparent; margin-top: -40rpx">
-                    <up-sticky :offset-top="stickyTop">
-                        <view class="d-flex a-c pd4-16-24-16-24 p-rtv">
-                            <view class="c-333 f-s-32 f-w-5 z-index-1">基地与地块管理</view>
-                            <image class="w-230" src="/static/images/plant/BasePlotManagement.png" mode="widthFix" style="position: absolute; top: 44rpx; left: 24rpx; z-index: 0" />
-                            <view class="flex1"></view>
-                            <view @click="$u.route({ url: '/plant/base/gap-base-info/index' })" class="c-primary f-s-22 z-index-1">GAP基地获评信息管理{{ '>' }}</view>
-                            <image src="/static/images/plant/basePlotBG.png" class="w-100%" mode="widthFix" style="position: absolute; top: 0; left: 0; z-index: -1"></image>
-                        </view>
-                        <view class="d-flex a-c pd4-20-24-20-24 bg-#f7f7f7">
-                            <view class="min-w-170 flex1">
-                                <ut-action-sheet v-model="form.queryType" :tabs="[{ label: '全部', value: '' }]" @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>
+                    <up-sticky :offset-top="stickyTop" zIndex="10">
+                        <view class="pd-24 p-rtv bg-#f7f7f7">
+                            <image src="/static/images/plant/basePlotBG.png" class="w-100%" mode="widthFix" style="position: absolute; top: 0; left: 0;"></image>
+                            <view class="d-flex a-c mb-26 p-rtv">
+                                <view class="p-rtv d-flex flex-cln">
+                                    <view class="c-333 f-s-32 f-w-5 z-index-1">基地与地块管理</view>
+                                    <image class="w-230 h-11" style="margin-top: -10rpx" src="/static/images/plant/BasePlotManagement.png" mode="widthFix" />
+                                </view>
+                                <view class="flex1"></view>
+                                <view @click="$u.route({ url: '/plant/base/gap-base-info/index' })" class="c-primary f-s-22 z-index-1">GAP基地获评信息管理{{ '>' }}</view>
                             </view>
-                            <view class="h-86 pl-24 w-100%">
-                                <ut-search ref="searchRef" v-model="form.keyword" @search="changeSeach" margin="0" :border="false" placeholder="搜基地名称、编号、地址、负责人" bgColor="#fff" height="86rpx" borderRadius="10rpx"></ut-search>
+                            <view class="d-flex a-c p-rtv">
+                                <view class="w-220">
+                                    <ut-action-sheet v-model="form.queryType" :tabs="[{ label: '全部', value: '' }]" @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="pl-16 flex1 ov-hd">
+                                    <ut-search ref="searchRef" v-model="form.keyword" @search="changeSeach" margin="0" :border="false" placeholder="搜基地名称、编号、地址、负责人" bgColor="#fff" height="86rpx" borderRadius="10rpx"></ut-search>
+                                </view>
                             </view>
                         </view>
                     </up-sticky>
-                    <view class="pd4-16-24-16-24">
+                    <view class="pd4-10-24-24-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">
@@ -277,6 +281,9 @@ const query = async (pageNum: number, pageSize: number) => {
         pageSize,
         ...form.value,
     };
+    if (pageNum == 1) {
+        getBaseCount();
+    }
     const res = await useClientRequest.get('/plt-api/app/base/pageList', params);
     console.log(res, 'res');
     if (res) {
@@ -322,9 +329,9 @@ const clickTempSwipe = async (event: object) => {
     if (index === 0) {
         try {
             const res = await uni.showModal({
-                title: '提示',
+                title: '删除提示',
                 content: '确定删除该基地吗?',
-                confirmColor: '#FF4C4C',
+                confirmColor: '#f56c6c',
             });
             if (!res.confirm) return;
             await uni.showLoading({
@@ -343,7 +350,18 @@ const clickTempSwipe = async (event: object) => {
         }
     }
 };
-
+// 获取统计信息
+const getBaseCount = async () => {
+    try {
+        const res = await useClientRequest.get('/plt-api/app/base/getBaseCount');
+        if (res?.code === 200) {
+            console.log(res);
+            
+        }
+    } catch (error) {
+        console.error('获取地块统计信息失败:', error);
+    }
+};
 onMounted(() => {
     const querys = uni.createSelectorQuery().in(instance?.proxy);
     querys

+ 1 - 1
src/plant/base/base-edit/models/form-plot.vue

@@ -60,7 +60,7 @@
         <template #footer>
             <view class="pd-24 d-flex a-c">
                 <up-button class="mr-16" @click="onClose">取消</up-button>
-                <up-button type="primary" @click="onConfirm">添加</up-button>
+                <up-button type="primary" @click="onConfirm">确定</up-button>
             </view>
         </template>
     </ut-confirm-dialog>

+ 1 - 1
src/plant/base/gap-base-info-edit/index.vue

@@ -86,7 +86,7 @@
                     </up-form-item>
                     <!-- 校验定位:建设时间 -->
                     <view class="h-1" id="ratedDatepppp"></view>
-                    <ut-datetime-picker v-model="form.ratedDate" mode="date" dateFields="day">
+                    <ut-datetime-picker v-model="form.ratedDate" mode="date" :minDate="new Date(1990, 0, 1)" dateFields="day">
                         <up-form-item borderBottom label="获评GAP基地时间" required prop="ratedDate">
                             <up-input v-model="form.ratedDate" placeholder="请选择获评GAP基地时间" border="none"
                                 clearable></up-input>

+ 141 - 2
src/plant/storage/storage-room/detail/index.vue

@@ -1,3 +1,142 @@
 <template>
-    <view>库房详情</view>
-</template>
+    <z-paging ref="paging" bgColor="#F7F7F7" safe-area-inset-bottom paging-class="paging-btm-shadow" scroll-with-animation>
+        <template #top>
+            <ut-navbar title="库房详情" :fixed="false" border></ut-navbar>
+        </template>
+
+        <template>
+            <view class="pd-24">
+                <view class="startline-title">库房信息</view>
+            </view>
+            <view class="pd-24 bg-#fff mb-10">
+                <view class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">库房类型:</span>
+                    <span class="c-#333 f-w-600">{{ selectDictLabel(pt_warehouse_type, form?.type) || '-' }}</span>
+                </view>
+                <view class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">库房名称:</span>
+                    <span class="c-#333 f-w-600">{{ form?.warehouseName || '-' }}</span>
+                </view>
+                <view v-if="form?.sn" class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">库房编号:</span>
+                    <span class="c-#333 f-w-600">{{ form?.sn }}</span>
+                </view>
+                <view class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">负责人:</span>
+                    <span class="c-#333 f-w-600">{{ form?.contactName || '-' }}</span>
+                </view>
+                <view class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">联系电话:</span>
+                    <span class="c-#333 f-w-600">{{ form?.tel || '-' }}</span>
+                </view>
+                <view class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">所在位置:</span>
+                    <span class="c-#333 f-w-600">{{ form?.address || '-' }}</span>
+                </view>
+                <view class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">面积:</span>
+                    <span class="c-#333 f-w-600">{{ form?.area || '-' }} 平方米</span>
+                </view>
+                <view v-if="form?.storageCondition" class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">库房条件:</span>
+                    <span class="c-#333 f-w-600">{{ form?.storageCondition }}</span>
+                </view>
+                <view v-if="form?.remark" class="f-s-30 pd2-16-0 info-border-bottom">
+                    <span class="c-#666">备注:</span>
+                    <span class="c-#333 f-w-600">{{ form?.remark }}</span>
+                </view>
+            </view>
+
+            <view class="pd-24">
+                <view class="startline-title">货位信息</view>
+            </view>
+            <view class="pd-24 bg-#fff mb-10">
+                <view class="f-s-28 c-#666 mb-10"
+                    >货位数量:<span class="c-#333 f-w-600">{{ form?.shelves?.length || 0 }}</span></view
+                >
+                <template v-if="form?.shelves?.length">
+                    <view>
+                        <view v-for="(item, index) in form.shelves" :key="index" class="plot-item pd-24 mb-20">
+                            <view class="d-flex mb-16">
+                                <view class="f-s-32 f-w-5 c-#333 flex1 mr-10">{{ item?.shelvesName || '-' }}</view>
+                                <view v-if="item?.sn" class="f-s-24 c-#333">编号:{{ item?.sn }}</view>
+                            </view>
+                            <view class="f-s-28 c-#666" v-if="item?.remark">
+                                <span class="">备注:</span>
+                                <span class="c-#333 f-w-5">{{ item?.remark }}</span>
+                            </view>
+                        </view>
+                    </view>
+                </template>
+                <template v-else>
+                    <ut-empty class="mg-at" size="28rpx" color="#999" padding="10rpx">暂无货位信息</ut-empty>
+                </template>
+            </view>
+        </template>
+
+        <template #empty>
+            <ut-empty class="mg-at" size="28rpx" color="#999" padding="10rpx">暂无库房详情</ut-empty>
+        </template>
+        <template #bottom>
+            <view class="pd-24 d-flex a-c">
+                <up-button type="primary" @click="clickEdit">修改</up-button>
+            </view>
+        </template>
+    </z-paging>
+</template>
+
+<script setup lang="ts">
+import { useClientRequest } from '@/utils/request';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { pt_warehouse_type } = toRefs<any>(proxy?.useDict('pt_warehouse_type'));
+
+const paging = ref<any>(null);
+const form = ref<any>({});
+const did = ref('');
+
+// 获取详情(仓库信息)
+const getDetailById = async (id: string) => {
+    if (!id) return;
+    const res = await useClientRequest.get(`/plt-api/app/warehouse/getInfoById/${id}`);
+    if (res && res.code === 200) {
+        form.value = res.data || {};
+    }
+};
+
+const onRefresh = () => {
+    getDetailById(did.value);
+    paging.value?.complete();
+};
+const clickEdit = () => {
+    uni.$on('storage-room-detail-refresh', () => {
+        getDetailById(did.value);
+        uni.$off('storage-room-detail-refresh');
+    });
+    uni.$u.route({
+        type: 'navigateTo',
+        url: '/plant/storage/storage-room/edit/index',
+        params: {
+            id: did.value,
+        },
+    });
+};
+// 页面入参解析并加载
+onLoad((options: any) => {
+    did.value = options?.id || '';
+    getDetailById(did.value);
+});
+</script>
+
+<style lang="scss" scoped>
+.z-paging-wrap {
+    position: absolute;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    left: 0;
+}
+.plot-item {
+    border: 1rpx solid rgba($u-primary, 0.4);
+    border-radius: 10rpx;
+}
+</style>

+ 76 - 0
src/plant/storage/storage-room/edit/index.ts

@@ -0,0 +1,76 @@
+
+/**
+ * ShelvesAddDTO,货架添加对象 pt_shelves
+ */
+export interface WarehouseShelfAddDTO {
+    /**
+     * 备注
+     */
+    remark?: string;
+    /**
+     * 货架名称
+     */
+    shelvesName?: string;
+    /**
+     * 货架编号
+     */
+    sn?: string;
+    /**
+     * 仓库ID
+     */
+    warehouseId: number;
+    [property: string]: any;
+}
+/**
+ * WarehouseAddDTO,仓库添加对象 pt_warehouse
+ */
+export interface WarehouseAddDTO {
+    /**
+     * 仓库地址
+     */
+    address?: string;
+    /**
+     * 面积
+     */
+    area?: number;
+    /**
+     * 负责人ID
+     */
+    contact?: string;
+    /**
+     * 负责人
+     */
+    contactName?: string;
+    /**
+     * 仓库附加信息(json 字符串)
+     */
+    extraInfo?: string;
+    /**
+     * 备注
+     */
+    remark?: string;
+    /**
+     * 仓库编号
+     */
+    sn?: string;
+    /**
+     * 仓库条件
+     */
+    storageCondition?: string;
+    /**
+     * 联系电话
+     */
+    tel?: string;
+    /**
+     * 仓库类型
+     */
+    type?: string;
+    /**
+     * 仓库名称
+     */
+    warehouseName?: string;
+    /**  * 货架列表
+     */
+    shelves?: WarehouseShelfAddDTO[];
+    [property: string]: any;
+}

+ 272 - 2
src/plant/storage/storage-room/edit/index.vue

@@ -1,3 +1,273 @@
 <template>
-    <view>新增编辑库房</view>
-</template>
+    <z-paging ref="paging" bgColor="#F7F7F7" safe-area-inset-bottom paging-class="paging-btm-shadow" scroll-with-animation>
+        <template #top>
+            <ut-navbar :title="did ? '编辑库房' : '新增库房'" :fixed="false" border></ut-navbar>
+        </template>
+        <up-form class="p-rtv" labelPosition="top" :model="form" :rules="rules" labelWidth="auto" ref="upFormRef">
+            <view class="pd-24">
+                <view class="startline-title">库房信息</view>
+            </view>
+            <view class="pd-24 bg-#fff mb-10">
+                <view class="h-1" id="typepppp"></view>
+                <ut-action-sheet v-model="form.type" :tabs="pt_warehouse_type" title="选择库房类型">
+                    <up-form-item borderBottom label="库房类型" required prop="type">
+                        <view v-if="form.type" class="f-s-30 c-333 f-w-5 flex1">{{ selectDictLabel(pt_warehouse_type, form.type) }}</view>
+                        <view v-else class="f-s-30 c-ccc f-w-4 flex1">请选择库房类型</view>
+                        <template #right>
+                            <up-icon size="22rpx" color="#2A6D52" name="arrow-down-fill"></up-icon>
+                        </template>
+                    </up-form-item>
+                </ut-action-sheet>
+                <view class="h-1" id="warehouseNamepppp"></view>
+                <up-form-item borderBottom label="库房名称" required prop="warehouseName">
+                    <up-input v-model="form.warehouseName" placeholder="请输入库房名称" border="none" clearable></up-input>
+                </up-form-item>
+                <up-form-item borderBottom label="库房编号" prop="sn">
+                    <up-input v-model="form.sn" placeholder="请输入库房编号" border="none" clearable></up-input>
+                </up-form-item>
+                <up-form-item borderBottom label="库房面积" prop="area">
+                    <up-input v-model="form.area" type="number" placeholder="请输入库房面积" border="none" clearable></up-input>
+                    <template #right>
+                        <span>平方米</span>
+                    </template>
+                </up-form-item>
+                <up-form-item borderBottom label="所在位置" prop="address">
+                    <up-input v-model="form.address" placeholder="请输入所在位置" border="none" clearable></up-input>
+                </up-form-item>
+                <view class="h-1" id="contactpppp"></view>
+                <up-form-item @click="selectStorageMember" borderBottom label="负责人" required prop="contact">
+                    <view v-if="form.contact" class="f-s-30 c-333 f-w-5 flex1">{{ form.contactName }}</view>
+                    <view v-else class="f-s-30 c-ccc f-w-4 flex1">请选择负责人</view>
+                    <template #right>
+                        <up-icon size="22rpx" color="#2A6D52" name="arrow-down-fill"></up-icon>
+                    </template>
+                </up-form-item>
+                <!-- 填写基地联系电话 -->
+                <view class="h-1" id="telpppp"></view>
+                <up-form-item borderBottom label="联系电话" required prop="tel">
+                    <up-input v-model="form.tel" placeholder="请输入联系电话" border="none" clearable></up-input>
+                </up-form-item>
+                <up-form-item label="库房条件" prop="storageCondition">
+                    <up-textarea v-model="form.storageCondition" placeholder="请输入库房条件" maxlength="300" autoHeight clearable></up-textarea>
+                </up-form-item>
+                <up-form-item label="备注" prop="remark">
+                    <up-textarea v-model="form.remark" placeholder="请输入备注" maxlength="300" autoHeight clearable></up-textarea>
+                </up-form-item>
+            </view>
+            <view class="pd-24">
+                <view class="startline-title">货位信息</view>
+            </view>
+            <view class="pd-24 bg-#fff mb-10">
+                <up-form-item borderBottom :label="`货位数量(${form?.shelves?.length || 0})`" prop="shelves"> </up-form-item>
+                <up-swipe-action>
+                    <template v-for="(item, index) in form.shelves" :key="index">
+                        <up-swipe-action-item class="mb-20" :options="shelveOptions" @click="clickShelveSwipe($event, item, index)">
+                            <view class="plot-item pd-24">
+                                <view class="f-s-32 f-w-5 c-#333">{{ item?.shelvesName || '-' }}</view>
+                                <view v-if="item?.sn" class="f-s-24 c-#666">{{ item?.sn }}</view>
+                                <view class="pd-5"></view>
+                                <view class="f-s-28 c-#333" v-if="item?.remark">
+                                    <span class="">备注:</span>
+                                    <span class="c-#333 f-w-5">{{ item?.remark }}</span>
+                                </view>
+                            </view>
+                        </up-swipe-action-item>
+                    </template>
+                </up-swipe-action>
+                <up-button @click="addShelve()" type="primary" text="确定" plain>
+                    <up-icon class="mr-10" name="plus" color="#37A954"></up-icon>
+                    <span>添加货位信息</span>
+                </up-button>
+            </view>
+        </up-form>
+        <template #bottom>
+            <view class="base-bottom-wrap pd-20 pb-0">
+                <up-button type="primary" @click="submit">
+                    <text>{{ did ? '保存' : '提交' }}</text>
+                </up-button>
+            </view>
+        </template>
+    </z-paging>
+
+    <!-- 悬浮新增按钮(编辑页通常不需要,但与列表风格一致时可保留) -->
+    <!-- <ut-suspension @click="submit">
+        <image src="/static/images/plant/addBase.png" mode="widthFix" class="w-120 h-120"></image>
+    </ut-suspension> -->
+
+    <!-- 货位表单弹窗:新增/编辑 -->
+    <FormShelve v-if="showShelve" v-model:show="showShelve" v-model="rowShelve" :title="shelveTitle" @submit="submitFormShelveAdd" />
+    <FormShelve v-if="showShelveEdit" v-model:show="showShelveEdit" v-model="rowShelveEdit" :title="shelveTitle" @submit="submitFormShelveEdit" />
+</template>
+<script setup lang="ts">
+import { WarehouseAddDTO, WarehouseShelfAddDTO } from './index';
+import { useClientRequest } from '@/utils/request';
+import FormShelve from './models/form-shelve.vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { pt_warehouse_type } = toRefs<any>(proxy?.useDict('pt_warehouse_type'));
+
+const upFormRef = ref();
+const paging = ref();
+const did = ref<string>('');
+
+const form = ref<WarehouseAddDTO>({
+    warehouseName: '',
+    sn: '',
+    type: '',
+    contactName: '',
+    tel: '',
+    address: '',
+    area: undefined,
+    storageCondition: '',
+    extraInfo: '',
+    remark: '',
+    shelves: [],
+});
+
+const rules = {
+    warehouseName: [{ required: true, message: '请输入库房名称', trigger: ['blur', 'change'] }],
+    type: [{ required: true, message: '请选择库房类型', trigger: ['change'] }],
+    tel: [
+        {
+            validator: (rule: any, value: string) => {
+                if (!value) return true;
+                return /^\d[\d-]{5,20}$/.test(value);
+            },
+            message: '联系电话格式不正确',
+            trigger: ['blur', 'change'],
+        },
+    ],
+    area: [
+        {
+            validator: (rule: any, value: any) => {
+                if (value === undefined || value === null || value === '') return true;
+                return Number(value) >= 0;
+            },
+            message: '面积需为非负数',
+            trigger: ['blur', 'change'],
+        },
+    ],
+};
+
+const loadDetail = async (id: string) => {
+    if (!id) return;
+    const res = await useClientRequest.get(`/plt-api/app/warehouse/getInfoById/${id}`);
+    if (res && res.code === 200) {
+        const data = res.data || {};
+        // 仅映射已知字段,避免污染
+        form.value = {
+            warehouseName: data.warehouseName || '',
+            sn: data.sn || '',
+            type: data.type || '',
+            contactName: data.contactName || '',
+            tel: data.tel || '',
+            address: data.address || '',
+            area: data.area,
+            storageCondition: data.storageCondition || '',
+            extraInfo: data.extraInfo || '',
+            remark: data.remark || '',
+            shelves: data.shelves || [],
+        } as WarehouseAddDTO;
+    }
+};
+
+const submit = async () => {
+    try {
+        await upFormRef.value?.validate();
+    } catch (error: any) {
+        // 滚动到第一个错误字段
+        const firstErrorField = error && error[0].prop + 'pppp';
+        paging.value?.scrollIntoViewById(firstErrorField, 30, true);
+        return;
+    }
+    try {
+        await uni.showLoading({ title: did.value ? '保存中...' : '提交中...', mask: true });
+        const url = did.value ? '/plt-api/app/warehouse/update' : '/plt-api/app/warehouse/add';
+        const payload = { ...(form.value as any) };
+        if (did.value) payload.id = did.value;
+        const res = await useClientRequest.post(url, payload);
+        uni.hideLoading();
+        if (res && res.code === 200) {
+            uni.showToast({ title: did.value ? '保存成功' : '新增成功', icon: 'success' });
+            uni.$emit('refreshStorageRoomList');
+            uni.$emit('storage-room-detail-refresh');
+            setTimeout(() => uni.navigateBack(), 300);
+        }
+    } catch (e) {
+        uni.hideLoading();
+        console.error('保存库房失败:', e);
+    }
+};
+const selectStorageMember = () => {
+    uni.$on('selectCpyMember', (item: any) => {
+        form.value.contact = item.userInfo?.id;
+        form.value.contactName = item.userInfo?.name;
+        form.value.tel = item.userInfo?.phone;
+        uni.$off('selectCpyMember');
+    });
+    uni.$u.route({
+        type: 'navigateTo',
+        url: '/tools/select-cpy-member/index',
+    });
+};
+
+// 货位增删改
+const shelveOptions = ref([
+    { text: '编辑', name: 'edit', style: { backgroundColor: '#37a954', color: '#fff' } },
+    { text: '删除', name: 'delete', style: { backgroundColor: '#FF3B30', color: '#fff' } },
+]);
+const showShelve = ref(false);
+const showShelveEdit = ref(false);
+const shelveTitle = ref('添加货位');
+const rowShelve = ref<WarehouseShelfAddDTO>({ shelvesName: '', sn: '', remark: '', warehouseId: 0 });
+const rowShelveEdit = ref<WarehouseShelfAddDTO>({ shelvesName: '', sn: '', remark: '', warehouseId: 0 });
+const rowShelveEditIdx = ref<number>(-1);
+
+const addShelve = () => {
+    rowShelve.value = { shelvesName: '', sn: '', remark: '', warehouseId: did.value ? Number(did.value) : 0 };
+    shelveTitle.value = '添加货位';
+    showShelve.value = true;
+};
+const clickShelveSwipe = (e: any, item: WarehouseShelfAddDTO, idx: number) => {
+    const index = e.index;
+    if (index === 0) {
+        // 编辑
+        shelveTitle.value = '编辑货位';
+        rowShelveEdit.value = { ...(item || {}) } as WarehouseShelfAddDTO;
+        rowShelveEditIdx.value = idx;
+        showShelveEdit.value = true;
+    }
+    if (index === 1) {
+        // 删除
+        form.value.shelves?.splice(idx, 1);
+    }
+};
+const submitFormShelveAdd = (data: WarehouseShelfAddDTO) => {
+    form.value.shelves?.push({ ...data });
+};
+const submitFormShelveEdit = (data: WarehouseShelfAddDTO) => {
+    if (rowShelveEditIdx.value === -1) return;
+    form.value.shelves![rowShelveEditIdx.value] = { ...data };
+};
+onLoad((options: any) => {
+    did.value = options?.id || '';
+    if (did.value) {
+        loadDetail(did.value);
+    }
+    paging.value?.complete();
+});
+</script>
+<style lang="scss" scoped>
+.startline-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #333;
+}
+.base-bottom-wrap {
+    background-color: #fff;
+}
+.plot-item {
+    border: 1rpx solid rgba($u-primary, 0.4);
+    border-radius: 10rpx;
+}
+</style>

+ 101 - 0
src/plant/storage/storage-room/edit/models/form-shelve.vue

@@ -0,0 +1,101 @@
+<template>
+    <ut-confirm-dialog v-model:show="visable" :title="title" width="680rpx" @close="onClose">
+        <template #content>
+            <scroll-view scroll-y class="scroll-view-content" style="max-height: 60vh">
+                <view class="p-rtv pd4-0-24-24-24">
+                    <up-form ref="formRef" :model="form" :rules="rules" labelWidth="auto" class="p-rtv" labelPosition="top">
+                        <up-form-item borderBottom label="货架名称" required prop="shelvesName">
+                            <up-input v-model="form.shelvesName" placeholder="请输入货架名称" border="none" clearable></up-input>
+                        </up-form-item>
+                        <up-form-item borderBottom label="货架编号" prop="sn">
+                            <up-input v-model="form.sn" placeholder="请输入货架编号" border="none" clearable></up-input>
+                        </up-form-item>
+                        <up-form-item label="备注" prop="remark">
+                            <up-textarea v-model="form.remark" placeholder="请输入备注" autoHeight></up-textarea>
+                        </up-form-item>
+                    </up-form>
+                </view>
+            </scroll-view>
+        </template>
+        <template #footer>
+            <view class="pd-24 d-flex a-c">
+                <up-button class="mr-16" @click="onClose">取消</up-button>
+                <up-button type="primary" @click="onConfirm">确定</up-button>
+            </view>
+        </template>
+    </ut-confirm-dialog>
+</template>
+
+<script setup lang="ts" name="FormPlot">
+import { WarehouseShelfAddDTO } from '../index';
+const props = withDefaults(
+    defineProps<{
+        show: boolean;
+        title?: string;
+        modelValue?: WarehouseShelfAddDTO;
+    }>(),
+    {
+        show: false,
+        title: '添加货位',
+        modelValue: () => ({
+          shelvesName: '',
+          sn: '',
+          remark: '',
+          warehouseId: 0,
+        }),
+    }
+);
+
+const visable = ref(props.show);
+const emit = defineEmits<{
+    'update:show': [value: boolean];
+    close: [];
+    submit: [data: WarehouseShelfAddDTO];
+}>();
+
+const formRef = ref();
+const submitting = ref(false);
+const form = ref<WarehouseShelfAddDTO>({
+    shelvesName: '',
+    sn: '',
+    remark: '',
+    warehouseId: 0,
+});
+
+const rules = reactive({
+    shelvesName: [{ required: true, message: '请输入货架名称', trigger: ['blur', 'change'] }],
+    // sn 可选,如需唯一校验可在外部提交前处理
+});
+
+const onClose = () => {
+    emit('update:show', false);
+    emit('close');
+};
+watch(
+    () => props.show,
+    (newVal) => {
+        visable.value = newVal;
+    }
+);
+const onConfirm = async () => {
+    try {
+        // uview-plus 的 up-form 暴露 validate 方法
+        await formRef.value?.validate();
+        emit('submit', form.value);
+        emit('update:show', false);
+    } catch (e) {
+        // 校验不通过
+    }
+};
+watch(
+    () => props.modelValue,
+    (newVal) => {
+        if (!newVal) return;
+        // 合并外部传入的初值(包含 warehouseId)
+        Object.assign(form.value, newVal);
+    },
+    { immediate: true }
+);
+</script>
+
+<style scoped lang="scss"></style>

+ 83 - 75
src/plant/storage/storage-room/list/index.vue

@@ -4,9 +4,9 @@
             <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="选择库房类型">
+                    <ut-action-sheet v-model="form.type" :tabs="pt_warehouse_type" @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(pt_warehouse_type, form.type) || '全部类型' }}</view>
                             <up-icon size="24rpx" color="#333" name="arrow-down-fill" class="mr-5"></up-icon>
                         </view>
                     </ut-action-sheet>
@@ -16,88 +16,66 @@
                 </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 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?.storeCount" :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>
-                        </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 v-if="item?.sn" class="f-s-24 c-#666">{{ item?.sn }}</view>
+                            <view class="pd-5"></view>
+                            <view class="f-s-28 pd2-5-0">
+                                <span class="c-#666">库房类型:</span>
+                                <span class="c-#333 f-w-600">{{ selectDictLabel(pt_warehouse_type, item?.type) }}</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 || 0 }}</span>
+                            </view>
+                        </view>
+                    </up-swipe-action-item>
+                </up-swipe-action>
             </view>
         </template>
+        <!-- 空数据处理 -->
+
+        <template #empty>
+            <ut-empty class="mg-at" size="28rpx" color="#999" padding="10rpx">暂未设置库房,点击+号新增库房吧</ut-empty>
+        </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 @click="$u.route({ url: '/plant/storage/storage-room/edit/index' })">
+        <image src="@/static/images/common/btn_add_logo.png" mode="widthFix" class="w-120 h-120"></image>
     </ut-suspension>
+    <form-shelve v-if="showShelve" v-model:show="showShelve" v-model="rowShelve" :title="shelveTitle" @submit="submitFormShelveAdd" />
 </template>
 <script setup lang="ts">
-import { copyText } from '@/utils/public';
 import { useClientRequest } from '@/utils/request';
-import { useInfoStore } from '@/store';
+import FormShelve from '../edit/models/form-shelve.vue';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { pt_warehouse_type } = toRefs<any>(proxy?.useDict('pt_warehouse_type'));
 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: '',
+    type: '',
 });
-
+const showShelve = ref(false);
+const rowShelve = ref<any>(null);
+const shelveTitle = ref('添加货位');
 const query = async (pageNum: number, pageSize: number) => {
     const params = {
         pageNum,
@@ -119,37 +97,67 @@ const optionsAction = reactive([
             backgroundColor: '#f56c6c',
         },
     },
+    // 添加货位
+    {
+        text: '添加货位',
+        style: {
+            backgroundColor: '#37A954',
+        },
+    },
 ]);
 const clickSwipe = async (event: object) => {
     const { name, index } = event as any;
     if (index === 0) {
         // 删除
         const res = await uni.showModal({
-            title: '提示',
-            content: '确定删除该GAP基地信息吗?',
+            title: '删除提示',
+            content: '删除后不可撤回,请谨慎操作!',
+            confirmColor: '#f56c6c',
         });
         console.log(res);
-
         if (res.confirm) {
-            const delRes = await useClientRequest.get(`/plt-api/app/gapCertificationInfo/delById/${name}`);
+            const delRes = await useClientRequest.get(`/plt-api/app/warehouse/delete/${name}`);
             if (delRes && delRes.code === 200) {
                 uni.showToast({ title: '删除成功', icon: 'none' });
                 onRefresh();
             }
         }
     }
+    if (index === 1) {
+        // 添加货位
+        shelveTitle.value = '添加货位';
+        rowShelve.value = {
+            shelvesName: '',
+            sn: '',
+            remark: '',
+            warehouseId: name,
+        };
+        showShelve.value = true;
+    }
 };
 const clickItem = (item: any) => {
     uni.$u.route({
         type: 'navigateTo',
-        url: '/plant/base/gap-base-info-detail/index',
+        url: '/plant/storage/storage-room/detail/index',
         params: {
             id: item?.id,
         },
     });
 };
+const submitFormShelveAdd = async (data: any) => {
+    try {
+        const res = await useClientRequest.post('/plt-api/app/shelves/add', data);
+        if (res && res.code === 200) {
+            uni.showToast({ title: '添加成功', icon: 'success' });
+            showShelve.value = false;
+            onRefresh();
+        }
+    } catch (e) {
+        console.error(e);
+    }
+};
 onMounted(() => {
-    uni.$on('gapBaseInfoUpdated', () => {
+    uni.$on('refreshStorageRoomList', () => {
         onRefresh();
     });
 });

BIN
src/static/images/common/btn_add_logo.png


BIN
src/static/images/plant/port/个体标识-从种源库快速添加.png


BIN
src/static/images/plant/port/个体标识-手动输入添加.png


BIN
src/static/images/plant/port/个体标识-扫一扫添加.png


BIN
src/static/images/plant/port/任务-养殖.png


BIN
src/static/images/plant/port/任务-种植.png


BIN
src/static/images/plant/port/任务-种畜繁育.png


BIN
src/static/images/plant/port/任务-种苗繁育.png


BIN
src/static/images/plant/port/任务类型-养殖.png


BIN
src/static/images/plant/port/任务类型-种植.png


BIN
src/static/images/plant/port/任务类型-种畜繁育.png


BIN
src/static/images/plant/port/任务类型-种苗繁育.png


BIN
src/static/images/plant/port/已结束.png


BIN
src/static/images/plant/port/种源信息.png


BIN
src/static/images/plant/port/采收管理.png


BIN
src/static/images/plant/port/饲养管理.png