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

feat: 增加农资报价维护,服务维护页面

lxf пре 3 дана
родитељ
комит
033259f4da

+ 18 - 0
src/router/globalRoutes.js

@@ -206,6 +206,24 @@ export default [
         name: "PriceDetail",
         component: () => import("@/views/old_mini/price_detail/index.vue"),
     },
+    // 报价维护
+    {
+        path: "/offer_price",
+        name: "OfferPrice",
+        component: () => import("@/views/old_mini/offer_price/index.vue"),
+    },
+    // 报价维护
+    {
+        path: "/edit_price",
+        name: "EditPrice",
+        component: () => import("@/views/old_mini/offer_price/component/editPrice.vue"),
+    },
+    // 服务维护
+    {
+        path: "/service_manage",
+        name: "ServiceManage",
+        component: () => import("@/views/old_mini/service_manage/index.vue"),
+    },
     // 气象预警详情/农事预警详情
     {
         path: "/warning_detail",

+ 8 - 0
src/views/old_mini/mine/index.vue

@@ -125,6 +125,14 @@ const cellItems = ref([
         title: "认证农资",
         path: "/register?identity=NZ&role=2",
     },
+    {
+        title: "报价维护",
+        path: "/offer_price?identity=NZ&role=2",
+    },
+    {
+        title: "服务维护",
+        path: "/service_manage?identity=NZ&role=2",
+    },
     // {
     //     title: "联系客服",
     //     path: "/customer-service",

+ 294 - 0
src/views/old_mini/offer_price/component/editPrice.vue

@@ -0,0 +1,294 @@
+<template>
+    <div class="edit-price">
+        <custom-header name="飞鸟管家"></custom-header>
+        <div class="edit-form">
+            <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
+                <el-form-item label="药肥名称" prop="name" required>
+                    <el-select v-model="form.name" placeholder="请选择" filterable clearable style="width: 100%">
+                        <el-option v-for="opt in nameOptions" :key="opt" :label="opt" :value="opt" />
+                    </el-select>
+                </el-form-item>
+
+                <el-form-item label="药肥品牌" prop="brand" required>
+                    <el-select v-model="form.brand" placeholder="请选择" filterable clearable style="width: 100%">
+                        <el-option v-for="opt in brandOptions" :key="opt" :label="opt" :value="opt" />
+                    </el-select>
+                </el-form-item>
+
+                <el-form-item label="药肥类型" required>
+                    <div class="row-3-selects">
+                        <el-select v-model="form.type1" placeholder="请选择" filterable clearable>
+                            <el-option v-for="opt in type1Options" :key="opt" :label="opt" :value="opt" />
+                        </el-select>
+                        <el-select v-model="form.type2" placeholder="请选择" filterable clearable>
+                            <el-option v-for="opt in type2Options" :key="opt" :label="opt" :value="opt" />
+                        </el-select>
+                        <el-select v-model="form.type3" placeholder="请选择" filterable clearable>
+                            <el-option v-for="opt in type3Options" :key="opt" :label="opt" :value="opt" />
+                        </el-select>
+                    </div>
+                </el-form-item>
+
+                <el-form-item label="计量单位" prop="unit" required>
+                    <el-radio-group v-model="form.unit">
+                        <el-radio  value="克">克</el-radio>
+                        <el-radio value="千克">千克</el-radio>
+                        <el-radio value="毫升">毫升</el-radio>
+                        <el-radio value="升" >升</el-radio>
+                    </el-radio-group>
+                </el-form-item>
+
+                <el-form-item label="施用方式" prop="method" required>
+                    <el-radio-group v-model="form.method">
+                        <el-radio value="1">叶面施</el-radio>
+                        <el-radio value="2">根部施</el-radio>
+                    </el-radio-group>
+                </el-form-item>
+
+                <el-form-item label="药肥成本" prop="cost" required class="input-unit">
+                    <el-input v-model.number="form.cost" placeholder="请输入">
+                        <template #append>元/{{ form.unit }}</template>
+                    </el-input>
+                </el-form-item>
+
+                <el-form-item label="药肥报价" prop="price" required class="input-unit">
+                    <el-input v-model.number="form.price" placeholder="请输入">
+                        <template #append>元/{{ form.unit }}</template>
+                    </el-input>
+                </el-form-item>
+
+                <el-form-item label="药肥库存" prop="stock" required class="input-unit">
+                    <el-input v-model.number="form.stock" placeholder="请输入">
+                        <template #append>{{ form.unit }}</template>
+                    </el-input>
+                </el-form-item>
+
+                <el-form-item label="单位兑水" class="input-unit">
+                    <div class="water-row">
+                        <el-input v-model.number="form.waterMin" placeholder="请输入兑水量">
+                            <template #append>kg</template>
+                        </el-input>
+                        <el-input v-model.number="form.waterMax" placeholder="请输入兑水量">
+                            <template #append>kg</template>
+                        </el-input>
+                    </div>
+                </el-form-item>
+
+                <el-form-item label="适用品种">
+                    <el-select v-model="form.crops" multiple placeholder="请选择" filterable clearable style="width: 100%">
+                        <el-option v-for="opt in cropOptions" :key="opt" :label="opt" :value="opt" />
+                    </el-select>
+                </el-form-item>
+            </el-form>
+        </div>
+
+        <div class="page-action">
+            <div class="btn-item del" v-if="!isAdd" @click="openDelete">删除</div>
+            <div class="btn-item cancel" v-if="isAdd" @click="goBack">取消</div>
+            <div class="btn-right">
+                <div class="btn-item cancel" v-if="!isAdd" @click="goBack">取消编辑</div>
+                <div class="btn-item primary" @click="handleSave">{{ isAdd ? '新增报价' : '保存报价' }}</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import customHeader from "@/components/customHeader.vue";
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { useRouter, useRoute } from "vue-router";
+import { ref, reactive } from 'vue'
+
+const router = useRouter();
+const route = useRoute();
+const isAdd = ref(false);
+if (route.query.isAdd) {
+    isAdd.value = true;
+}
+const goBack = () => {
+    router.back();
+}
+
+const openDelete = () => {
+  ElMessageBox.confirm(
+    '确认要删除该报价吗?',
+    '提示',
+    {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(() => {
+    })
+    .catch(() => {
+    })
+}
+
+// 表单模型与选项
+const formRef = ref()
+const form = reactive({
+    name: '',
+    brand: '',
+    type1: '',
+    type2: '',
+    type3: '',
+    unit: '克',
+    method: '叶面施',
+    cost: null,
+    price: null,
+    stock: null,
+    waterMin: null,
+    waterMax: null,
+    crops: ['荔枝', '香蕉']
+})
+
+const rules = {
+    name: [{ required: true, message: '请选择药肥名称', trigger: 'change' }],
+    brand: [{ required: true, message: '请选择药肥品牌', trigger: 'change' }],
+    unit: [{ required: true, message: '请选择计量单位', trigger: 'change' }],
+    method: [{ required: true, message: '请选择施用方式', trigger: 'change' }],
+    cost: [{ required: true, message: '请输入成本', trigger: 'blur' }],
+    price: [{ required: true, message: '请输入报价', trigger: 'blur' }],
+    stock: [{ required: true, message: '请输入库存', trigger: 'blur' }]
+}
+
+const nameOptions = ['乙烯利乙烯利']
+const brandOptions = ['国光']
+const type1Options = ['农药类', '调节', '肥料']
+const type2Options = ['一级', '二级', '三级']
+const type3Options = ['一级', '二级', '三级']
+const cropOptions = ['荔枝', '香蕉', '龙眼', '芒果']
+</script>
+
+<style lang="scss" scoped>
+.edit-price {
+    width: 100%;
+    height: 100vh;
+    background: #f5f7fb;
+    .edit-form {
+        margin: 12px;
+        padding: 12px;
+        background: #FFFFFF;
+        .row-3-selects {
+            width: 100%;
+            display: flex;
+            gap: 5px;
+            ::v-deep {
+                .el-select__wrapper {
+                    padding: 2px 6px;
+                }
+            }
+            > .el-select { flex: 1; }
+        }
+        .water-row {
+            display: flex;
+            gap: 12px;
+            > .el-input { flex: 1; }
+        }
+
+        ::v-deep {
+            .el-form-item__label {
+                color: rgba(29, 33, 41, 0.2);
+                font-size: 14px;
+            }
+            .el-form-item--default {
+                margin-bottom: 16px;
+            }
+            .el-select__input {
+                color: #2199F8;
+            }
+            .el-select__wrapper {
+                // height: 30px;
+                color: #2199F8;
+                min-height: 30px;
+                line-height: 28px;
+                box-shadow: 0 0 0 1px rgba(33, 153, 248, 0.3) inset;
+            }
+            .el-select__caret {
+                color: #2199F8;
+            }
+            .el-select__placeholder {
+                color: #2199F8;
+            }
+            .el-radio {
+                margin-right: 16px;
+            }
+            .el-input__wrapper {
+                box-shadow: none;
+            }
+            .el-input-group__append {
+                padding: 0 10px;
+                background: none;
+                box-shadow: none;
+            }
+            .el-input-group__append {
+                color: rgba(33, 153, 248, 0.5);
+            }
+            .el-tag.el-tag--info {
+                --el-tag-text-color: #2199F8;
+                --el-tag-bg-color: rgba(33, 153, 248, 0.1);
+            }
+            .input-unit {
+                .el-input {
+                    border: 1px solid rgba(33, 153, 248, 0.3);
+                    border-radius: 5px;
+                    height: 30px;
+                    box-sizing: border-box;
+                }
+                .el-input__wrapper {
+                    padding: 0 2px 0 10px;
+                    height: 28px;
+                    line-height: 28px;
+                    min-height: 28px;
+                }
+                .el-input__inner {
+                    --el-input-inner-height: 28px;
+                    height: 28px;
+                    line-height: 28px;
+                    min-height: 28px;
+                    color: #2199F8;
+                    --el-input-placeholder-color: rgba(33, 153, 248, 0.43);
+                }
+            }
+        }
+    }
+
+    .page-action {
+        position: fixed;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        padding: 12px 12px;
+        background: #fff;
+        box-shadow: 2px 2px 4.5px rgba(0, 0, 0, 0.4);
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .btn-item {
+            height: 40px;
+            line-height: 41px;
+            border-radius: 20px;
+            width: fit-content;
+            padding: 0 20px;
+            color: #666666;
+            font-size: 14px;
+            &.del {
+                color: #FF943D;
+                background: rgba(255, 148, 61, 0.1);
+            }
+            &.cancel {
+                border: 1px solid rgba(153, 153, 153, 0.5);
+            }
+            &.primary {
+                color: #fff;
+                background: linear-gradient(#76C3FF, #2199F8);
+            }
+        }
+        .btn-right {
+            display: flex;
+            gap: 10px;
+        }
+    }
+}
+</style>

+ 292 - 0
src/views/old_mini/offer_price/component/fertilizerPrice.vue

@@ -0,0 +1,292 @@
+<template>
+    <div class="fertilizer-price">
+        <search v-model="searchVal" placeholder="搜索专家" />
+        <div class="record-filter">
+            <div
+                class="filter-item"
+                v-for="(item, index) in filterType"
+                :key="index"
+                @click="handlePlanClick(index)"
+                :class="{ active: activePlanIndex === index }"
+            >
+                {{ item }}
+            </div>
+        </div>
+        <div class="fertilizer-list">
+            <div class="fertilizer-card" @click.stop="handleEdit(item)" v-for="(item, index) in 4" :key="index">
+                <div class="card-header">
+                    <div class="title">乙烯利乙烯利乙烯利</div>
+                    <div class="action-btn">
+                        <el-icon @click.stop="handleEdit(item)" color="#2199F8" size="18"><Edit /></el-icon>
+                        <el-icon @click.stop="openDelete" color="#E04C4C" size="18"><Delete /></el-icon>
+                    </div>
+                </div>
+                <div class="info">
+                    <div class="row">
+                        <span class="label">药物品牌</span>
+                        <span class="value">国光国光国光</span>
+                    </div>
+                    <div class="row">
+                        <span class="label">药费类型</span>
+                        <span class="value">农药类/杀虫剂/胃毒性</span>
+                    </div>
+                    <div class="row">
+                        <span class="label">施用方式</span>
+                        <span class="value">叶面施</span>
+                    </div>
+                    <transition name="collapse">
+                        <div class="extra-info" v-show="isExpanded(index)">
+                            <div class="row">
+                                <span class="label">计量单位</span>
+                                <span class="value">克</span>
+                            </div>
+                            <div class="row">
+                                <span class="label">单位兑水量</span>
+                                <span class="value">1000ml-2000ml</span>
+                            </div>
+                            <div class="row">
+                                <span class="label">适用品类</span>
+                                <span class="value tag-group">
+                                    <span class="tag-item">荔枝</span>
+                                    <span class="tag-item">龙眼</span>
+                                </span>
+                            </div>
+                        </div>
+                    </transition>
+                </div>
+                <div class="expand" @click.stop="toggleExpand(index)">
+                    <span>{{ isExpanded(index) ? '收起' : '展开更多' }}</span>
+                    <el-icon :class="{ rotate: isExpanded(index) }"><ArrowDown /></el-icon>
+                </div>
+                <div class="stats">
+                    <div class="col">
+                        <div class="num">1258<span class="unit"> 元/克</span></div>
+                        <div class="desc">成本</div>
+                    </div>
+                    <div class="col">
+                        <div class="num">1258<span class="unit"> 元/克</span></div>
+                        <div class="desc">报价</div>
+                    </div>
+                    <div class="col">
+                        <div class="num">12222<span class="unit"> 克</span></div>
+                        <div class="desc">库存</div>
+                    </div>
+                </div>
+            </div>
+            <div class="page-action">
+                <div class="btn-primary" @click="handleAdd">新增药肥报价</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { Search } from 'vant';
+import { useRouter } from "vue-router";
+import { ElMessageBox } from 'element-plus'
+
+const router = useRouter();
+const searchVal = ref("");
+
+const filterType = ref(["全部", "农药", "调节", "肥料"]);
+const activePlanIndex = ref(0);
+// 记录已展开卡片索引的集合
+const expandedSet = ref(new Set());
+
+const handlePlanClick = (index) => {
+    activePlanIndex.value = index;
+};
+const isExpanded = (index) => expandedSet.value.has(index);
+const toggleExpand = (index) => {
+    const next = new Set(expandedSet.value);
+    if (next.has(index)) {
+        next.delete(index);
+    } else {
+        next.add(index);
+    }
+    expandedSet.value = next;
+};
+const handleEdit = (item) => {
+    router.push({
+        path: "/edit_price",
+        query: { id: item.id }
+    });
+};
+
+const openDelete = () => {
+  ElMessageBox.confirm(
+    '确认要删除该报价吗?',
+    '提示',
+    {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(() => {
+    })
+    .catch(() => {
+    })
+}
+const handleAdd = () => {
+    router.push({
+        path: "/edit_price",
+        query: { isAdd: true }
+    });
+};
+</script>
+
+<style lang="scss" scoped>
+.fertilizer-price {
+    ::v-deep {
+        .van-search {
+            background: #F5F7FB;
+            .van-search__content {
+                background: #FFFFFF;
+                border-radius: 16px;
+            }
+        }
+    }
+    .record-filter {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        padding: 5px 0;
+        .filter-item {
+            color: rgba(0, 0, 0, 0.5);
+            padding: 0 16px;
+            height: 32px;
+            line-height: 32px;
+            border-radius: 20px;
+            color: #8B8B8B;
+            &.active {
+                color: #fff;
+                background: #2199F8;
+            }
+        }
+        .filter-item + .filter-item {
+            margin-left: 8px;
+        }
+    }
+    .fertilizer-list {
+        padding: 8px 12px 80px;
+        .fertilizer-card {
+            margin-bottom: 10px;
+            background: #FFFFFF;
+            border-radius: 8px;
+            // box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+            padding: 12px 10px;
+            .card-header {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                .title {
+                    font-size: 16px;
+                    font-weight: 600;
+                    color: #000;
+                }
+                .action-btn {
+                    display: flex;
+                    gap: 10px;
+                }
+            }
+            .info {
+                padding-top: 4px;
+                border-top: 1px solid #F5F5F5;
+                margin-top: 8px;
+                font-size: 14px;
+                .row {
+                    display: flex;
+                    padding: 4px 0;
+                    .label {
+                        width: 80px;
+                        color: rgba(0, 0, 0, 0.2);
+                        flex-shrink: 0;
+                    }
+                    .value {
+                        color: #767676;
+                    }
+                }
+                .extra-info {
+                    overflow: hidden;
+                }
+            }
+            .tag-group {
+                display: flex;
+                align-items: center;
+                gap: 4px;
+                .tag-item {
+                    font-size: 12px;
+                    color: #2199F8;
+                    background: #E8F3FF;
+                    border-radius: 2px;
+                    padding: 0 8px;
+                    height: 20px;
+                    line-height: 20px;
+                }
+            }
+            .expand {
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                color: rgba(0, 0, 0, 0.2);
+                font-size: 12px;
+                margin: 6px 0 12px;
+                span { margin-right: 4px; }
+                cursor: pointer;
+                .el-icon { transition: transform .3s ease; }
+                .el-icon.rotate { transform: rotate(180deg); }
+            }
+            .stats {
+                display: flex;
+                align-items: stretch;
+                justify-content: space-between;
+                background: rgba(33, 153, 248, 0.1);
+                border: 1px solid rgba(33, 153, 248, 0.2);
+                border-radius: 10px;
+                padding: 10px 8px;
+                font-size: 14px;
+                .col {
+                    flex: 1;
+                    text-align: center;
+                    .num {
+                        color: #2199F8;
+                        font-weight: 600;
+                        font-size: 16px;
+                        .unit { font-size: 12px; font-weight: 400; color: rgba(0, 0, 0, 0.5); }
+                    }
+                    .desc { color: #000; margin-top: 4px; }
+                }
+                .col + .col { border-left: 1px solid rgba(33, 153, 248, 0.4); }
+            }
+        }
+        .page-action {
+            position: fixed;
+            left: 0; right: 0; bottom: 0;
+            padding: 10px 16px;
+            background: #fff;
+            box-shadow: 2px 2px 4.5px rgba(0, 0, 0, 0.4);
+            .btn-primary {
+                margin: 0 auto;
+                height: 40px;
+                line-height: 40px;
+                border-radius: 20px;
+                width: fit-content;
+                padding: 0 20px;
+                color: #fff;
+                font-size: 14px;
+                background: linear-gradient(#76C3FF, #2199F8);
+            }
+        }
+    }
+}
+
+/* 折叠动画:使用 max-height + 透明度 以获得平滑过渡 */
+.collapse-enter-active,
+.collapse-leave-active { transition: max-height .3s cubic-bezier(0.4, 0, 0.2, 1), opacity .3s cubic-bezier(0.4, 0, 0.2, 1); }
+.collapse-enter-from,
+.collapse-leave-to { max-height: 0; opacity: 0; }
+.collapse-enter-to,
+.collapse-leave-from { max-height: 90px; opacity: 1; }
+</style>

+ 148 - 0
src/views/old_mini/offer_price/component/servicePrice.vue

@@ -0,0 +1,148 @@
+<template>
+    <div class="service-price">
+        <div class="service-form">
+            <div class="service-form-title">服务报价</div>
+            <el-form ref="formRef" :model="form" label-width="86px">
+                <el-form-item label="人工服务" prop="manual" class="input-unit">
+                    <el-input v-if="isEdit" v-model.number="form.manual" placeholder="请输入数字">
+                        <template #append>元/亩</template>
+                    </el-input>
+                    <div v-else class="service-form-value">12<span class="unit">元/亩</span></div>
+                </el-form-item>
+                <el-form-item label="无人机服务" prop="drone" class="input-unit">
+                    <el-input v-if="isEdit" v-model.number="form.drone" placeholder="请输入数字">
+                        <template #append>元/亩</template>
+                    </el-input>
+                    <div v-else class="service-form-value">120<span class="unit">元/亩</span></div>
+                </el-form-item>
+            </el-form>
+        </div>
+        <div class="page-action" v-if="isEdit">
+            <div class="btn-item cancel" @click="isEdit = false">取消</div>
+            <div class="btn-right">
+                <div class="btn-item primary" @click="isEdit = false">保存服务报价</div>
+            </div>
+        </div>
+        <div class="page-action" v-else>
+            <div class="btn-item primary center-btn" @click="isEdit = true">编辑服务报价</div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, reactive } from "vue";
+
+// 表单模型与选项
+const formRef = ref();
+const form = reactive({
+    manual: "",
+    drone: "",
+});
+
+const isEdit = ref(false);
+</script>
+
+<style lang="scss" scoped>
+.service-price {
+    width: 100%;
+    height: 100%;
+
+    .service-form {
+        margin: 12px;
+        padding: 12px 12px 2px 12px;
+        background: #ffffff;
+        border-radius: 8px;
+        .service-form-title {
+            font-size: 16px;
+            color: #000000;
+            margin-bottom: 10px;
+        }
+        .service-form-value {
+            .unit {
+                color: rgba(0, 0, 0, 0.3);
+                padding-left: 8px;
+            }
+        }
+        ::v-deep {
+            .el-form-item__label {
+                color: rgba(0, 0, 0, 0.4);
+                font-size: 14px;
+            }
+            .el-form-item--default {
+                margin-bottom: 16px;
+            }
+            .el-input__wrapper {
+                box-shadow: none;
+            }
+            .el-input-group__append {
+                padding: 0 10px;
+                background: none;
+                box-shadow: none;
+                color: rgba(0, 0, 0, 0.3);
+            }
+            .input-unit {
+                .el-input {
+                    border: 1px solid rgba(24, 24, 24, 0.3);
+                    border-radius: 5px;
+                    height: 30px;
+                    box-sizing: border-box;
+                }
+                .el-input__wrapper {
+                    padding: 0 2px 0 10px;
+                    height: 28px;
+                    line-height: 28px;
+                    min-height: 28px;
+                }
+                .el-input__inner {
+                    --el-input-inner-height: 28px;
+                    height: 28px;
+                    line-height: 28px;
+                    min-height: 28px;
+                    color: #000000;
+                    // --el-input-placeholder-color: rgba(33, 153, 248, 0.43);
+                }
+            }
+        }
+    }
+
+    .page-action {
+        position: fixed;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        padding: 12px 12px;
+        background: #fff;
+        box-shadow: 2px 2px 4.5px rgba(0, 0, 0, 0.4);
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .btn-item {
+            height: 40px;
+            line-height: 41px;
+            border-radius: 20px;
+            width: fit-content;
+            padding: 0 20px;
+            color: #666666;
+            font-size: 14px;
+            &.del {
+                color: #ff943d;
+                background: rgba(255, 148, 61, 0.1);
+            }
+            &.cancel {
+                border: 1px solid rgba(153, 153, 153, 0.5);
+            }
+            &.primary {
+                color: #fff;
+                background: linear-gradient(#76c3ff, #2199f8);
+            }
+        }
+        .center-btn {
+            margin: 0 auto;
+        }
+        .btn-right {
+            display: flex;
+            gap: 10px;
+        }
+    }
+}
+</style>

+ 60 - 0
src/views/old_mini/offer_price/index.vue

@@ -0,0 +1,60 @@
+<template>
+    <div class="agri-services">
+        <custom-header name="报价维护"></custom-header>
+        <tabs v-model:active="active" class="tabs">
+            <tab title="肥药报价">
+                <fertilizer-price />
+            </tab>
+            <tab title="服务报价">
+                <service-price />
+            </tab>
+        </tabs>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import { Tab, Tabs } from "vant";
+import fertilizerPrice from "./component/fertilizerPrice.vue";
+import servicePrice from "./component/servicePrice.vue";
+import customHeader from "@/components/customHeader.vue";
+const active = ref(0);
+
+</script>
+<style lang="scss" scoped>
+.agri-services {
+    width: 100%;
+    height: 100vh;
+    .farm-dynamics-container {
+        ::v-deep{
+            .task-content{
+                height: calc(100% - 80px);
+            }
+        }
+    }
+    .tabs {
+        height: calc(100% - 40px);
+        ::v-deep {
+            .van-tabs__wrap {
+                margin-bottom: 8px;
+            }
+            .van-tabs__line {
+                width: 24px;
+                height: 4px;
+            }
+            .van-tab {
+                width: 100px;
+                flex: none;
+            }
+            .van-tabs__nav {
+                justify-content: center;
+            }
+            .van-tabs__content {
+                overflow: auto;
+                height: calc(100% - 44px);
+                background: #F5F7FB;
+            }
+        }
+    }
+}
+</style>

+ 299 - 0
src/views/old_mini/service_manage/index.vue

@@ -0,0 +1,299 @@
+<template>
+    <div class="service-manage">
+        <custom-header name="服务维护"></custom-header>
+        <div class="main-content">
+            <div class="service-title">
+                <img class="label-icon" src="@/assets/img/home/label-icon.png" alt="">
+                服务信息
+            </div>
+            <div class="service-content">
+                <div class="service-item">
+                    <div class="sub-title">服务作物</div>
+                    <div class="tag-group" v-if="!isEdit">
+                        <div class="tag-item" v-for="(item, idx) in crops" :key="'c-'+idx">{{ item.name }}</div>
+                    </div>
+                    <div class="tag-group add-tag-group" v-else>
+                        <div class="tag-item" :class="{ self: item.isSelf === 1 }" v-for="(item, idx) in crops" :key="'ce-'+idx">
+                            <span class="text">{{ item.name }}<el-icon @click.stop="handleEdit(item.name)" v-if="item.isSelf===1" class="edit-icon"><Edit /></el-icon></span>
+                            <el-icon v-if="item.isSelf===1" class="del-icon" @click.stop="handleDelete('crops', idx)"><Close /></el-icon>
+                        </div>
+                        <div class="tag-item last-add" @click="handleAdd('作物')"><el-icon class="add-icon"><Plus /></el-icon>作物</div>
+                    </div>
+                </div>
+                <div class="service-item">
+                    <div class="sub-title">服务类型</div>
+                    <div class="tag-group" v-if="!isEdit">
+                        <div class="tag-item" v-for="(item, idx) in serviceTypes" :key="'t-'+idx">{{ item.name }}</div>
+                    </div>
+                    <div class="tag-group add-tag-group" v-else>
+                        <div class="tag-item" :class="{ self: item.isSelf === 1 }" v-for="(item, idx) in serviceTypes" :key="'te-'+idx">
+                            <span class="text">{{ item.name }}<el-icon @click.stop="handleEdit(item.name)" v-if="item.isSelf===1" class="edit-icon"><Edit /></el-icon></span>
+                            <el-icon v-if="item.isSelf===1" class="del-icon" @click.stop="handleDelete('serviceTypes', idx)"><Close /></el-icon>
+                        </div>
+                        <div class="tag-item last-add" @click="handleAdd('类型')"><el-icon class="add-icon"><Plus /></el-icon>类型</div>
+                    </div>
+                </div>
+                <div class="service-item">
+                    <div class="sub-title">农机设备</div>
+                    <div class="tag-group" v-if="!isEdit">
+                        <div class="tag-item" v-for="(item, idx) in machines" :key="'m-'+idx">{{ item.name }}</div>
+                    </div>
+                    <div class="tag-group add-tag-group" v-else>
+                        <div class="tag-item" :class="{ self: item.isSelf === 1 }" v-for="(item, idx) in machines" :key="'me-'+idx">
+                            <span class="text">{{ item.name }}<el-icon @click.stop="handleEdit(item.name)" v-if="item.isSelf===1" class="edit-icon"><Edit /></el-icon></span>
+                            <el-icon v-if="item.isSelf===1" class="del-icon" @click.stop="handleDelete('machines', idx)"><Close /></el-icon>
+                        </div>
+                        <div class="tag-item last-add" @click="handleAdd('设备')"><el-icon class="add-icon"><Plus /></el-icon>设备</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        
+        <div class="page-action" v-if="isEdit">
+            <div class="btn-item cancel" @click="isEdit = false">取消</div>
+            <div class="btn-right">
+                <div class="btn-item primary" @click="isEdit = false">保存服务类型</div>
+            </div>
+        </div>
+        <div class="page-action" v-else>
+            <div class="btn-item primary center-btn" @click="isEdit = true">编辑服务类型</div>
+        </div>
+    </div>
+
+    <popup class="add-tag-popup" round v-model:show="showAddPopup">
+        <div class="popup-title" v-if="isEditPopup">编辑标签</div>
+        <div class="popup-title" v-else>添加{{addTypeName}}<span class="ml-2">标签</span></div>
+        <el-input class="popup-input" v-model="input" placeholder="标签" size="large" />
+        <div class="popup-button">
+            <div class="cancel" @click="showAddPopup = false">取消</div>
+            <div>{{isEditPopup ? '确定' : '添加'}}</div>
+        </div>
+    </popup>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import customHeader from "@/components/customHeader.vue";
+import { Popup} from "vant";
+import { Edit, Close, Plus } from '@element-plus/icons-vue'
+
+const isEdit = ref(true);
+const showAddPopup = ref(false);
+
+const input = ref("");
+
+const addTypeName = ref("");
+function handleAdd(type) {
+    isEditPopup.value = false
+    addTypeName.value = type;
+    input.value = "";
+    showAddPopup.value = true;
+}
+
+// 三类数据:数组对象形式,随意赋值 isSelf=0/1
+const crops = ref([
+    { name: "荔枝", isSelf: 1 },
+    { name: "龙眼", isSelf: 1 },
+    { name: "芒果", isSelf: 0 }
+]);
+const serviceTypes = ref([
+    { name: "飞防类", isSelf: 1 },
+    { name: "技术类", isSelf: 1 },
+    { name: "劳力类", isSelf: 0 },
+    { name: "机械类", isSelf: 0 }
+]);
+const machines = ref([
+    { name: "收割机", isSelf: 1 },
+    { name: "M3E", isSelf: 0 }
+]);
+
+function handleDelete(category, index) {
+    if (category === 'crops') crops.value.splice(index, 1);
+    if (category === 'serviceTypes') serviceTypes.value.splice(index, 1);
+    if (category === 'machines') machines.value.splice(index, 1);
+}
+
+const isEditPopup = ref(false);
+function handleEdit(val) {
+    isEditPopup.value = true;
+    input.value = val;
+    showAddPopup.value = true;
+}
+</script>
+
+<style lang="scss" scoped>
+.service-manage {
+    width: 100%;
+    height: 100vh;
+    background: #F5F7FB;
+    .main-content {
+        max-height: calc(100% - 40px);
+        overflow: auto;
+        padding: 15px 12px;
+        margin: 14px 12px;
+        border-radius: 12px;
+        background: #fff;
+        .service-title {
+            font-size: 18px;
+            color: #222222;
+            font-weight: 500;
+            margin-bottom: 10px;
+            .label-icon {
+                width: 14px;
+                padding-right: 5px;
+            }
+        }
+        .service-content {
+            margin-top: 12px;
+            padding-top: 12px;
+            border-top: 1px solid #F5F5F5;
+            .service-item {
+                .sub-title {
+                    font-size: 16px;
+                    font-weight: 500;
+                    color: rgba(0, 0, 0, 0.9);
+                }
+                .tag-group {
+                    display: flex;
+                    align-items: center;
+                    flex-wrap: wrap;
+                    gap: 0 12px;
+                    font-size: 16px;
+                    .tag-item {
+                        margin-top: 10px;
+                        position: relative;
+                        background: #E8F5FF;
+                        border-radius: 8px;
+                        color: #2199f8;
+                        padding: 0 12px;
+                        box-sizing: border-box;
+                        min-width: 26vw;
+                        height: 48px;
+                        text-align: center;
+                        line-height: 48px;
+                        .text { display: inline-flex; align-items: center; }
+                        .edit-icon { margin-left: 8px; }
+                        .del-icon {
+                            position: absolute;
+                            right: -8px;
+                            top: -8px;
+                            background: #2199F8;
+                            border-radius: 50%;
+                            width: 16px; height: 16px;
+                            font-size: 10px;
+                            display: flex; align-items: center; justify-content: center;
+                            color: #fff;
+                        }
+                    }
+                    &.add-tag-group {
+                        .tag-item {
+                            color: #000000;
+                            background: none;
+                            border: 1px solid #999999;
+                            &.self {
+                                border: 1px solid #2199F8;
+                                background: #E8F5FF;
+                                color: #2199F8;
+                            }
+                            &.last-add {
+                                background: #F7F7F7;
+                                color: #343434;
+                                border: none;
+                                display: flex;
+                                align-items: center;
+                                justify-content: center;
+                                .add-icon {
+                                    font-size: 14px;
+                                    font-weight: bold;
+                                    margin-right: 3px;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            .service-item + .service-item {
+                margin-top: 20px;
+            }
+        }
+    }
+    
+    .page-action {
+        position: fixed;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        padding: 12px 12px;
+        background: #fff;
+        box-shadow: 2px 2px 4.5px rgba(0, 0, 0, 0.4);
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .btn-item {
+            height: 40px;
+            line-height: 41px;
+            border-radius: 20px;
+            width: fit-content;
+            padding: 0 20px;
+            color: #666666;
+            font-size: 14px;
+            &.del {
+                color: #ff943d;
+                background: rgba(255, 148, 61, 0.1);
+            }
+            &.cancel {
+                border: 1px solid rgba(153, 153, 153, 0.5);
+            }
+            &.primary {
+                color: #fff;
+                background: linear-gradient(#76c3ff, #2199f8);
+            }
+        }
+        .center-btn {
+            margin: 0 auto;
+        }
+        .btn-right {
+            display: flex;
+            gap: 10px;
+        }
+    }
+}
+
+
+.add-tag-popup{
+    width: 90%;
+    padding: 20px 16px;
+    .popup-title{
+        font-size: 18px;
+        font-weight: 500;
+        text-align: center;
+        margin-bottom: 12px;
+    }
+    .ml-2 {
+        margin-left: 3px;
+    }
+    .popup-input{
+        margin-bottom: 30px;
+    }
+    .popup-button{
+        display: flex;
+        padding-top: 20px;
+        border-top: 1px solid rgba(0, 0, 0, 0.1);
+        div{
+            flex: 1;
+            font-size: 16px;
+            padding: 9px;
+            border-radius: 20px;
+            background: #2199F8;
+            color: #fff;
+            text-align: center;
+        }
+        .cancel{
+            margin-right: 20px;
+            color: #000;
+            background: #fff;
+            border: 1px solid #999999;
+        }
+    }
+}
+</style>