Browse Source

feat(运费模板): 对接后端 API 替换前端模拟数据

ylong 2 weeks ago
parent
commit
dff7e967bf

+ 63 - 101
src/views/mallLogistics/feeTemplate/components/area-selector.vue

@@ -1,7 +1,6 @@
 <!-- 区域选择弹窗 -->
 <template>
-    <ele-modal form :width="1080" :bodyStyle="{ 'min-height': '400px' }" v-model="visible" :title="title"
-        @open="handleOpen">
+    <ele-modal form :width="1080" :bodyStyle="{ 'min-height': '400px' }" v-model="visible" :title="title">
         <div class="flex flex-col" v-loading="loading">
             <div class="flex" v-for="dq in areaList" :key="dq.code">
                 <el-checkbox :label="dq.name" :indeterminate="isIndeterminateShow(dq)" style="min-width: 100px"
@@ -49,6 +48,7 @@
 <script setup>
 import { ref, reactive, nextTick, computed, defineEmits, defineExpose } from 'vue';
 import { CaretBottom } from '@element-plus/icons-vue';
+import request from '@/utils/request';
 
 const emit = defineEmits(['confirm']);
 const visible = defineModel({ type: Boolean });
@@ -57,80 +57,53 @@ const title = ref('选择配送区域');
 const loading = ref(false);
 const areaList = ref([]);
 
