Răsfoiți Sursa

feat(工单): 添加运单号列并优化搜索组件

refactor(订单详情): 调整标签和说明的布局样式

feat(退款): 增加快递拦截功能并优化状态显示

style(商品价格编辑): 调整表单布局和代码格式

style(用户卡片): 优化头像上传组件布局

refactor(订单商品列表): 调整表格列宽和样式

feat(退款详情): 增加拦截状态显示和操作按钮

style(默认图片管理): 优化图片上传和显示布局

refactor(退款项): 调整状态颜色和布局样式
ylong 2 săptămâni în urmă
părinte
comite
197f11c0f4

+ 51 - 45
src/views/data/defaultImage/index.vue

@@ -11,56 +11,62 @@
                 >
 
                 <el-checkbox-group
-                style="height: calc(100vh - 270px)"
-                v-model="checkList"
-                class="flex flex-wrap items-start content-start overflow-auto"
-                @change="handlecheckboxChange"
-            >
-                <el-upload
-                    ref="uploadRef"
-                    v-model:file-list="form.images"
-                    list-type="picture-card"
-                    :show-file-list="false"
-                    multiple
-                    accept="image/*"
-                    :http-request="uploadFile"
-                    style="margin-right: 14px; margin-top: 12px"
+                    style="height: calc(100vh - 270px)"
+                    v-model="checkList"
+                    class="flex flex-wrap items-start content-start overflow-auto"
+                    @change="handlecheckboxChange"
                 >
-                    <div class="flex flex-col items-center">
-                        <el-icon :size="24"><Plus /></el-icon>
-                        <el-text type="info" style="margin-top: 20px"
-                            >上传图片</el-text
-                        >
-                    </div>
-                </el-upload>
-                <div
-                    class="pos-relative default-image-item"
-                    v-for="item in imageList"
-                >
-                    <el-checkbox :value="item.id" class="checkbox-item" />
-                    <el-image
-                        style="width: 146px; height: 146px; border-radius: 6px"
-                        :src="item.imgUrl"
-                        fit="cover"
-                    />
-
+                    <el-upload
+                        ref="uploadRef"
+                        v-model:file-list="form.images"
+                        list-type="picture-card"
+                        :show-file-list="false"
+                        multiple
+                        accept="image/*"
+                        :http-request="uploadFile"
+                        style="margin-right: 14px; margin-top: 12px"
+                    >
+                        <div class="flex flex-col items-center">
+                            <el-icon :size="24"><Plus /></el-icon>
+                            <el-text type="info" style="margin-top: 20px"
+                                >上传图片</el-text
+                            >
+                        </div>
+                    </el-upload>
                     <div
-                        class="mask-error flex items-center justify-center"
-                        v-if="item.defaultUse == 1"
+                        class="pos-relative default-image-item"
+                        v-for="item in imageList"
                     >
-                        <el-text type="primary" size="small">默认图片</el-text>
+                        <el-checkbox :value="item.id" class="checkbox-item" />
+                        <el-image
+                            style="
+                                width: 146px;
+                                height: 146px;
+                                border-radius: 6px;
+                            "
+                            :src="item.imgUrl"
+                            fit="cover"
+                        />
+
+                        <div
+                            class="mask-error flex items-center justify-center"
+                            v-if="item.defaultUse == 1"
+                        >
+                            <el-text type="primary" size="small"
+                                >默认图片</el-text
+                            >
+                        </div>
                     </div>
-                </div>
-            </el-checkbox-group>
+                </el-checkbox-group>
 
-            <ele-pagination
-                v-model:current-page="pageParams.pageNum"
-                v-model:page-size="pageParams.pageSize"
-                :total="total"
-                layout="total,prev, pager, next, sizes, jumper"
-                @update:currentPage="handleCurrentChange"
-                @update:pageSize="handlePageSizeChange"
-            />
+                <ele-pagination
+                    v-model:current-page="pageParams.pageNum"
+                    v-model:page-size="pageParams.pageSize"
+                    :total="total"
+                    layout="total,prev, pager, next, sizes, jumper"
+                    @update:currentPage="handleCurrentChange"
+                    @update:pageSize="handlePageSizeChange"
+                />
             </div>
         </ele-card>
     </ele-page>

+ 71 - 59
src/views/goods/list/components/edit-price-modal.vue

@@ -1,5 +1,10 @@
 <template>
-    <ele-modal :width="500" title="编辑价格" v-model="visible" @closed="handleClosed">
+    <ele-modal
+        :width="500"
+        title="编辑价格"
+        v-model="visible"
+        @closed="handleClosed"
+    >
         <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
             <el-form-item label="商品标题">
                 <span>{{ rowData?.bookName || '-' }}</span>
@@ -8,80 +13,87 @@
                 <span>{{ rowData?.isbn || '-' }}</span>
             </el-form-item>
             <el-form-item label="一般品售价" prop="productPrice">
-                <el-input-number v-model="form.productPrice" :min="0" :precision="2" :step="0.1" style="width: 100%" />
+                <el-input-number
+                    v-model="form.productPrice"
+                    :min="0"
+                    :precision="2"
+                    :step="0.1"
+                    style="width: 100%"
+                />
             </el-form-item>
         </el-form>
 
         <template #footer>
             <el-button @click="visible = false">取消</el-button>
-            <el-button type="primary" @click="handleSubmit" :loading="loading">提交</el-button>
+            <el-button type="primary" @click="handleSubmit" :loading="loading"
+                >提交</el-button
+            >
         </template>
     </ele-modal>
 </template>
 
 <script setup>
-import { ref, reactive } from 'vue';
-import { EleMessage } from 'ele-admin-plus/es';
-import request from '@/utils/request';
+    import { ref, reactive } from 'vue';
+    import { EleMessage } from 'ele-admin-plus/es';
+    import request from '@/utils/request';
 
-const emit = defineEmits(['done']);
-// const visible = defineModel({ type: Boolean });
-const visible = ref(false);
-const loading = ref(false);
-const rowData = ref({});
-const formRef = ref(null);
+    const emit = defineEmits(['done']);
+    // const visible = defineModel({ type: Boolean });
+    const visible = ref(false);
+    const loading = ref(false);
+    const rowData = ref({});
+    const formRef = ref(null);
 
-const form = reactive({
-    productPrice: 0
-});
-
-const rules = {
-    productPrice: [
-        { required: true, message: '请输入售价', trigger: 'blur' }
-    ]
-};
-
-// 打开弹窗
-const open = (row) => {
-    rowData.value = row;
-    form.productPrice = Number(row.productPrice) || 0;
-    visible.value = true;
-};
-
-const handleSubmit = () => {
-    formRef.value?.validate(async (valid) => {
-        if (!valid) return;
+    const form = reactive({
+        productPrice: 0
+    });
 
-        loading.value = true;
-        try {
-            request.post('/shop/shopbook/setInfo', {
-                isbn: rowData.value.isbn,
-                productPrice: form.productPrice
-            }).then((res) => {
-                if (res.data.code === 200) {
-                    EleMessage.success('价格更新成功');
-                    visible.value = false;
-                    emit('done');
-                } else {
-                    EleMessage.error(res.data.msg || '更新失败');
-                }
-            });
+    const rules = {
+        productPrice: [
+            { required: true, message: '请输入售价', trigger: 'blur' }
+        ]
+    };
 
+    // 打开弹窗
+    const open = (row) => {
+        rowData.value = row;
+        form.productPrice = Number(row.productPrice) || 0;
+        visible.value = true;
+    };
 
+    const handleSubmit = () => {
+        formRef.value?.validate(async (valid) => {
+            if (!valid) return;
 
-        } catch (e) {
-            EleMessage.error(e.message || '更新失败');
-        } finally {
-            loading.value = false;
-        }
-    });
-};
+            loading.value = true;
+            try {
+                request
+                    .post('/shop/shopbook/setInfo', {
+                        isbn: rowData.value.isbn,
+                        productPrice: form.productPrice
+                    })
+                    .then((res) => {
+                        if (res.data.code === 200) {
+                            EleMessage.success('价格更新成功');
+                            visible.value = false;
+                            emit('done');
+                        } else {
+                            EleMessage.error(res.data.msg || '更新失败');
+                        }
+                    });
+            } catch (e) {
+                EleMessage.error(e.message || '更新失败');
+            } finally {
+                loading.value = false;
+            }
+        });
+    };
 