-// Mock Data Generation
-const generateMockData = () => {
-    const regions = {
-        'hd': ['上海', '江苏', '浙江', '安徽', '福建', '江西', '山东'],
-        'hb': ['北京', '天津', '河北', '山西', '内蒙古'],
-        'hz': ['河南', '湖北', '湖南'],
-        'hn': ['广东', '广西', '海南'],
-        'db': ['辽宁', '吉林', '黑龙江'],
-        'xb': ['陕西', '甘肃', '青海', '宁夏', '新疆'],
-        'xn': ['重庆', '四川', '贵州', '云南', '西藏'],
-        'gat': ['香港', '澳门', '台湾'],
-        'hw': ['海外']
-    };
-    
-    const mockData = {};
-    let idCounter = 1;
-
-    for (const [code, provinces] of Object.entries(regions)) {
-        mockData[code] = provinces.map(prov => ({
-            id: idCounter++,
-            district: prov,
-            code: code, // Adding code for key if needed
-            mySelected: 0,
-            otherSelected: 0,
-            childInfo: [] // For simplicity, no cities for now, or maybe just one '全境' city if needed, but let's assume province level for fee template mostly. 
-                          // Wait, the original code uses childInfo for popover. Let's add dummy cities.
-        }));
-        
-        // Add dummy cities for interaction testing
-        mockData[code].forEach(prov => {
-             prov.childInfo = [
-                 { id: idCounter++, district: prov.district + '市1', mySelected: 0, otherSelected: 0 },
-                 { id: idCounter++, district: prov.district + '市2', mySelected: 0, otherSelected: 0 }
-             ];
-        });
+// Fetch Province Info
+const getProvinceInfo = async () => {
+    loading.value = true;
+    try {
+        const res = await request.get('/shipping/templete/provinceInfo');
+        if (res.data.code === 200) {
+            return res.data.data || [];
+        }
+    } catch (error) {
+        console.error(error);
+    } finally {
+        loading.value = false;
     }
-    return mockData;
+    return [];
 };
 
-// Define Regions
-const dqList = ref([
-    { name: '华东', code: 'hd' },
-    { name: '华北', code: 'hb' },
-    { name: '华中', code: 'hz' },
-    { name: '华南', code: 'hn' },
-    { name: '东北', code: 'db' },
-    { name: '西北', code: 'xb' },
-    { name: '西南', code: 'xn' },
-    { name: '港澳台', code: 'gat' },
-    { name: '海外', code: 'hw' }
-]);
-
-const handleOpen = (existingSelection) => {
+const handleOpen = async (existingSelection) => {
     visible.value = true;
     loading.value = true;
     
-    // Simulate API call
-    setTimeout(() => {
-        const data = generateMockData();
-        areaList.value = [];
-        dqList.value.forEach((item) => {
-            const dqItem = { ...item, checked: false, children: data[item.code] || [] };
-            areaList.value.push(dqItem);
+    const data = await getProvinceInfo();
+    
+    // Transform API data to component structure
+    areaList.value = data.map(area => ({
+        name: area.areaCode,
+        code: area.areaCode,
+        children: (area.provinceInfo || []).map(prov => ({
+            id: prov.provinceId,
+            district: prov.provinceName,
+            mySelected: 0,
+            childInfo: [] // API only provides provinces, no cities
+        }))
+    }));
+
+    // Restore selection
+    if (existingSelection && Array.isArray(existingSelection)) {
+        const selectedIds = new Set(existingSelection.map(item => item.provinceId));
+        areaList.value.forEach(area => {
+            area.children.forEach(prov => {
+                if (selectedIds.has(prov.id)) {
+                    prov.mySelected = 1;
+                }
+            });
         });
-
-        // Restore selection if passed (simplified restoration logic)
-        // If existingSelection is passed, we would need to map it back to mySelected=1
-        if (existingSelection && Array.isArray(existingSelection)) {
-             // Logic to restore selection would go here
-             // For now, let's just clear
-        }
-        
-        loading.value = false;
-    }, 300);
+    }
+    
+    loading.value = false;
 };
 
 const handleCancel = () => {
@@ -139,12 +112,11 @@ const handleCancel = () => {
 
 // Computed Properties
 const isPopoverDisabled = computed(() => (item) => {
-    // Simplified: disabled if all children are 'otherSelected' (not applicable in this standalone version usually)
-    return item.childInfo.length === 0;
+    return true; // Always disabled as no cities
 });
 
 const isShowAll = computed(() => (item) => {
-    return item.childInfo.length > 0 && item.childInfo.every((i) => i.mySelected == 1);
+    return false; // No cities to show all
 });
 
 const isIndeterminateShow = computed(() => (dq) => {
@@ -154,9 +126,7 @@ const isIndeterminateShow = computed(() => (dq) => {
 });
 
 const isIndeterCityShow = computed(() => (item, type) => {
-    let len = item.childInfo.length;
-    let length = item.childInfo.filter((i) => i[type] == 1).length;
-    return length > 0 && length < len;
+    return false;
 });
 
 // Event Handlers
@@ -164,46 +134,38 @@ const handleDqChange = (value, dq) => {
     const val = value ? 1 : 0;
     dq.children.forEach((item) => {
         item.mySelected = val;
-        item.childInfo.forEach((i) => {
-            i.mySelected = val;
-        });
     });
 };
 
 const handleParentChange = (value, item) => {
-    const val = value ? 1 : 0;
-    item.childInfo.forEach((i) => {
-        i.mySelected = val;
-    });
+    // value is boolean from checkbox
+    // item.mySelected is bound to v-model, so it updates automatically? 
+    // Wait, v-model="item.mySelected" on checkbox updates it.
+    // But we also need to handle 'change' event if we want extra logic.
+    // The template uses: @change="(value) => handleParentChange(value, item)"
+    // and v-model="item.mySelected".
+    // Element Plus checkbox change emits the new value.
+    // We don't need to manually set item.mySelected if v-model is used, 
+    // but the original code did manual updates for children.
+    // Since we have no children (cities), we might just rely on v-model.
+    // However, keeping the handler for consistency.
+    item.mySelected = value ? 1 : 0;
 };
 
 const handleChildChange = (value, item, child) => {
-    let checked = item.childInfo.some((i) => i.mySelected == 1);
-    item.mySelected = checked ? 1 : 0;
+    // No children
 };
 
 const handleSubmit = () => {
-    // Collect selected regions
+    // Collect selected provinces
     const selected = [];
     areaList.value.forEach(dq => {
         dq.children.forEach(prov => {
             if (prov.mySelected === 1) {
-                // If all cities selected, just push province name
-                if (isShowAll.value(prov)) {
-                     selected.push(prov.district);
-                } else {
-                    // Else push selected cities
-                    const cities = prov.childInfo.filter(c => c.mySelected === 1).map(c => c.district);
-                    if (cities.length > 0) {
-                        selected.push(`${prov.district}(${cities.join(',')})`);
-                    }
-                }
-            } else {
-                 // Check partial selection
-                 const cities = prov.childInfo.filter(c => c.mySelected === 1).map(c => c.district);
-                 if (cities.length > 0) {
-                      selected.push(`${prov.district}(${cities.join(',')})`);
-                 }
+                selected.push({
+                    provinceId: prov.id,
+                    provinceName: prov.district
+                });
             }
         });
     });

+ 141 - 111
src/views/mallLogistics/feeTemplate/components/edit-dialog.vue

@@ -1,51 +1,50 @@
 <template>
-    <ele-modal
-        :width="1000"
-        v-model="visible"
-        :title="title"
-        @open="handleOpen"
-        :body-style="{ paddingBottom: '20px' }"
-    >
+    <ele-modal :width="1000" v-model="visible" :title="title"
+        :body-style="{ paddingBottom: '20px' }">
         <el-form :model="form" ref="formRef" :rules="rules" label-width="100px">
-            <el-form-item label="模板名称" prop="name">
-                <el-input v-model="form.name" placeholder="请输入模板名称" />
-            </el-form-item>
-            <el-form-item label="配送方式" prop="deliveryMethod">
-                <el-radio-group v-model="form.deliveryMethod">
-                    <el-radio :label="1">快递</el-radio>
-                </el-radio-group>
+            <el-form-item label="模板名称" prop="templeteName">
+                <el-input v-model="form.templeteName" placeholder="请输入模板名称" />
             </el-form-item>
 
             <!-- Table for Rules -->
             <div class="px-4">
-                <el-table :data="form.rules" border style="width: 100%">
+                <el-table :data="form.shippingTempleteDetailList" border style="width: 100%">
                     <el-table-column label="选择地区" min-width="250">
                         <template #default="{ row, $index }">
                             <div class="flex items-center justify-between">
-                                <span v-if="!row.regions || row.regions.length === 0" class="text-gray-400">未选择地区</span>
-                                <span v-else class="truncate pr-2" :title="row.regions.join(',')">{{ row.regions.join('、') }}</span>
+                                <span v-if="!row.shippingProvinceList || row.shippingProvinceList.length === 0"
+                                    class="text-gray-400">未选择地区</span>
+                                <span v-else class="truncate pr-2"
+                                    :title="row.shippingProvinceList.map(p => p.provinceName).join('、')">
+                                    {{row.shippingProvinceList.map(p => p.provinceName).join('、')}}
+                                </span>
                                 <el-button type="primary" link @click="openAreaSelector($index)">编辑</el-button>
                             </div>
                         </template>
                     </el-table-column>
                     <el-table-column label="设置包邮条件" width="220">
-                         <template #default="{ row }">
+                        <template #default="{ row }">
                             <div class="flex items-center">
                                 <span class="mr-2">满</span>
-                                <el-input-number v-model="row.freeAmount" :min="0" :precision="2" :controls="false" style="width: 80px" />
+                                <el-input-number v-model="row.minMoney" :min="0" :precision="2" :controls="false"
+                                    style="width: 80px" />
                                 <span class="ml-2">元包邮</span>
                             </div>
                         </template>
                     </el-table-column>
                     <el-table-column label="基础运费" width="150">
                         <template #default="{ row }">
-                            <el-input-number v-model="row.baseFee" :min="0" :precision="2" :controls="false" style="width: 100%" />
+                            <el-input-number v-model="row.shippingMoney" :min="0" :precision="2" :controls="false"
+                                style="width: 100%" />
                         </template>
                     </el-table-column>
                     <el-table-column label="操作" width="80" align="center">
                         <template #default="{ $index }">
-                            <el-button type="danger" link @click="removeRule($index)" :disabled="$index === 0 && form.rules.length === 1">
-                                <el-icon><Delete /></el-icon>
+                            <el-button type="danger" link @click="removeRule($index)"
+                                :disabled="$index === 0 && form.shippingTempleteDetailList.length === 1">
+                                <el-icon>
+                                    <Delete />
+                                </el-icon>
                             </el-button>
                         </template>
                     </el-table-column>
@@ -54,8 +53,6 @@
                     <el-button type="primary" link :icon="Plus" @click="addRule">添加规则</el-button>
                 </div>
             </div>
-            
-            <!-- Default Rule Tip or separate section? Figure 2 shows rows being added. -->
         </el-form>
 
         <template #footer>
@@ -68,99 +65,132 @@
 </template>
 
 <script setup>
-import { ref, reactive, nextTick, defineEmits, defineExpose } from 'vue';
-import { Delete, Plus } from '@element-plus/icons-vue';
-import { ElMessage } from 'element-plus';
-import AreaSelector from './area-selector.vue';
-
-const visible = defineModel({ type: Boolean });
-const emit = defineEmits(['success']);
-
-const title = ref('新增运费模板');
-const formRef = ref(null);
-const areaSelectorRef = ref(null);
-const areaSelectorVisible = ref(false);
-const currentRuleIndex = ref(-1);
-
-const form = reactive({
-    id: undefined,
-    name: '',
-    deliveryMethod: 1,
-    rules: [
-        { regions: ['全国'], freeAmount: 0, baseFee: 0 } // Default rule
-    ]
-});
-
-const rules = {
-    name: [{ required: true, message: '请输入模板名称', trigger: 'blur' }],
-    deliveryMethod: [{ required: true, message: '请选择配送方式', trigger: 'change' }]
-};
-
-const handleOpen = (row) => {
-    visible.value = true;
-    nextTick(() => {
-        formRef.value?.resetFields();
-        if (row && row.id) {
-            title.value = '编辑运费模板';
-            Object.assign(form, JSON.parse(JSON.stringify(row)));
-        } else {
-            title.value = '新增运费模板';
-            form.id = undefined;
-            form.name = '';
-            form.deliveryMethod = 1;
-            form.rules = [{ regions: [], freeAmount: undefined, baseFee: 5 }];
-        }
-    });
-};
+    import { ref, reactive, nextTick, defineEmits, defineExpose } from 'vue';
+    import { Delete, Plus } from '@element-plus/icons-vue';
+    import { ElMessage } from 'element-plus';
+    import request from '@/utils/request';
+    import AreaSelector from './area-selector.vue';
 
-const addRule = () => {
-    form.rules.push({ regions: [], freeAmount: undefined, baseFee: 5 });
-};
+    const visible = defineModel({ type: Boolean });
+    const emit = defineEmits(['success']);
 
-const removeRule = (index) => {
-    form.rules.splice(index, 1);
-};
+    const title = ref('新增运费模板');
+    const formRef = ref(null);
+    const areaSelectorRef = ref(null);
+    const areaSelectorVisible = ref(false);
+    const currentRuleIndex = ref(-1);
 
-const openAreaSelector = (index) => {
-    currentRuleIndex.value = index;
-    areaSelectorVisible.value = true;
-    nextTick(() => {
-        areaSelectorRef.value?.handleOpen(form.rules[index].regions);
+    const form = reactive({
+        id: undefined,
+        templeteName: '',
+        shippingTempleteDetailList: [
+            {
+                tempKey: '',
+                shippingProvinceList: [],
+                minMoney: 0,
+                shippingMoney: 0
+            }
+        ]
     });
-};
-
-const handleAreaConfirm = (selectedRegions) => {
-    if (currentRuleIndex.value !== -1) {
-        form.rules[currentRuleIndex.value].regions = selectedRegions;
-    }
-};
-
-const submit = () => {
-    formRef.value.validate((valid) => {
-        if (valid) {
-            // Validate rules
-            for (let i = 0; i < form.rules.length; i++) {
-                const rule = form.rules[i];
-                if (!rule.regions || rule.regions.length === 0) {
-                    ElMessage.warning(`第 ${i + 1} 行未选择地区`);
-                    return;
+
+    const rules = {
+        templeteName: [{ required: true, message: '请输入模板名称', trigger: 'blur' }]
+    };
+
+    const generateTempKey = () => {
+        return 'tk_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
+    };
+
+    const handleOpen = (row) => {
+        visible.value = true;
+        nextTick(() => {
+            formRef.value?.resetFields();
+            if (row && row.id) {
+                title.value = '编辑运费模板';
+                form.id = row.id;
+                form.templeteName = row.templeteName;
+                // Map templeteList to shippingTempleteDetailList
+                form.shippingTempleteDetailList = (row.templeteList || []).map(item => ({
+                    tempKey: item.tempKey || generateTempKey(),
+                    minMoney: item.minMoney,
+                    shippingMoney: item.shippingMoney,
+                    shippingProvinceList: item.shippingProvinceList || []
+                }));
+            } else {
+                title.value = '新增运费模板';
+                form.id = undefined;
+                form.templeteName = '';
+                form.shippingTempleteDetailList = [{
+                    tempKey: generateTempKey(),
+                    shippingProvinceList: [],
+                    minMoney: 0,
+                    shippingMoney: 0
+                }];
+            }
+        });
+    };
+
+    const addRule = () => {
+        form.shippingTempleteDetailList.push({
+            tempKey: generateTempKey(),
+            shippingProvinceList: [],
+            minMoney: 0,
+            shippingMoney: 0
+        });
+    };
+
+    const removeRule = (index) => {
+        form.shippingTempleteDetailList.splice(index, 1);
+    };
+
+    const openAreaSelector = (index) => {
+        currentRuleIndex.value = index;
+        areaSelectorVisible.value = true;
+        nextTick(() => {
+            areaSelectorRef.value?.handleOpen(form.shippingTempleteDetailList[index].shippingProvinceList);
+        });
+    };
+
+    const handleAreaConfirm = (selectedRegions) => {
+        if (currentRuleIndex.value !== -1) {
+            form.shippingTempleteDetailList[currentRuleIndex.value].shippingProvinceList = selectedRegions;
+        }
+    };
+
+    const submit = () => {
+        formRef.value.validate(async (valid) => {
+            if (valid) {
+                // Validate rules
+                for (let i = 0; i < form.shippingTempleteDetailList.length; i++) {
+                    const rule = form.shippingTempleteDetailList[i];
+                    if (!rule.shippingProvinceList || rule.shippingProvinceList.length === 0) {
+                        ElMessage.warning(`第 ${i + 1} 行未选择地区`);
+                        return;
+                    }
+                    if (rule.shippingMoney === undefined || rule.shippingMoney === null) {
+                        ElMessage.warning(`第 ${i + 1} 行未设置基础运费`);
+                        return;
+                    }
                 }
-                if (rule.baseFee === undefined || rule.baseFee === null) {
-                     ElMessage.warning(`第 ${i + 1} 行未设置基础运费`);
-                     return;
+
+                try {
+                    const res = await request.post('/shipping/templete/add', form);
+                    if (res.data.code === 200) {
+                        ElMessage.success('保存成功');
+                        emit('success');
+                        visible.value = false;
+                    } else {
+                        ElMessage.error(res.data.msg || '保存失败');
+                    }
+                } catch (error) {
+                    console.error(error);
+                    ElMessage.error('保存失败');
                 }
             }
-            
-            // Mock submission
-            console.log('Submitting:', form);
-            ElMessage.success('保存成功');
-            emit('success', { ...form, id: form.id || Date.now() }); // Simulate returning saved data
-            visible.value = false;
-        }
-    });
-};
+        });
+    };
 
-defineExpose({
-    handleOpen
-});
+    defineExpose({
+        handleOpen
+    });
 </script>

+ 123 - 88
src/views/mallLogistics/feeTemplate/index.vue

@@ -5,43 +5,62 @@
                 <el-button type="success" :icon="Plus" @click="handleAdd">添加运费模板</el-button>
             </div>
 
-            <div v-if="templateList.length === 0" class="text-center py-10 text-gray-400">
-                暂无运费模板
-            </div>
+            <div v-loading="loading">
+                <div v-if="templateList.length === 0" class="text-center py-10 text-gray-400">
+                    暂无运费模板
+                </div>
 
-            <div v-for="(tpl, index) in templateList" :key="tpl.id"
-                class="mb-6 border border-gray-200 rounded-lg shadow-sm overflow-hidden bg-white">
-                <!-- Template Header -->
-                <div class="bg-green-50 px-4 py-3 flex justify-between items-center border-b border-gray-200">
-                    <span class="font-bold text-gray-700">{{ tpl.name }}</span>
-                    <div class="space-x-4 text-sm">
-                        <el-button type="primary" link @click="handleCopy(tpl)">复制模板</el-button>
-                        <el-button type="primary" link @click="handleEdit(tpl)">编辑</el-button>
-                        <el-button type="danger" link @click="handleDelete(index)">删除</el-button>
+                <div v-for="(tpl, index) in templateList" :key="tpl.id"
+                    class="mb-6 border border-gray-200 rounded-lg shadow-sm overflow-hidden bg-white">
+                    <!-- Template Header -->
+                    <div class="bg-green-50 px-4 py-3 flex justify-between items-center border-b border-gray-200">
+                        <div class="flex items-center space-x-4">
+                            <span class="font-bold text-gray-700">{{ tpl.templeteName }}</span>
+                            <el-tag v-if="tpl.useStatus === 1" type="success" size="small">使用中</el-tag>
+                        </div>
+                        <div class="space-x-4 text-sm flex items-center">
+                            <span class="text-gray-400 text-xs">更新时间: {{ tpl.updateTime }}</span>
+                            <el-button v-if="tpl.useStatus === 0" type="success" link @click="handleEnable(tpl)">启用</el-button>
+                            <!-- <el-button type="primary" link @click="handleCopy(tpl)">复制模板</el-button> -->
+                            <el-button type="primary" link @click="handleEdit(tpl)">编辑</el-button>
+                            <!-- <el-button type="danger" link @click="handleDelete(index)">删除</el-button> -->
+                        </div>
                     </div>
-                </div>
 
-                <!-- Rules Table -->
-                <el-table :data="tpl.rules" style="width: 100%" :show-header="true">
-                    <el-table-column prop="regions" label="运送范围" min-width="300">
-                        <template #default="{ row }">
-                            <span v-if="!row.regions || row.regions.length === 0">未指定地区</span>
-                            <span v-else>{{ row.regions.join('、') }}</span>
-                        </template>
-                    </el-table-column>
-                    <el-table-column label="计费规则" width="200" align="center">
-                        <template #default="{ row }">
-                            <span v-if="row.freeAmount > 0">满{{ row.freeAmount }}元包邮</span>
-                            <span v-else>不支持配送/默认</span>
-                        </template>
-                    </el-table-column>
-                    <el-table-column prop="baseFee" label="基础运费" width="150" align="center" />
-                    <el-table-column label="配送方式" width="150" align="center">
-                        <template #default>
-                            {{ tpl.deliveryMethod === 1 ? '快递' : '其他' }}
-                        </template>
-                    </el-table-column>
-                </el-table>
+                    <!-- Rules Table -->
+                    <el-table :data="tpl.templeteList" style="width: 100%" :show-header="true">
+                        <el-table-column prop="shippingProvinceList" label="运送范围" min-width="300">
+                            <template #default="{ row }">
+                                <span v-if="!row.shippingProvinceList || row.shippingProvinceList.length === 0">未指定地区</span>
+                                <span v-else>{{ row.shippingProvinceList.map(p => p.provinceName).join('、') }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="计费规则" width="200" align="center">
+                            <template #default="{ row }">
+                                <span v-if="row.minMoney > 0">满{{ row.minMoney }}元包邮</span>
+                                <span v-else>默认</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="shippingMoney" label="基础运费" width="150" align="center" />
+                        <el-table-column label="配送方式" width="150" align="center">
+                            <template #default>
+                                快递
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </div>
+            </div>
+            
+            <div class="flex justify-end mt-4">
+                <el-pagination
+                    v-model:current-page="pageParams.page"
+                    v-model:page-size="pageParams.limit"
+                    :total="total"
+                    :page-sizes="[10, 20, 50, 100]"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    @size-change="handleSizeChange"
+                    @current-change="handleCurrentChange"
+                />
             </div>
 
             <EditDialog ref="editDialogRef" @success="handleSaveSuccess" />
@@ -50,43 +69,47 @@
 </template>
 
 <script setup>
-import { ref, reactive } from 'vue';
+import { ref, reactive, onMounted } from 'vue';
 import { Plus } from '@element-plus/icons-vue';
 import { ElMessageBox, ElMessage } from 'element-plus';
+import request from '@/utils/request';
 import EditDialog from './components/edit-dialog.vue';
 
 const editDialogRef = ref(null);
+const loading = ref(false);
+const templateList = ref([]);
+const total = ref(0);
+const pageParams = reactive({
+    page: 1,
+    limit: 10
+});
 
-// Mock Data
-const templateList = ref([
-    {
-        id: 1,
-        name: '运费模板名称',
-        deliveryMethod: 1,
-        rules: [
-            {
-                regions: ['江苏', '浙江', '安徽', '江西', '北京', '天津', '山西', '山东', '河北', '湖南', '湖北', '河南', '广东', '广西', '福建', '辽宁', '吉林', '黑龙江', '陕西', '甘肃', '宁夏', '重庆', '云南', '贵州', '四川'],
-                freeAmount: 5.8,
-                baseFee: 5
-            },
-            {
-                regions: ['新疆', '西藏', '香港', '澳门', '台湾', '海外'],
-                freeAmount: 0, // Assuming 0 means not supported or default fee
-                baseFee: 5
-            },
-            {
-                regions: ['上海', '内蒙古', '青海'],
-                freeAmount: 6.8,
-                baseFee: 5
-            },
-            {
-                regions: ['海南'],
-                freeAmount: 9.8,
-                baseFee: 5
-            }
-        ]
+const getList = async () => {
+    loading.value = true;
+    try {
+        const res = await request.get('/shipping/templete/pagelist', { params: pageParams });
+        if (res.data.code === 200) { // Assuming standard structure based on description (rows, total) but usually wrapped in data
+             // Check if response is directly the object or inside data
+             // API desc says: Response -> { total: 0, rows: [...] }
+             // Axios usually returns { data: { ...API_RESPONSE... } }
+             // So res.data should be the object.
+             const data = res.data; 
+             // However, sometimes it is res.data.data if wrapped.
+             // The screenshot shows "返回响应 200 ... rows array". It looks like the root object has rows.
+             // But standard practice in this project (based on previous files) often checks res.data.code.
+             // If res.data has code, then rows might be in res.data.rows or res.data.data.rows.
+             // The screenshot shows `code` and `rows` at the same level.
+             // So res.data.rows.
+             templateList.value = data.rows || [];
+             total.value = data.total || 0;
+        }
+    } catch (error) {
+        console.error(error);
+        ElMessage.error('获取列表失败');
+    } finally {
+        loading.value = false;
     }
-]);
+};
 
 const handleAdd = () => {
     editDialogRef.value?.handleOpen();
@@ -96,33 +119,45 @@ const handleEdit = (tpl) => {
     editDialogRef.value?.handleOpen(tpl);
 };
 
-const handleCopy = (tpl) => {
-    const newTpl = JSON.parse(JSON.stringify(tpl));
-    newTpl.id = Date.now();
-    newTpl.name = newTpl.name + ' (副本)';
-    templateList.value.unshift(newTpl);
-    ElMessage.success('复制成功');
+// Copy and Delete not implemented in API description provided
+// const handleCopy = (tpl) => { ... };
+// const handleDelete = (index) => { ... };
+
+const handleEnable = async (tpl) => {
+    try {
+        await ElMessageBox.confirm(`确定启用"${tpl.templeteName}"吗?`, '提示', {
+            type: 'warning'
+        });
+        const res = await request.post('/shipping/templete/enableShippingTemplete', { id: tpl.id });
+        if (res.data.code === 200) {
+            ElMessage.success('启用成功');
+            getList();
+        } else {
+            ElMessage.error(res.data.msg || '启用失败');
+        }
+    } catch (error) {
+        if (error !== 'cancel') {
+             console.error(error);
+             // ElMessage.error('操作失败');
+        }
+    }
 };
 
-const handleDelete = (index) => {
-    ElMessageBox.confirm('确定删除该运费模板吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-    }).then(() => {
-        templateList.value.splice(index, 1);
-        ElMessage.success('删除成功');
-    });
+const handleSaveSuccess = () => {
+    getList();
 };
 
-const handleSaveSuccess = (data) => {
-    const index = templateList.value.findIndex(item => item.id === data.id);
-    if (index !== -1) {
-        // Update
-        templateList.value[index] = data;
-    } else {
-        // Add
-        templateList.value.unshift(data);
-    }
+const handleSizeChange = (val) => {
+    pageParams.limit = val;
+    getList();
 };
+
+const handleCurrentChange = (val) => {
+    pageParams.page = val;
+    getList();
+};
+
+onMounted(() => {
+    getList();
+});
 </script>