-const handleClosed = () => {
-    rowData.value = {};
-    form.productPrice = 0;
-    formRef.value?.resetFields();
-};
+    const handleClosed = () => {
+        rowData.value = {};
+        form.productPrice = 0;
+        formRef.value?.resetFields();
+    };
 
-defineExpose({ open });
+    defineExpose({ open });
 </script>

+ 36 - 6
src/views/mallOrder/all/components/order-base-info.vue

@@ -139,23 +139,53 @@
                                     >查看</el-button
                                 >
                             </template>
-                            <el-timeline style="max-height: 400px; overflow-y: auto;" v-if="detail.trackingVoList && detail.trackingVoList.length > 0">
+                            <el-timeline
+                                style="max-height: 400px; overflow-y: auto"
+                                v-if="
+                                    detail.trackingVoList &&
+                                    detail.trackingVoList.length > 0
+                                "
+                            >
                                 <el-timeline-item
-                                    v-for="(activity, index) in detail.trackingVoList"
+                                    v-for="(
+                                        activity, index
+                                    ) in detail.trackingVoList"
                                     :key="index"
                                     :timestamp="activity.ftime"
                                     :type="index === 0 ? 'success' : ''"
                                     :color="index === 0 ? '#0bbd87' : ''"
                                 >
                                     <div>
-                                        <div style="margin-bottom: 5px;" v-if="activity.status">
-                                            <el-tag size="small" :type="index === 0 ? 'success' : 'info'">{{ activity.status }}</el-tag>
+                                        <div
+                                            style="margin-bottom: 5px"
+                                            v-if="activity.status"
+                                        >
+                                            <el-tag
+                                                size="small"
+                                                :type="
+                                                    index === 0
+                                                        ? 'success'
+                                                        : 'info'
+                                                "
+                                                >{{ activity.status }}</el-tag
+                                            >
                                         </div>
-                                        <div style="color: #606266; font-size: 13px; line-height: 1.5;">{{ activity.context }}</div>
+                                        <div
+                                            style="
+                                                color: #606266;
+                                                font-size: 13px;
+                                                line-height: 1.5;
+                                            "
+                                            >{{ activity.context }}</div
+                                        >
                                     </div>
                                 </el-timeline-item>
                             </el-timeline>
-                            <el-empty v-else description="暂无物流信息" :image-size="60" />
+                            <el-empty
+                                v-else
+                                description="暂无物流信息"
+                                :image-size="60"
+                            />
                         </el-popover>
                     </div>
 

+ 120 - 105
src/views/mallOrder/all/components/order-product-list.vue

@@ -4,125 +4,140 @@
             <div>
                 <el-table :data="detail.detailList" border style="width: 100%">
                     <!-- Image -->
-                <el-table-column label="图片" width="90" align="center">
-                    <template #default="{ row }">
-                        <el-image
-                            :src="row.cover"
-                            style="width: 60px; height: 60px"
-                            fit="cover"
-                            :preview-src-list="[row.cover]"
-                            preview-teleported
-                        />
-                    </template>
-                </el-table-column>
+                    <el-table-column label="图片" width="90" align="center">
+                        <template #default="{ row }">
+                            <el-image
+                                :src="row.cover"
+                                style="width: 60px; height: 60px"
+                                fit="cover"
+                                :preview-src-list="[row.cover]"
+                                preview-teleported
+                            />
+                        </template>
+                    </el-table-column>
 
-                <!-- Product Info -->
-                <el-table-column label="商品信息" min-width="250">
-                    <template #default="{ row }">
-                        <div class="product-info">
-                            <div class="title">{{ row.bookName }}</div>
-                            <div class="meta">ISBN: {{ row.isbn }}</div>
-                            <div class="tags">
-                                <span class="discount-tag"
-                                    >回收折扣:
-                                    {{ row.recycleDiscount || 1 }}折</span
-                                >
-                                <span class="status-tag"
-                                    >(回收状态:
-                                    {{
-                                        getRecycleStatusText(row.recycleStatus)
-                                    }})</span
-                                >
+                    <!-- Product Info -->
+                    <el-table-column label="商品信息" min-width="250">
+                        <template #default="{ row }">
+                            <div class="product-info">
+                                <div class="title">{{ row.bookName }}</div>
+                                <div class="meta">ISBN: {{ row.isbn }}</div>
+                                <div class="tags">
+                                    <span class="discount-tag"
+                                        >回收折扣:
+                                        {{ row.recycleDiscount || 1 }}折</span
+                                    >
+                                    <span class="status-tag"
+                                        >(回收状态:
+                                        {{
+                                            getRecycleStatusText(
+                                                row.recycleStatus
+                                            )
+                                        }})</span
+                                    >
+                                </div>
                             </div>
-                        </div>
-                    </template>
-                </el-table-column>
+                        </template>
+                    </el-table-column>
 
-                <!-- Specs -->
-                <el-table-column label="规格" width="100" align="center">
-                    <template #default="{ row }">
-                        <span>({{ getConditionText(row.conditionType) }})</span>
-                    </template>
-                </el-table-column>
+                    <!-- Specs -->
+                    <el-table-column label="规格" width="100" align="center">
+                        <template #default="{ row }">
+                            <span
+                                >({{
+                                    getConditionText(row.conditionType)
+                                }})</span
+                            >
+                        </template>
+                    </el-table-column>
 
-                <!-- Status -->
-                <el-table-column label="状态" width="120" align="center">
-                    <template #default="{ row }">
-                        <span :class="getStatusColor(row.status)">{{
-                            getDetailStatusText(row.status)
-                        }}</span>
-                    </template>
-                </el-table-column>
+                    <!-- Status -->
+                    <el-table-column label="状态" width="120" align="center">
+                        <template #default="{ row }">
+                            <span :class="getStatusColor(row.status)">{{
+                                getDetailStatusText(row.status)
+                            }}</span>
+                        </template>
+                    </el-table-column>
 
-                <!-- Quantity -->
-                <el-table-column label="数量" width="80" align="center">
-                    <template #default="{ row }">
-                        <span>x{{ row.num }}</span>
-                    </template>
-                </el-table-column>
+                    <!-- Quantity -->
+                    <el-table-column label="数量" width="80" align="center">
+                        <template #default="{ row }">
+                            <span>x{{ row.num }}</span>
+                        </template>
+                    </el-table-column>
 
-                <!-- Unit Price -->
-                <el-table-column label="单价" width="100" align="center">
-                    <template #default="{ row }">
-                        <span>¥ {{ row.price }}</span>
-                    </template>
-                </el-table-column>
+                    <!-- Unit Price -->
+                    <el-table-column label="单价" width="100" align="center">
+                        <template #default="{ row }">
+                            <span>¥ {{ row.price }}</span>
+                        </template>
+                    </el-table-column>
 
-                <!-- Discount -->
-                <el-table-column label="优惠" width="120" align="center">
-                    <template #default="{ row }">
-                        <div v-if="row.discountMoney > 0">
-                            <div style="color: red"
-                                >- ¥{{ row.discountMoney }}</div
-                            >
-                        </div>
-                        <div v-else>¥ 0</div>
-                    </template>
-                </el-table-column>
+                    <!-- Discount -->
+                    <el-table-column label="优惠" width="120" align="center">
+                        <template #default="{ row }">
+                            <div v-if="row.discountMoney > 0">
+                                <div style="color: red"
+                                    >- ¥{{ row.discountMoney }}</div
+                                >
+                            </div>
+                            <div v-else>¥ 0</div>
+                        </template>
+                    </el-table-column>
 
-                <!-- Actual Payment -->
-                <el-table-column label="实收款" width="100" align="center">
-                    <template #default="{ row }">
-                        <span>¥ {{ row.payMoney }}</span>
-                    </template>
-                </el-table-column>
+                    <!-- Actual Payment -->
+                    <el-table-column label="实收款" width="100" align="center">
+                        <template #default="{ row }">
+                            <span>¥ {{ row.payMoney }}</span>
+                        </template>
+                    </el-table-column>
 
-                <!-- Weight -->
-                <el-table-column label="重量" width="80" align="center">
-                    <template #default="{ row }">
-                        <span>0kg</span>
-                    </template>
-                </el-table-column>
-            </el-table>
+                    <!-- Weight -->
+                    <el-table-column label="重量" width="80" align="center">
+                        <template #default="{ row }">
+                            <span>0kg</span>
+                        </template>
+                    </el-table-column>
+                </el-table>
 
-            <!-- Summary Footer -->
-            <div class="summary-footer">
-                <template v-if="detail.discountList && detail.discountList.length > 0">
-                    <div
-                        class="summary-row"
-                        v-for="(item, index) in detail.discountList"
-                        :key="index"
+                <!-- Summary Footer -->
+                <div class="summary-footer">
+                    <template
+                        v-if="
+                            detail.discountList &&
+                            detail.discountList.length > 0
+                        "
                     >
-                        <span>{{ item.discountActivityMsg || '-' }}:</span>
-                        <span class="amount red">- ¥{{ item.discountMoney }}</span>
+                        <div
+                            class="summary-row"
+                            v-for="(item, index) in detail.discountList"
+                            :key="index"
+                        >
+                            <span>{{ item.discountActivityMsg || '-' }}:</span>
+                            <span class="amount red"
+                                >- ¥{{ item.discountMoney }}</span
+                            >
+                        </div>
+                    </template>
+                    <div class="summary-row" v-if="detail.reduceMoney > 0">
+                        <span>分享降价:</span>
+                        <span class="amount red"
+                            >- ¥{{ detail.reduceMoney }}</span
+                        >
+                    </div>
+                    <div class="summary-row">
+                        <span>运费:</span>
+                        <span class="amount">¥ {{ detail.expressMoney }}</span>
+                    </div>
+                    <div class="summary-row total">
+                        <span>实收款:</span>
+                        <span class="amount red large"
+                            >¥ {{ detail.payMoney }}</span
+                        >
                     </div>
-                </template>
-                <div class="summary-row" v-if="detail.reduceMoney > 0">
-                    <span>分享降价:</span>
-                    <span class="amount red">- ¥{{ detail.reduceMoney }}</span>
-                </div>
-                <div class="summary-row">
-                    <span>运费:</span>
-                    <span class="amount">¥ {{ detail.expressMoney }}</span>
-                </div>
-                <div class="summary-row total">
-                    <span>实收款:</span>
-                    <span class="amount red large"
-                        >¥ {{ detail.payMoney }}</span
-                    >
                 </div>
             </div>
-            </div>
         </ele-card>
     </div>
 </template>

+ 5 - 7
src/views/mallOrder/refund/components/agree-dialog.vue

@@ -48,9 +48,7 @@
                         >
                             <div class="flex-1 min-w-0">
                                 <template v-if="selectedAddress">
-                                    <div
-                                        class="text-sm text-gray-900 truncate"
-                                    >
+                                    <div class="text-sm text-gray-900 truncate">
                                         {{ selectedAddress.name }}
                                         {{ selectedAddress.mobile }}
                                         <el-tag
@@ -120,7 +118,9 @@
                                         {{ item.mobile }}
                                     </div>
                                     <el-tag
-                                        v-if="Number(item.defaultReturnFlag) === 1"
+                                        v-if="
+                                            Number(item.defaultReturnFlag) === 1
+                                        "
                                         size="small"
                                         type="success"
                                         class="ml-2"
@@ -185,9 +185,7 @@
     const selectedAddress = computed(() => {
         const id = String(form.refundAddressId || '');
         if (!id) return null;
-        return (
-            addressList.value.find((a) => String(a.id) === id) || null
-        );
+        return addressList.value.find((a) => String(a.id) === id) || null;
     });
 
     const form = reactive({

+ 60 - 4
src/views/mallOrder/refund/components/refund-detail-dialog.vue

@@ -90,12 +90,24 @@
                                 <el-step
                                     :title="`买家申请${getRefundTypeText(form.refundType)}`"
                                 />
-                                <el-step title="卖家处理退款申请" />
+                                <el-step
+                                    :title="
+                                        String(form.status) === '10'
+                                            ? '退款已撤销'
+                                            : '卖家处理退款申请'
+                                    "
+                                />
                                 <el-step title="退款完毕" />
                             </template>
                             <template v-else>
                                 <el-step title="买家申请退货退款" />
-                                <el-step title="卖家处理退货申请" />
+                                <el-step
+                                    :title="
+                                        String(form.status) === '10'
+                                            ? '退款已撤销'
+                                            : '卖家处理退货申请'
+                                    "
+                                />
                                 <el-step title="买家退货" />
                                 <el-step title="退款完毕" />
                             </template>
@@ -364,6 +376,13 @@
                 <el-button @click="handleAgree" :disabled="!canOperate">{{
                     isRefundOnly ? '同意退款' : '同意退货'
                 }}</el-button>
+                <el-button
+                    v-if="isRefundOnly"
+                    type="primary"
+                    @click="handleExpressIntercept"
+                    :disabled="form.interceptStatus === '1'"
+                    >快递拦截</el-button
+                >
                 <el-button
                     v-if="isRefundOnly"
                     type="primary"
@@ -411,6 +430,7 @@
 <script setup>
     import { ref, computed } from 'vue';
     import { EleMessage } from 'ele-admin-plus/es';
+    import { ElMessageBox } from 'element-plus';
     import { CopyDocument } from '@element-plus/icons-vue';
     import { useClipboard } from '@vueuse/core';
     import request from '@/utils/request';
@@ -440,7 +460,7 @@
     // 进度条状态
     const processStatus = computed(() => {
         const status = Number(form.value.status);
-        if ([5, 6].includes(status)) {
+        if ([5, 6, 10].includes(status)) {
             return 'error';
         }
         return 'process';
@@ -456,6 +476,8 @@
             if ([4, 7, 8].includes(status)) return 2;
             // 9:退款成功 -> 全绿(第3步完成)
             if (status === 9) return 3;
+            // 10:退款已撤销 -> 停在第2步
+            if (status === 10) return 1;
             return 1;
         }
 
@@ -468,6 +490,8 @@
         if ([7, 8].includes(status)) return 3;
         // 9:退款成功 -> 全绿(第4步完成)
         if (status === 9) return 4;
+        // 10:退款已撤销 -> 停在第2步
+        if (status === 10) return 1;
         return 1;
     });
 
@@ -579,7 +603,8 @@
             6: '超时关闭',
             7: '买家已发货',
             8: '确认收货',
-            9: '退款成功'
+            9: '退款成功',
+            10: '退款已撤销'
         };
         return map[key] || status;
     };
@@ -603,6 +628,37 @@
         handleOpen(form.value);
     };
 
+    const handleExpressIntercept = () => {
+        ElMessageBox.confirm('确定要拦截该快递吗?', '提示', {
+            type: 'warning',
+            confirmButtonText: '确定',
+            cancelButtonText: '取消'
+        })
+            .then(() => {
+                loading.value = true;
+                request
+                    .post(
+                        `/shop/shopOrder/refundExpressIntercept/${form.value.refundOrderId}`
+                    )
+                    .then((res) => {
+                        if (res.data.code === 200) {
+                            EleMessage.success('快递拦截成功');
+                            handleOpen(form.value);
+                        } else {
+                            EleMessage.error(res.data.msg || '快递拦截失败');
+                        }
+                    })
+                    .catch((e) => {
+                        console.error(e);
+                        EleMessage.error('网络错误');
+                    })
+                    .finally(() => {
+                        loading.value = false;
+                    });
+            })
+            .catch(() => {});
+    };
+
     const handleRefuse = () => {
         if (refuseDialogRef.value) {
             refuseDialogRef.value.open(form.value.refundOrderId);

+ 251 - 183
src/views/mallOrder/refund/components/refund-item.vue

@@ -6,9 +6,15 @@
             <el-tag type="success" size="small" class="mr-2" effect="plain">
                 {{ getRefundTypeText(item.refundType) }}
             </el-tag>
-            <span class="mr-4 text-black-600">退款编号: {{ item.refundOrderId }}</span>
-            <span class="mr-4 text-black-600">原订单编号: {{ item.originOrderId }}</span>
-            <span class="mr-4 text-black-600">申请时间: {{ item.createTime }}</span>
+            <span class="mr-4 text-black-600"
+                >退款编号: {{ item.refundOrderId }}</span
+            >
+            <span class="mr-4 text-black-600"
+                >原订单编号: {{ item.originOrderId }}</span
+            >
+            <span class="mr-4 text-black-600"
+                >申请时间: {{ item.createTime }}</span
+            >
             <span class="mr-4 text-black-600" v-if="item.cancelStatus == 2">
                 <el-tag type="info" size="small">已撤销</el-tag>
             </span>
@@ -18,22 +24,36 @@
         <div class="refund-body">
             <!-- Product Info (Flex 3) -->
             <div class="col-product-wrapper">
-                <div v-for="(product, idx) in item.detailList" :key="idx" class="product-row">
+                <div
+                    v-for="(product, idx) in item.detailList"
+                    :key="idx"
+                    class="product-row"
+                >
                     <div class="product-info">
-                        <el-image :src="product.cover" class="product-img" fit="cover" />
+                        <el-image
+                            :src="product.cover"
+                            class="product-img"
+                            fit="cover"
+                        />
                         <div class="product-detail">
                             <div class="product-title">{{
                                 product.bookName
-                                }}</div>
+                            }}</div>
                             <div class="product-isbn">
                                 ISBN:
                                 <span class="link">{{ product.isbn }}</span>
-                                <el-icon class="cursor-pointer ml-1" @click="handleCopy(product.isbn)">
+                                <el-icon
+                                    class="cursor-pointer ml-1"
+                                    @click="handleCopy(product.isbn)"
+                                >
                                     <CopyDocument />
                                 </el-icon>
                             </div>
                             <div class="product-price">
-                                <span class="mr-2">退回数量: {{ product.refundNum || product.num }}</span>
+                                <span class="mr-2"
+                                    >退回数量:
+                                    {{ product.refundNum || product.num }}</span
+                                >
                                 <span>单价: ¥ {{ product.price }}</span>
                             </div>
                         </div>
@@ -44,7 +64,10 @@
             <!-- Refund Amount (Flex 1) -->
             <div class="col-merged col-amount">
                 <div class="amount">申请: ¥ {{ item.refundMoney }}</div>
-                <div class="final-amount text-green-500" v-if="item.refundMoneyFinal">实退: ¥ {{ item.refundMoneyFinal }}
+                <div
+                    class="final-amount text-green-500"
+                    v-if="item.refundMoneyFinal"
+                    >实退: ¥ {{ item.refundMoneyFinal }}
                 </div>
                 <div class="money-status text-xs text-gray-400 mt-1">
                     {{ getMoneyStatusText(item.moneyStatus) }}
@@ -56,245 +79,290 @@
                 <div class="buyer-info">
                     <el-avatar :size="30" :src="item.avatar" />
                     <div class="buyer-name">{{ item.userNick }}</div>
-                    <div class="buyer-id text-xs text-gray-400">ID: {{ item.userId }}</div>
+                    <div class="buyer-id text-xs text-gray-400"
+                        >ID: {{ item.userId }}</div
+                    >
                 </div>
             </div>
 
             <!-- Reason (Flex 1) -->
             <div class="col-merged col-reason">
                 <div class="font-bold">{{ item.refundReason }}</div>
-                <div class="text-xs text-gray-500 mt-1">货物状态: {{ getShopStatusText(item.shopStatus) }}</div>
+                <div class="text-xs text-gray-500 mt-1"
+                    >货物状态: {{ getShopStatusText(item.shopStatus) }}</div
+                >
             </div>
 
             <!-- Logistics (Flex 1.5) -->
-            <div class="col-merged col-logistics flex flex-col justify-center items-center w-full">
-                <div class="logistics-row flex justify-center items-center mb-2 w-full">
-                    <span class="text-gray-600">商家发货:{{ item.shopStatus == 2 ? '已收货' : '未发货' }}</span>
+            <div
+                class="col-merged col-logistics flex flex-col justify-center items-center w-full"
+            >
+                <div
+                    class="logistics-row flex justify-center items-center mb-2 w-full"
+                >
+                    <span class="text-gray-600"
+                        >商家发货:{{
+                            item.shopStatus == 2 ? '已收货' : '未发货'
+                        }}</span
+                    >
                 </div>
-                <div class="logistics-row flex justify-center items-center w-full"
-                    v-if="String(item.refundType) === '1'">
-                    <span class="text-gray-600">买家退货:{{ getBuyerLogisticsStatus(item) }}</span>
+                <div
+                    class="logistics-row flex justify-center items-center w-full"
+                    v-if="String(item.refundType) === '1'"
+                >
+                    <span class="text-gray-600"
+                        >买家退货:{{ getBuyerLogisticsStatus(item) }}</span
+                    >
                 </div>
             </div>
 
             <!-- Status (Flex 1) -->
             <div class="col-merged col-status">
-                <div class="text-red-500 font-bold mb-1">
-                    <dict-data code="shop_refund_status" v-model="item.status" type="text" />
-
-                    <el-button class="ml-2" type="primary" size="small" @click="$emit('confirm-refund', item)"
-                        v-if="item.status == 8 && item.moneyStatus == 1">同意退款</el-button>
+                <div
+                    class="font-bold mb-1"
+                    :class="{
+                        'text-red-500': ['5', '6', '10'].includes(item.status)
+                    }"
+                >
+                    <dict-data
+                        code="shop_refund_status"
+                        v-model="item.status"
+                        type="text"
+                    />
+
+                    <el-button
+                        class="ml-2"
+                        type="primary"
+                        size="small"
+                        @click="$emit('confirm-refund', item)"
+                        v-if="item.status == 8 && item.moneyStatus == 1"
+                        >同意退款</el-button
+                    >
                 </div>
-                <el-button link type="primary" size="small" @click="$emit('view-detail', item)">[查看详情]</el-button>
+                <el-button
+                    link
+                    type="primary"
+                    size="small"
+                    @click="$emit('view-detail', item)"
+                    >[查看详情]</el-button
+                >
             </div>
 
             <!-- Action (Flex 1) -->
             <div class="col-merged col-action">
-                <div class="action-btn text-green-500" @click="$emit('push-sms', item)">[推送短信]</div>
-                <div class="action-btn text-orange-500" @click="$emit('send-packet', item)">[发红包]</div>
+                <div
+                    class="action-btn text-green-500"
+                    @click="$emit('push-sms', item)"
+                    >[推送短信]</div
+                >
+                <div
+                    class="action-btn text-orange-500"
+                    @click="$emit('send-packet', item)"
+                    >[发红包]</div
+                >
             </div>
         </div>
     </div>
 </template>
 
 <script setup>
-import { CopyDocument } from '@element-plus/icons-vue';
-import { EleMessage } from 'ele-admin-plus/es';
-import { useClipboard } from '@vueuse/core';
-
-const props = defineProps({
-    item: {
-        type: Object,
-        required: true
-    }
-});
-
-defineEmits([
-    'push-sms',
-    'send-packet',
-    'view-detail',
-    'view-logistics',
-    'confirm-refund'
-]);
-
-const { copy } = useClipboard();
-
-const handleCopy = async (text) => {
-    try {
-        await copy(text);
-        EleMessage.success('复制成功');
-    } catch (e) {
-        EleMessage.error('复制失败');
-    }
-};
-
-const getRefundTypeText = (type) => {
-    const map = {
-        0: '极速退款',
-        1: '退货退款',
-        2: '仅退款',
-        3: '缺货退款'
+    import { CopyDocument } from '@element-plus/icons-vue';
+    import { EleMessage } from 'ele-admin-plus/es';
+    import { useClipboard } from '@vueuse/core';
+
+    const props = defineProps({
+        item: {
+            type: Object,
+            required: true
+        }
+    });
+
+    defineEmits([
+        'push-sms',
+        'send-packet',
+        'view-detail',
+        'view-logistics',
+        'confirm-refund'
+    ]);
+
+    const { copy } = useClipboard();
+
+    const handleCopy = async (text) => {
+        try {
+            await copy(text);
+            EleMessage.success('复制成功');
+        } catch (e) {
+            EleMessage.error('复制失败');
+        }
+    };
+
+    const getRefundTypeText = (type) => {
+        const map = {
+            0: '极速退款',
+            1: '退货退款',
+            2: '仅退款',
+            3: '缺货退款'
+        };
+        return map[type] || type;
+    };
+
+    const getShopStatusText = (status) => {
+        const map = {
+            1: '未收到货',
+            2: '已收到货'
+        };
+        return map[status] || '-';
     };
-    return map[type] || type;
-};
 
-const getShopStatusText = (status) => {
-    const map = {
-        1: '未收到货',
-        2: '已收到货'
+    const getBuyerLogisticsStatus = (item) => {
+        const status = String(item.status);
+        if (status === '7') return '运输中';
+        if (item.finishTime) return '已收货';
+        return '未发货';
     };
-    return map[status] || '-';
-};
-
-const getBuyerLogisticsStatus = (item) => {
-    const status = String(item.status);
-    if (status === '7') return '运输中';
-    if (item.finishTime) return '已收货';
-    return '未发货';
-};
-
-const getMoneyStatusText = (status) => {
-    const map = {
-        1: '未退还',
-        2: '已退还',
-        3: '已到账'
+
+    const getMoneyStatusText = (status) => {
+        const map = {
+            1: '未退还',
+            2: '已退还',
+            3: '已到账'
+        };
+        return map[status] || '未退还';
     };
-    return map[status] || '未退还';
-};
 </script>
 
 <style lang="scss" scoped>
-.refund-item {
-    border: 1px solid #ebeef5;
-    margin-bottom: 15px;
-    background: #fff;
-
-    .refund-header {
-        background-color: #f5f7fa;
-        padding: 8px 15px;
-        font-size: 13px;
-        display: flex;
-        align-items: center;
-        border-bottom: 1px solid #ebeef5;
-    }
-
-    .refund-body {
-        display: flex;
-        align-items: stretch;
+    .refund-item {
+        border: 1px solid #ebeef5;
+        margin-bottom: 15px;
+        background: #fff;
+
+        .refund-header {
+            background-color: #f5f7fa;
+            padding: 8px 15px;
+            font-size: 13px;
+            display: flex;
+            align-items: center;
+            border-bottom: 1px solid #ebeef5;
+        }
 
-        .col-product-wrapper {
-            flex: 3;
+        .refund-body {
             display: flex;
-            flex-direction: column;
-            border-right: 1px solid #ebeef5;
+            align-items: stretch;
 
-            .product-row {
+            .col-product-wrapper {
+                flex: 3;
                 display: flex;
-                padding: 15px;
-                border-bottom: 1px solid #ebeef5;
-
-                &:last-child {
-                    border-bottom: none;
-                }
+                flex-direction: column;
+                border-right: 1px solid #ebeef5;
 
-                .product-info {
+                .product-row {
                     display: flex;
-                    width: 100%;
-
-                    .product-img {
-                        width: 80px;
-                        height: 80px;
-                        margin-right: 15px;
-                        border-radius: 4px;
-                        border: 1px solid #eee;
+                    padding: 15px;
+                    border-bottom: 1px solid #ebeef5;
+
+                    &:last-child {
+                        border-bottom: none;
                     }
 
-                    .product-detail {
-                        font-size: 13px;
-                        flex: 1;
+                    .product-info {
+                        display: flex;
+                        width: 100%;
 
-                        .product-title {
-                            font-weight: 500;
-                            margin-bottom: 5px;
-                            color: #333;
+                        .product-img {
+                            width: 80px;
+                            height: 80px;
+                            margin-right: 15px;
+                            border-radius: 4px;
+                            border: 1px solid #eee;
                         }
 
-                        .product-isbn {
-                            color: #666;
-                            margin-bottom: 3px;
-                            font-size: 12px;
+                        .product-detail {
+                            font-size: 13px;
+                            flex: 1;
 
-                            .link {
-                                color: #409eff;
+                            .product-title {
+                                font-weight: 500;
+                                margin-bottom: 5px;
+                                color: #333;
+                            }
+
+                            .product-isbn {
+                                color: #666;
+                                margin-bottom: 3px;
+                                font-size: 12px;
+
+                                .link {
+                                    color: #409eff;
+                                }
                             }
-                        }
 
-                        .product-price {
-                            margin-top: 5px;
-                            color: #333;
+                            .product-price {
+                                margin-top: 5px;
+                                color: #333;
+                            }
                         }
                     }
                 }
             }
-        }
 
-        .col-merged {
-            border-right: 1px solid #ebeef5;
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-            padding: 10px;
-            text-align: center;
-            font-size: 13px;
+            .col-merged {
+                border-right: 1px solid #ebeef5;
+                display: flex;
+                flex-direction: column;
+                justify-content: center;
+                align-items: center;
+                padding: 10px;
+                text-align: center;
+                font-size: 13px;
 
-            &:last-child {
-                border-right: none;
-            }
+                &:last-child {
+                    border-right: none;
+                }
 
-            &.col-amount {
-                flex: 1;
-            }
+                &.col-amount {
+                    flex: 1;
+                }
 
-            &.col-buyer {
-                flex: 1;
-            }
+                &.col-buyer {
+                    flex: 1;
+                }
 
-            &.col-reason {
-                flex: 1;
-            }
+                &.col-reason {
+                    flex: 1;
+                }
 
-            &.col-logistics {
-                flex: 1.5;
-                align-items: flex-start;
-                padding-left: 15px;
+                &.col-logistics {
+                    flex: 1.5;
+                    align-items: flex-start;
+                    padding-left: 15px;
 
-                .logistics-row {
-                    display: flex;
-                    align-items: center;
-                    margin-bottom: 4px;
-                    color: #666;
+                    .logistics-row {
+                        display: flex;
+                        align-items: center;
+                        margin-bottom: 4px;
+                        color: #666;
+                    }
                 }
-            }
 
-            &.col-status {
-                flex: 1;
-            }
+                &.col-status {
+                    flex: 1;
+                }
 
-            &.col-action {
-                flex: 1;
+                &.col-action {
+                    flex: 1;
+                }
             }
         }
-    }
 
-    .action-btn {
-        cursor: pointer;
-        margin-bottom: 5px;
-        font-size: 13px;
+        .action-btn {
+            cursor: pointer;
+            margin-bottom: 5px;
+            font-size: 13px;
 
-        &:hover {
-            opacity: 0.8;
+            &:hover {
+                opacity: 0.8;
+            }
         }
     }
-}
 </style>

+ 61 - 61
src/views/profile/components/user-card.vue

@@ -3,73 +3,73 @@
         <div>
             <div class="info-user">
                 <div class="info-user-avatar" @click="openCropper">
-                <el-avatar :size="100" :src="data.avatar" />
-                <el-icon class="info-user-avatar-icon">
-                    <CloudUploadOutlined style="stroke-width: 3" />
-                </el-icon>
-            </div>
-            <ele-text size="xxl" style="margin-top: 6px">
-                {{ data.nickName }}
-            </ele-text>
-            <ele-text type="placeholder">
-                <span>{{ data.dept?.deptName }}</span>
-                <span style="padding: 0 8px">/</span>
-                <span>{{ data.postGroup }}</span>
-            </ele-text>
-        </div>
-        <el-divider border-style="dashed" style="margin: 22px 0" />
-        <div class="info-list">
-            <div class="info-item">
-                <el-icon>
-                    <UserOutlined />
-                </el-icon>
-                <div class="info-item-text">
-                    <span>用户名称:</span>
-                    <span>{{ data.userName }}</span>
+                    <el-avatar :size="100" :src="data.avatar" />
+                    <el-icon class="info-user-avatar-icon">
+                        <CloudUploadOutlined style="stroke-width: 3" />
+                    </el-icon>
                 </div>
+                <ele-text size="xxl" style="margin-top: 6px">
+                    {{ data.nickName }}
+                </ele-text>
+                <ele-text type="placeholder">
+                    <span>{{ data.dept?.deptName }}</span>
+                    <span style="padding: 0 8px">/</span>
+                    <span>{{ data.postGroup }}</span>
+                </ele-text>
             </div>
-            <div class="info-item">
-                <el-icon>
-                    <MobileOutlined />
-                </el-icon>
-                <div class="info-item-text">
-                    <span>手机号码:</span>
-                    <span>{{ data.phonenumber }}</span>
+            <el-divider border-style="dashed" style="margin: 22px 0" />
+            <div class="info-list">
+                <div class="info-item">
+                    <el-icon>
+                        <UserOutlined />
+                    </el-icon>
+                    <div class="info-item-text">
+                        <span>用户名称:</span>
+                        <span>{{ data.userName }}</span>
+                    </div>
                 </div>
-            </div>
-            <div class="info-item">
-                <el-icon>
-                    <MailOutlined />
-                </el-icon>
-                <div class="info-item-text">
-                    <span>邮箱账号:</span>
-                    <span>{{ data.email }}</span>
+                <div class="info-item">
+                    <el-icon>
+                        <MobileOutlined />
+                    </el-icon>
+                    <div class="info-item-text">
+                        <span>手机号码:</span>
+                        <span>{{ data.phonenumber }}</span>
+                    </div>
                 </div>
-            </div>
-            <div class="info-item">
-                <el-icon>
-                    <IdcardOutlined />
-                </el-icon>
-                <div class="info-item-text">
-                    <span>所属角色:</span>
-                    <span>{{ data.roleGroup }}</span>
+                <div class="info-item">
+                    <el-icon>
+                        <MailOutlined />
+                    </el-icon>
+                    <div class="info-item-text">
+                        <span>邮箱账号:</span>
+                        <span>{{ data.email }}</span>
+                    </div>
+                </div>
+                <div class="info-item">
+                    <el-icon>
+                        <IdcardOutlined />
+                    </el-icon>
+                    <div class="info-item-text">
+                        <span>所属角色:</span>
+                        <span>{{ data.roleGroup }}</span>
+                    </div>
                 </div>
             </div>
-        </div>
-        <!-- 头像裁剪弹窗 -->
-        <ele-cropper-modal
-            v-model="visible"
-            :src="data.avatar"
-            :options="{
-                aspectRatio: 1,
-                autoCropArea: 1,
-                viewMode: 1,
-                dragMode: 'move'
-            }"
-            :to-blob="true"
-            :modal-props="{ destroyOnClose: true }"
-            @done="handleCrop"
-        />
+            <!-- 头像裁剪弹窗 -->
+            <ele-cropper-modal
+                v-model="visible"
+                :src="data.avatar"
+                :options="{
+                    aspectRatio: 1,
+                    autoCropArea: 1,
+                    viewMode: 1,
+                    dragMode: 'move'
+                }"
+                :to-blob="true"
+                :modal-props="{ destroyOnClose: true }"
+                @done="handleCrop"
+            />
         </div>
     </ele-card>
 </template>

+ 12 - 4
src/views/recycleOrder/detail/index.vue

@@ -38,15 +38,23 @@
 
         <ele-card class="order-service" header="订单服务">
             <div>
-                <el-tag size="large" style="margin-right: 20px">免费退回</el-tag>
-                <el-tag size="large" style="margin-right: 20px">72小时验</el-tag>
-                <el-tag size="large" style="margin-right: 20px">卖亏必赔</el-tag>
+                <el-tag size="large" style="margin-right: 20px"
+                    >免费退回</el-tag
+                >
+                <el-tag size="large" style="margin-right: 20px"
+                    >72小时验</el-tag
+                >
+                <el-tag size="large" style="margin-right: 20px"
+                    >卖亏必赔</el-tag
+                >
                 <el-tag size="large">极速打款</el-tag>
             </div>
         </ele-card>
 
         <ele-card class="order-note" header="审核计价说明">
-            <div>品相良好:按原始回收折扣计价;品相一般:按原始回收折扣*0.7计价;品相极差:不收。</div>
+            <div
+                >品相良好:按原始回收折扣计价;品相一般:按原始回收折扣*0.7计价;品相极差:不收。</div
+            >
         </ele-card>
         <ele-card class="order-freight" header="物流动态">
             <orderFreightStatus :records="detail.trackingVoList || []" />

+ 12 - 4
src/views/recycleOrder/needReturned/refund-detail.vue

@@ -47,15 +47,23 @@
 
         <ele-card class="order-service" header="订单服务">
             <div>
-                <el-tag size="large" style="margin-right: 20px">免费退回</el-tag>
-                <el-tag size="large" style="margin-right: 20px">72小时验</el-tag>
-                <el-tag size="large" style="margin-right: 20px">卖亏必赔</el-tag>
+                <el-tag size="large" style="margin-right: 20px"
+                    >免费退回</el-tag
+                >
+                <el-tag size="large" style="margin-right: 20px"
+                    >72小时验</el-tag
+                >
+                <el-tag size="large" style="margin-right: 20px"
+                    >卖亏必赔</el-tag
+                >
                 <el-tag size="large">极速打款</el-tag>
             </div>
         </ele-card>
 
         <ele-card class="order-note" header="审核计价说明">
-            <div>品相良好:按原始回收折扣计价;品相一般:按原始回收折扣*0.7计价;品相极差:不收。</div>
+            <div
+                >品相良好:按原始回收折扣计价;品相一般:按原始回收折扣*0.7计价;品相极差:不收。</div
+            >
         </ele-card>
         <ele-card class="order-freight" header="物流动态">
             <orderFreightStatus :records="detail.trackingVoList" />

+ 497 - 0
src/views/workOrder/analysis/index.vue

@@ -0,0 +1,497 @@
+<template>
+    <ele-page>
+        <ele-card
+            :body-style="{ padding: '24px' }"
+            shadow="never"
+            class="border-none rounded-xl"
+        >
+            <!-- 数据概览 -->
+            <div class="mb-6">
+                <div class="flex items-center mb-4">
+                    <div class="w-1 h-4 bg-blue-500 mr-2 rounded"></div>
+                    <span class="font-bold text-gray-800 text-base"
+                        >数据概览</span
+                    >
+                </div>
+                <el-row :gutter="24">
+                    <el-col :span="8">
+                        <div
+                            class="flex items-center p-6 bg-white border border-gray-100 shadow-sm rounded-lg h-[104px] transition-shadow hover:shadow-md"
+                        >
+                            <div
+                                class="mr-4 flex items-center justify-center w-12 h-12 rounded-lg bg-blue-50"
+                            >
+                                <svg
+                                    width="24"
+                                    height="24"
+                                    viewBox="0 0 48 48"
+                                    fill="none"
+                                    xmlns="http://www.w3.org/2000/svg"
+                                >
+                                    <path
+                                        d="M24 4L28.1 10.5H35.5V17.9L42 22L39 28.5L42 35.5H34.5V42.5H27.5L24 45L20.5 42.5H13.5V35.5H6L9 28.5L6 22L12.5 17.9V10.5H19.9L24 4Z"
+                                        stroke="#3B82F6"
+                                        stroke-width="4"
+                                        stroke-linejoin="round"
+                                    />
+                                    <circle
+                                        cx="24"
+                                        cy="24.5"
+                                        r="5"
+                                        stroke="#3B82F6"
+                                        stroke-width="4"
+                                    />
+                                </svg>
+                            </div>
+                            <div class="flex flex-col justify-center">
+                                <span class="text-gray-500 text-sm mb-1"
+                                    >未完结工单数量</span
+                                >
+                                <span
+                                    class="text-[28px] font-bold text-gray-800 leading-none"
+                                    >56</span
+                                >
+                            </div>
+                        </div>
+                    </el-col>
+                    <el-col :span="8">
+                        <div
+                            class="flex items-center p-6 bg-white border border-gray-100 shadow-sm rounded-lg h-[104px] transition-shadow hover:shadow-md"
+                        >
+                            <div
+                                class="mr-4 flex items-center justify-center w-12 h-12 rounded-lg bg-orange-50"
+                            >
+                                <svg
+                                    width="24"
+                                    height="24"
+                                    viewBox="0 0 48 48"
+                                    fill="none"
+                                    xmlns="http://www.w3.org/2000/svg"
+                                >
+                                    <path
+                                        d="M24 4L28.1 10.5H35.5V17.9L42 22L39 28.5L42 35.5H34.5V42.5H27.5L24 45L20.5 42.5H13.5V35.5H6L9 28.5L6 22L12.5 17.9V10.5H19.9L24 4Z"
+                                        stroke="#F59E0B"
+                                        stroke-width="4"
+                                        stroke-linejoin="round"
+                                    />
+                                    <circle
+                                        cx="24"
+                                        cy="24.5"
+                                        r="5"
+                                        stroke="#F59E0B"
+                                        stroke-width="4"
+                                    />
+                                </svg>
+                            </div>
+                            <div class="flex flex-col justify-center">
+                                <span class="text-gray-500 text-sm mb-1"
+                                    >待处理工单数量</span
+                                >
+                                <span
+                                    class="text-[28px] font-bold text-gray-800 leading-none"
+                                    >56</span
+                                >
+                            </div>
+                        </div>
+                    </el-col>
+                    <el-col :span="8">
+                        <div
+                            class="flex items-center p-6 bg-white border border-gray-100 shadow-sm rounded-lg h-[104px] transition-shadow hover:shadow-md"
+                        >
+                            <div
+                                class="mr-4 flex items-center justify-center w-12 h-12 rounded-lg bg-green-50"
+                            >
+                                <svg
+                                    width="24"
+                                    height="24"
+                                    viewBox="0 0 48 48"
+                                    fill="none"
+                                    xmlns="http://www.w3.org/2000/svg"
+                                >
+                                    <path
+                                        d="M24 4L28.1 10.5H35.5V17.9L42 22L39 28.5L42 35.5H34.5V42.5H27.5L24 45L20.5 42.5H13.5V35.5H6L9 28.5L6 22L12.5 17.9V10.5H19.9L24 4Z"
+                                        stroke="#10B981"
+                                        stroke-width="4"
+                                        stroke-linejoin="round"
+                                    />
+                                    <circle
+                                        cx="24"
+                                        cy="24.5"
+                                        r="5"
+                                        stroke="#10B981"
+                                        stroke-width="4"
+                                    />
+                                </svg>
+                            </div>
+                            <div class="flex flex-col justify-center">
+                                <span class="text-gray-500 text-sm mb-1"
+                                    >处理中工单数量</span
+                                >
+                                <span
+                                    class="text-[28px] font-bold text-gray-800 leading-none"
+                                    >56</span
+                                >
+                            </div>
+                        </div>
+                    </el-col>
+                </el-row>
+            </div>
+
+            <!-- 工单趋势 -->
+            <div class="mb-6 mt-8">
+                <div class="flex justify-between items-center mb-4">
+                    <div class="flex items-center">
+                        <div class="w-1 h-4 bg-blue-500 mr-2 rounded"></div>
+                        <span class="font-bold text-gray-800 text-base"
+                            >工单趋势</span
+                        >
+                    </div>
+                    <div class="flex items-center gap-3">
+                        <el-radio-group
+                            v-model="trendSearch.days"
+                            class="custom-radio-group"
+                        >
+                            <el-radio-button label="7">7日</el-radio-button>
+                            <el-radio-button label="15">15日</el-radio-button>
+                            <el-radio-button label="30">30日</el-radio-button>
+                        </el-radio-group>
+                        <el-date-picker
+                            v-model="trendSearch.dateRange"
+                            type="daterange"
+                            range-separator="至"
+                            start-placeholder="选择时间"
+                            end-placeholder="选择时间"
+                            style="width: 260px"
+                        />
+                        <el-button
+                            type="primary"
+                            class="custom-btn-primary"
+                            @click="fetchTrendData"
+                            >查询</el-button
+                        >
+                        <el-button
+                            class="custom-btn-info"
+                            @click="resetTrendSearch"
+                            >重置</el-button
+                        >
+                    </div>
+                </div>
+                <ele-card
+                    :body-style="{ padding: '10px 20px 20px 20px' }"
+                    shadow="never"
+                    class="border border-gray-100 shadow-sm rounded-lg"
+                >
+                    <v-chart
+                        ref="trendChartRef"
+                        style="height: 380px"
+                        :option="trendChartOption"
+                        autoresize
+                    />
+                </ele-card>
+            </div>
+
+            <!-- 工作量统计 -->
+            <div class="mb-6 mt-8">
+                <div class="flex justify-between items-center mb-4">
+                    <div class="flex items-center">
+                        <div class="w-1 h-4 bg-blue-500 mr-2 rounded"></div>
+                        <span class="font-bold text-gray-800 text-base"
+                            >工作量统计</span
+                        >
+                    </div>
+                    <div class="flex items-center gap-3">
+                        <el-radio-group
+                            v-model="workloadSearch.days"
+                            class="custom-radio-group"
+                        >
+                            <el-radio-button label="7">7日</el-radio-button>
+                            <el-radio-button label="15">15日</el-radio-button>
+                            <el-radio-button label="30">30日</el-radio-button>
+                        </el-radio-group>
+                        <el-date-picker
+                            v-model="workloadSearch.dateRange"
+                            type="daterange"
+                            range-separator="至"
+                            start-placeholder="选择时间"
+                            end-placeholder="选择时间"
+                            style="width: 260px"
+                        />
+                        <el-button
+                            type="primary"
+                            class="custom-btn-primary"
+                            @click="fetchWorkloadData"
+                            >查询</el-button
+                        >
+                        <el-button
+                            class="custom-btn-info"
+                            @click="resetWorkloadSearch"
+                            >重置</el-button
+                        >
+                    </div>
+                </div>
+                <ele-card
+                    :body-style="{ padding: '0px' }"
+                    shadow="never"
+                    class="border border-gray-100 shadow-sm rounded-lg overflow-hidden"
+                >
+                    <el-table
+                        :data="workloadData"
+                        show-summary
+                        border
+                        style="width: 100%"
+                        :header-cell-style="{
+                            background: '#F2F2F2',
+                            color: '#333',
+                            fontWeight: 'bold'
+                        }"
+                    >
+                        <el-table-column
+                            type="index"
+                            label="序号"
+                            width="80"
+                            align="center"
+                        />
+                        <el-table-column prop="operator" label="操作员" />
+                        <el-table-column prop="created" label="创建工单数量" />
+                        <el-table-column prop="finished" label="完结工单数量" />
+                        <el-table-column prop="claimed" label="理赔工单数量" />
+                        <el-table-column
+                            prop="claimAmount"
+                            label="理赔工单金额"
+                        />
+                    </el-table>
+                </ele-card>
+            </div>
+        </ele-card>
+    </ele-page>
+</template>
+
+<script setup>
+    import { ref, reactive } from 'vue';
+    import { use } from 'echarts/core';
+    import { CanvasRenderer } from 'echarts/renderers';
+    import { BarChart } from 'echarts/charts';
+    import {
+        GridComponent,
+        TooltipComponent,
+        LegendComponent
+    } from 'echarts/components';
+    import VChart from 'vue-echarts';
+    import { useEcharts } from '@/utils/use-echarts';
+
+    defineOptions({ name: 'WorkOrderAnalysis' });
+
+    use([
+        CanvasRenderer,
+        BarChart,
+        GridComponent,
+        TooltipComponent,
+        LegendComponent
+    ]);
+
+    const trendChartRef = ref(null);
+    useEcharts([trendChartRef]);
+
+    // 趋势搜索参数
+    const trendSearch = reactive({
+        days: '7',
+        dateRange: []
+    });
+
+    // 趋势图表配置
+    const trendChartOption = reactive({
+        tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+                type: 'shadow'
+            }
+        },
+        legend: {
+            data: [
+                { name: '新增工单', icon: 'roundRect' },
+                { name: '完结工单', icon: 'roundRect' }
+            ],
+            top: 0,
+            itemWidth: 20,
+            itemHeight: 12,
+            textStyle: {
+                color: '#333'
+            }
+        },
+        grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+        },
+        xAxis: {
+            type: 'category',
+            data: ['1日', '2日', '3日', '4日', '5日', '6日', '7日'],
+            axisLine: {
+                show: true,
+                lineStyle: {
+                    color: '#E5E5E5'
+                }
+            },
+            axisTick: {
+                show: true,
+                alignWithLabel: true,
+                lineStyle: {
+                    color: '#E5E5E5'
+                }
+            },
+            axisLabel: {
+                color: '#333',
+                margin: 12
+            }
+        },
+        yAxis: {
+            type: 'value',
+            max: 250,
+            interval: 50,
+            splitLine: {
+                show: true,
+                lineStyle: {
+                    color: '#E5E5E5'
+                }
+            },
+            axisLabel: {
+                color: '#333'
+            }
+        },
+        series: [
+            {
+                name: '新增工单',
+                type: 'bar',
+                barWidth: 32,
+                itemStyle: {
+                    color: '#3B82F6',
+                    borderRadius: [4, 4, 0, 0]
+                },
+                data: [100, 140, 230, 100, 130, 150, 180]
+            },
+            {
+                name: '完结工单',
+                type: 'bar',
+                barWidth: 32,
+                itemStyle: {
+                    color: '#10B981',
+                    borderRadius: [4, 4, 0, 0]
+                },
+                data: [150, 100, 200, 140, 100, 175, 125]
+            }
+        ]
+    });
+
+    const fetchTrendData = () => {
+        // mock api call
+    };
+
+    const resetTrendSearch = () => {
+        trendSearch.days = '7';
+        trendSearch.dateRange = [];
+    };
+
+    // 工作量搜索参数
+    const workloadSearch = reactive({
+        days: '7',
+        dateRange: []
+    });
+
+    // 工作量表格数据
+    const workloadData = ref([
+        {
+            operator: 'zzz',
+            created: 11,
+            finished: 22,
+            claimed: 22,
+            claimAmount: 22
+        },
+        {
+            operator: 'zzz',
+            created: 11,
+            finished: 22,
+            claimed: 22,
+            claimAmount: 22
+        },
+        {
+            operator: 'zzz',
+            created: 11,
+            finished: 22,
+            claimed: 22,
+            claimAmount: 22
+        }
+    ]);
+
+    const fetchWorkloadData = () => {
+        // mock api call
+    };
+
+    const resetWorkloadSearch = () => {
+        workloadSearch.days = '7';
+        workloadSearch.dateRange = [];
+    };
+</script>
+
+<style scoped>
+    /* 自定义单选按钮组样式 */
+    .custom-radio-group :deep(.el-radio-button__inner) {
+        border: 1px solid transparent !important;
+        border-radius: 4px !important;
+        margin-right: 8px;
+        background-color: #f3f4f6;
+        color: #4b5563;
+        padding: 6px 16px;
+        box-shadow: none !important;
+        transition: all 0.2s;
+    }
+    .custom-radio-group
+        :deep(
+            .el-radio-button__original-radio:checked + .el-radio-button__inner
+        ) {
+        background-color: #3b82f6;
+        color: #fff;
+    }
+    .custom-radio-group
+        :deep(.el-radio-button:last-child .el-radio-button__inner) {
+        margin-right: 0;
+    }
+    .custom-radio-group :deep(.el-radio-button__inner:hover) {
+        background-color: #e5e7eb;
+        color: #1f2937;
+    }
+    .custom-radio-group
+        :deep(
+            .el-radio-button__original-radio:checked
+                + .el-radio-button__inner:hover
+        ) {
+        background-color: #2563eb;
+        color: #fff;
+    }
+
+    /* 自定义按钮样式 */
+    .custom-btn-primary {
+        background-color: #3b82f6;
+        border-color: #3b82f6;
+        color: #fff;
+        padding: 8px 20px;
+    }
+    .custom-btn-primary:hover,
+    .custom-btn-primary:focus {
+        background-color: #2563eb;
+        border-color: #2563eb;
+    }
+
+    .custom-btn-info {
+        background-color: #ffffff;
+        border-color: #d1d5db;
+        color: #4b5563;
+        padding: 8px 20px;
+    }
+    .custom-btn-info:hover,
+    .custom-btn-info:focus {
+        background-color: #f9fafb;
+        border-color: #9ca3af;
+        color: #1f2937;
+    }
+</style>

+ 14 - 9
src/views/workOrder/recycle/index.vue

@@ -1,7 +1,11 @@
 <template>
     <ele-page flex-table>
         <!-- Search Component -->
-        <PageSearch @search="handleSearch" :myPendingCount="myPendingCount" />
+        <PageSearch
+            @search="handleSearch"
+            :myPendingCount="myPendingCount"
+            orderType="recycle"
+        />
 
         <!-- Common Table -->
         <common-table
@@ -133,7 +137,7 @@
     } from 'vue';
     import { ElMessage, ElMessageBox } from 'element-plus';
     import CommonTable from '@/components/CommonPage/CommonTable.vue';
-    import PageSearch from '@/views/workOrder/sell/components/page-search.vue';
+    import PageSearch from '../sell/components/page-search.vue';
     import EditDialog from '@/views/workOrder/sell/components/edit-dialog.vue';
     import { useUserStore } from '@/store/modules/user';
     import { Plus, Check } from '@element-plus/icons-vue';
@@ -161,17 +165,18 @@
     // Column Definitions
     const columns = ref([
         { type: 'selection', width: 50, align: 'center' },
-        { prop: 'id', label: '工单任务编号', width: 120, align: 'center' },
+        { prop: 'id', label: '工单任务编号', width: 110, align: 'center' },
+        { prop: 'orderId', label: '订单编号', minWidth: 100, align: 'center' },
         {
-            prop: 'taskTypeName',
-            label: '任务类型',
-            width: 120,
+            prop: 'waybillCode',
+            label: '运单号',
+            minWidth: 140,
             align: 'center'
         },
         {
-            prop: 'inspectionStatusName',
-            label: '验货状态',
-            width: 100,
+            prop: 'taskTypeName',
+            label: '任务类型',
+            width: 120,
             align: 'center'
         },
         { prop: 'statusName', label: '状态', width: 100, align: 'center' },

+ 66 - 35
src/views/workOrder/sell/components/page-search.vue

@@ -32,13 +32,17 @@
 </template>
 
 <script setup>
-    import { reactive, defineEmits, defineProps, ref } from 'vue';
+    import { reactive, defineEmits, defineProps, ref, computed } from 'vue';
     import ProSearch from '@/components/CommonPage/ProSearch2.vue';
 
     const props = defineProps({
         myPendingCount: {
             type: Number,
             default: 0
+        },
+        orderType: {
+            type: String,
+            default: 'sell' // 'sell' 或 'recycle'
         }
     });
 
@@ -47,41 +51,68 @@
     const proSearchRef = ref(null);
     const myHandleWorkOrder = ref(0);
 
-    const formItems = reactive([
-        {
-            type: 'dictSelect',
-            label: '任务状态',
-            prop: 'status',
-            props: { code: 'work_order_status' },
-            colProps: { span: 3 }
-        },
-        {
-            type: 'dictSelect',
-            label: '任务类型',
-            prop: 'taskType',
-            props: { code: 'task_type' },
-            colProps: { span: 3 }
-        },
-        {
-            type: 'dictSelect',
-            label: '验货状态',
-            prop: 'inspectionStatus',
-            props: { code: 'inspection_status' },
-            colProps: { span: 3 }
-        },
-        {
-            type: 'input',
-            label: '运单号',
-            prop: 'waybillCode',
-            placeholder: '请输入运单号'
-        },
-        {
-            type: 'datetimerange',
-            label: '创建时间',
-            prop: 'createTime',
-            colProps: { span: 6 } // Make date range wider
+    const formItems = computed(() => {
+        const items = [
+            {
+                type: 'dictSelect',
+                label: '任务状态',
+                prop: 'status',
+                props: { code: 'work_order_status' },
+                colProps: { span: 3 }
+            },
+            {
+                type: 'dictSelect',
+                label: '任务类型',
+                prop: 'taskType',
+                props: {
+                    code:
+                        props.orderType === 'sell'
+                            ? 'sell_task_type'
+                            : 'task_type'
+                },
+                colProps: { span: 3 }
+            }
+        ];
+
+        // 验货状态只在卖书工单显示
+        if (props.orderType === 'sell') {
+            items.push({
+                type: 'dictSelect',
+                label: '验货状态',
+                prop: 'inspectionStatus',
+                props: { code: 'inspection_status' },
+                colProps: { span: 3 }
+            });
         }
-    ]);
+
+        // 订单编号只在回收工单显示
+        if (props.orderType === 'recycle') {
+            items.push({
+                type: 'input',
+                label: '订单编号',
+                prop: 'orderId',
+                placeholder: '请输入订单编号',
+                colProps: { span: 3 }
+            });
+        }
+
+        items.push(
+            {
+                type: 'input',
+                label: '运单号',
+                prop: 'waybillCode',
+                placeholder: '请输入运单号'
+            },
+            {
+                type: 'datetimerange',
+                label: '创建时间',
+                prop: 'createTime',
+                colProps: { span: 6 } // Make date range wider
+            }
+        );
+
+        return items;
+    });
 
     const toggleMyPending = () => {
         myHandleWorkOrder.value = myHandleWorkOrder.value === 0 ? 1 : 0;

+ 11 - 1
src/views/workOrder/sell/index.vue

@@ -1,7 +1,11 @@
 <template>
     <ele-page flex-table>
         <!-- Search Component -->
-        <PageSearch @search="handleSearch" :myPendingCount="myPendingCount" />
+        <PageSearch
+            @search="handleSearch"
+            :myPendingCount="myPendingCount"
+            orderType="sell"
+        />
 
         <!-- Common Table -->
         <common-table
@@ -162,6 +166,12 @@
     const columns = ref([
         { type: 'selection', width: 50, align: 'center' },
         { prop: 'id', label: '工单任务编号', width: 120, align: 'center' },
+        {
+            prop: 'waybillCode',
+            label: '运单号',
+            minWidth: 120,
+            align: 'center'
+        },
         {
             prop: 'taskTypeName',
             label: '任务类型',