Ver Fonte

0818-0219-h01

Zory há 1 mês atrás
pai
commit
8adcfc6328

+ 33 - 0
src/api/model/goodsoff.js

@@ -0,0 +1,33 @@
+import config from "@/config";
+import http from "@/utils/request";
+
+export default {
+    list: {
+        url: `${config.API_URL}/merchant/goodsoff/list`,
+        name: "-",
+        get: async function (params) {
+            return await http.get(this.url, params);
+        },
+    },
+    save: {
+        url: `${config.API_URL}/merchant/goodsoff/save`,
+        name: "-",
+        post: async function (params) {
+            return await http.post(this.url, params);
+        },
+    },
+    batch: {
+        url: `${config.API_URL}/merchant/goodsoff/batch`,
+        name: "-",
+        post: async function (params) {
+            return await http.post(this.url, params);
+        },
+    },
+    del: {
+        url: `${config.API_URL}/merchant/goodsoff/del`,
+        name: "-",
+        post: async function (params) {
+            return await http.post(this.url, params);
+        },
+    },
+}

+ 40 - 0
src/api/model/serChat.js

@@ -0,0 +1,40 @@
+import config from "@/config";
+import http from "@/utils/request";
+
+export default {
+    user: {
+        url: `${config.API_URL}/chat/user`,
+        name: "-",
+        get: async function (data = {}) {
+            return await http.get(this.url, data);
+        },
+    },
+    msg: {
+        url: `${config.API_URL}/chat/msg`,
+        name: "-",
+        get: async function (data = {}) {
+            return await http.get(this.url, data);
+        },
+    },
+    text: {
+        url: `${config.API_URL}/chat/text`,
+        name: "-",
+        post: async function (data = {}) {
+            return await http.post(this.url, data);
+        },
+    },
+    image: {
+        url: `${config.API_URL}/chat/image`,
+        name: "-",
+        post: async function (data = {}) {
+            return await http.post(this.url, data);
+        },
+    },
+    msgOrder: {
+        url: `${config.API_URL}/chat/order`,
+        name: "-",
+        get: async function (data = {}) {
+            return await http.get(this.url, data);
+        },
+    },
+}

+ 5 - 1
src/components/imFloat/order.vue

@@ -27,6 +27,8 @@
                         <div class="status-name" v-if="orderData.status == 3">已退款</div>
                         <div class="status-name" v-if="orderData.status == 4">退款中</div>
                         <div class="status-name" v-if="orderData.status == 5">已关闭</div>
+                        <div class="status-name" v-if="orderData.status == 6">部分退款</div>
+                        <div class="status-name" v-if="orderData.status == 7">履约中</div>
                         
                         <div class="status-desc" v-if="orderData.status == 0">超过30分钟未支付,订单将自动取消</div>
                         <div class="status-desc" v-if="orderData.status == 1">待使用</div>
@@ -34,6 +36,8 @@
                         <div class="status-desc" v-if="orderData.status == 3">{{ $TOOL.money(orderData.pay_money) }} 已原路退回</div>
                         <div class="status-desc" v-if="orderData.status == 4">已申请退款,钱款预计1-3个自然日到账</div>
                         <div class="status-desc" v-if="orderData.status == 5">订单已取消或已关闭</div>
+                        <div class="status-desc" v-if="orderData.status == 6">部分退款中</div>
+                        <div class="status-desc" v-if="orderData.status == 7">货物运输中</div>
                         
                     </div>
                     <div class="order-goods">
@@ -44,7 +48,7 @@
                                 <div class="time" v-if="orderData.end_at">有效期至:{{ orderData.end_at }} </div>
                                 <div class="num">x {{ orderData.number }}</div>
                             </div>
-                            <div class="total">合计:{{ $TOOL.money(orderData.price) }}</div>
+                            <div class="total">合计:{{ $TOOL.money(orderData.pay_money) }}</div>
                         </div>
                     </div>
                     <div class="order-info">

+ 2 - 1
src/views/merchant/components/goods.vue

@@ -160,7 +160,8 @@ export default{
             mode:"add",
             titleMap:{
                 add:"添加推广团购",
-                edit:"添加推广团购"
+                edit:"添加推广团购",
+                off:"关联团购"
             },
             list: {
                 apiObj: this.$API.merGoods.list

+ 15 - 10
src/views/merchant/goods/add.vue

@@ -21,13 +21,17 @@
                             <template v-if="showNext">
                             <el-card shadow="never" header="商品信息">
                                 <el-form-item label="商品名称" prop="product_name">
-                                    <el-input v-model="formData.product_name" maxlength="40" show-word-limit clearable placeholder="请输入"></el-input>
-                                    <div class="el-form-item-msg">限制40个字符,一个汉字等于两个字符</div>
+                                    <el-input v-model="formData.product_name" maxlength="10" show-word-limit clearable placeholder="请输入类目信息,如服装类">
+                                        <template #prefix>
+                                            <div style="margin-right: 10px;">{{ formData.price?formData.price:'0.00' }}元代{{ formData.line_price?formData.line_price:'0.00' }}元商场权益券</div>
+                                        </template>
+                                    </el-input>
+                                    <div class="el-form-item-msg"></div>
                                 </el-form-item>
                                 <el-row :gutter="15">
                                     <el-col :span="formData.product_type==11?8:12">
                                         <el-form-item label="顾客实际需支付" prop="price">
-                                            <el-input v-model="formData.price" type="number" clearable placeholder="请输入">
+                                            <el-input v-model="formData.price" min="0" type="number" clearable placeholder="请输入">
                                                 <template #append>元</template>
                                             </el-input>
                                             <div class="el-form-item-msg"></div>
@@ -35,7 +39,7 @@
                                     </el-col>
                                     <el-col :span="formData.product_type==11?8:12">
                                         <el-form-item label="划线价" prop="line_price">
-                                            <el-input v-model="formData.line_price" type="number" clearable placeholder="请输入">
+                                            <el-input v-model="formData.line_price" min="0" type="number" clearable placeholder="请输入">
                                                 <template #append>元</template>
                                             </el-input>
                                             <div class="el-form-item-msg"></div>
@@ -189,7 +193,6 @@
                             <el-card shadow="never" header="售卖信息">
                                 <el-form-item label="投放渠道" prop="show_channel">
                                     <el-radio-group v-model="formData.show_channel">
-                                        <el-radio :label="1">不限制</el-radio>
                                         <el-popover placement="top-start" trigger="hover" content="用户仅可在直播间开播时支付购买,售价及库存均仅在直播间渠道生效。用户无法通过收藏或分享的商品链接支付购买。如希望以此售价通过多渠道分享和售卖,投放渠道需选择“不限制”。" :width="280">
                                             <template #reference>
                                                 <el-radio :label="2">
@@ -319,7 +322,7 @@
                                     <div class="el-form-item-msg"></div>
                                 </el-form-item>
                                 <el-form-item label="顾客不可消费日期" prop="can_no_use_date">
-                                    <el-radio-group v-model="formData.can_no_use_date">
+                                    <el-radio-group v-model="formData.can_no_use_date" disabled>
                                         <el-radio :label="1">所有日期均可使用</el-radio>
                                         <el-radio :label="2">部分日期不可用</el-radio>
                                     </el-radio-group>
@@ -401,7 +404,7 @@
                                     </div>
                                 </el-form-item>
                                 <el-form-item label="预约规则" prop="booking_type">
-                                    <el-radio-group v-model="formData.booking_type">
+                                    <el-radio-group v-model="formData.booking_type" disabled>
                                         <el-radio :label="1">无需预约</el-radio>
                                         <el-radio :label="2">到店前需要预约</el-radio>
                                     </el-radio-group>
@@ -432,7 +435,7 @@
                                     </div>
                                 </el-form-item>
                                 <el-form-item label="库存方式" prop="stock_type">
-                                    <el-radio-group v-model="formData.stock_type">
+                                    <el-radio-group v-model="formData.stock_type" disabled>
                                         <el-radio :label="1">付款减库存</el-radio>
                                         <el-radio :label="2">核销减库存</el-radio>
                                     </el-radio-group>
@@ -569,7 +572,7 @@ export default {
                 customer_reserved_info:2,
                 auto_renew:1,
                 use_date_type:1,
-                day_duration:180,
+                day_duration:200,
                 can_no_use_date:1,
                 code_source_type:1,
                 limit_rule_type:1,
@@ -583,7 +586,7 @@ export default {
                 detail_image_list:[],
                 environment_image_list:[],
                 mult_sku:1,
-                stock_type:1
+                stock_type:2
             },
             holiday:[{'key':1,"name":"元旦"},{'key':2,"name":"春节"},{'key':3,"name":"清明"},{'key':4,"name":"劳动节"},{'key':5,"name":"端午节"},{'key':6,"name":"中秋节"},{'key':7,"name":"国庆节"},{'key':8,"name":"情人节"},{'key':9,"name":"圣诞节"},{'key':10,"name":"元宵节"},{'key':11,"name":"520"},{'key':12,"name":"儿童节"},{'key':13,"name":"七夕节"},{'key':14,"name":"二月二"},{'key':15,"name":"母亲节"},{'key':16,"name":"父亲节"},{'key':17,"name":"教师节"}],
             weeksData:[{'key':1,"name":"星期一"},{'key':2,"name":"星期二"},{'key':3,"name":"星期三"},{'key':4,"name":"星期四"},{'key':5,"name":"星期五"},{'key':6,"name":"星期六"},{'key':7,"name":"星期天"},],
@@ -897,4 +900,6 @@ export default {
 .el-dialog__wrapper {
     z-index: 9999 !important;
 }
+.flex-title{display: flex;align-items: center;gap: 10px;}
+.flex-title .name{flex: 1;}
 </style>

+ 22 - 9
src/views/merchant/goods/edit.vue

@@ -24,13 +24,17 @@
                             <template v-if="showNext">
                             <el-card shadow="never" header="商品信息">
                                 <el-form-item label="商品名称" prop="product_name">
-                                    <el-input v-model="formData.product_name" maxlength="40" show-word-limit clearable placeholder="请输入"></el-input>
-                                    <div class="el-form-item-msg">限制40个字符,一个汉字等于两个字符</div>
+                                    <el-input v-model="formData.product_name" maxlength="10" show-word-limit clearable placeholder="请输入类目信息,如服装类">
+                                        <template #prefix>
+                                            <div style="margin-right: 10px;">{{ formData.price?formData.price:'0.00' }}元代{{ formData.line_price?formData.line_price:'0.00' }}元商场权益券</div>
+                                        </template>
+                                    </el-input>
+                                    <div class="el-form-item-msg"></div>
                                 </el-form-item>
                                 <el-row :gutter="15">
                                     <el-col :span="formData.product_type==11?8:12">
                                         <el-form-item label="顾客实际需支付" prop="price">
-                                            <el-input v-model="formData.price" type="number" clearable placeholder="请输入">
+                                            <el-input v-model="formData.price" min="0" type="number" clearable placeholder="请输入">
                                                 <template #append>元</template>
                                             </el-input>
                                             <div class="el-form-item-msg"></div>
@@ -38,7 +42,7 @@
                                     </el-col>
                                     <el-col :span="formData.product_type==11?8:12">
                                         <el-form-item label="划线价" prop="line_price">
-                                            <el-input v-model="formData.line_price" type="number" clearable placeholder="请输入">
+                                            <el-input v-model="formData.line_price" min="0" type="number" clearable placeholder="请输入">
                                                 <template #append>元</template>
                                             </el-input>
                                             <div class="el-form-item-msg"></div>
@@ -46,7 +50,7 @@
                                     </el-col>
                                     <el-col :span="8" v-if="formData.product_type==11">
                                         <el-form-item label="差价金额" prop="custom_price">
-                                            <el-input v-model="formData.custom_price" type="number" clearable placeholder="请输入">
+                                            <el-input v-model="formData.custom_price" min="0" type="number" clearable placeholder="请输入">
                                                 <template #append>元</template>
                                             </el-input>
                                             <div class="el-form-item-msg"></div>
@@ -192,7 +196,6 @@
                             <el-card shadow="never" header="售卖信息">
                                 <el-form-item label="投放渠道" prop="show_channel">
                                     <el-radio-group v-model="formData.show_channel">
-                                        <el-radio :label="1">不限制</el-radio>
                                         <el-popover placement="top-start" trigger="hover" content="用户仅可在直播间开播时支付购买,售价及库存均仅在直播间渠道生效。用户无法通过收藏或分享的商品链接支付购买。如希望以此售价通过多渠道分享和售卖,投放渠道需选择“不限制”。" :width="280">
                                             <template #reference>
                                                 <el-radio :label="2">
@@ -321,7 +324,7 @@
                                     </el-radio-group>
                                     <div class="el-form-item-msg"></div>
                                 </el-form-item>
-                                <el-form-item label="顾客不可消费日期" prop="can_no_use_date">
+                                <el-form-item label="顾客不可消费日期" prop="can_no_use_date" disabled>
                                     <el-radio-group v-model="formData.can_no_use_date">
                                         <el-radio :label="1">所有日期均可使用</el-radio>
                                         <el-radio :label="2">部分日期不可用</el-radio>
@@ -397,7 +400,7 @@
                                     </div>
                                 </el-form-item>
                                 <el-form-item label="预约规则" prop="booking_type">
-                                    <el-radio-group v-model="formData.booking_type">
+                                    <el-radio-group v-model="formData.booking_type" disabled>
                                         <el-radio :label="1">无需预约</el-radio>
                                         <el-radio :label="2">到店前需要预约</el-radio>
                                     </el-radio-group>
@@ -427,6 +430,16 @@
                                         </el-input>
                                     </div>
                                 </el-form-item>
+                                <el-form-item label="库存方式" prop="stock_type">
+                                    <el-radio-group v-model="formData.stock_type" disabled>
+                                        <el-radio :label="1">付款减库存</el-radio>
+                                        <el-radio :label="2">核销减库存</el-radio>
+                                    </el-radio-group>
+                                    <div class="el-form-item-msg">
+                                        <span v-if="formData.stock_type==1">用户在小程序支付成功后,已售数量增加,库存减少,不限库存不受影响</span>
+                                        <span v-if="formData.stock_type==2">用户在完成订单核销后,已售数量增加,库存减少,不限库存不受影响</span>
+                                    </div>
+                                </el-form-item>
                             </el-card>
                             <el-card shadow="never" header="其他详情">
                                 <el-form-item label="详情内容" prop="content">
@@ -568,7 +581,7 @@ export default {
                 detail_image_list:[],
                 environment_image_list:[],
                 mult_sku:1,
-                stock_type:1
+                stock_type:2
             },
             holiday:[{'key':1,"name":"元旦"},{'key':2,"name":"春节"},{'key':3,"name":"清明"},{'key':4,"name":"劳动节"},{'key':5,"name":"端午节"},{'key':6,"name":"中秋节"},{'key':7,"name":"国庆节"},{'key':8,"name":"情人节"},{'key':9,"name":"圣诞节"},{'key':10,"name":"元宵节"},{'key':11,"name":"520"},{'key':12,"name":"儿童节"},{'key':13,"name":"七夕节"},{'key':14,"name":"二月二"},{'key':15,"name":"母亲节"},{'key':16,"name":"父亲节"},{'key':17,"name":"教师节"}],
             weeksData:[{'key':1,"name":"星期一"},{'key':2,"name":"星期二"},{'key':3,"name":"星期三"},{'key':4,"name":"星期四"},{'key':5,"name":"星期五"},{'key':6,"name":"星期六"},{'key':7,"name":"星期天"},],

+ 137 - 0
src/views/merchant/goods/off/components/form.vue

@@ -0,0 +1,137 @@
+<template>
+    <el-drawer v-model="visible" size="800px" destroy-on-close :close-on-click-modal="false" :with-header="false">
+        <el-container class="flex-column">
+            <div class="drawer-detail-main">
+                <div class="drawer-detail-header">
+                    <div class="drawer-detail-header-body">
+                        <div class="drawer-detail-header-left">{{titleMap[mode]}}</div>
+                        <div class="drawer-detail-header-left">
+                            <el-button type="default" icon="el-icon-close" @click="visible=false"></el-button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <el-main>
+                <el-form ref="dialogForm" :model="formData" :rules="rules" label-width="100px" label-position="top">
+                    <el-form-item label="关联团单" prop="product_name">
+                        <el-input v-model="formData.product_name" placeholder="请选择团单" clearbale readonly :style="{width:'100%'}" @click="selectStore">
+                            <template #append>
+                                <el-tooltip effect="dark" content="点这里,清除选择" placement="top-start">
+                                    <div class="remove-a" @click="clearStore">清除</div>
+                                </el-tooltip>
+                            </template>
+                        </el-input>
+                        <div class="el-form-item-msg"></div>
+                    </el-form-item>
+                    <el-form-item label="商品标题" prop="title">
+                        <el-input v-model="formData.title" clearable placeholder="请输入"></el-input>
+                        <div class="el-form-item-msg"></div>
+                    </el-form-item>
+                    <el-form-item label="商品主图" prop="image">
+                        <sc-upload v-model="formData.image" tip="图片尺寸不超过5M,图片越小呈现的速度越快,建议分辨率不低于750*562px"></sc-upload>
+                    </el-form-item>
+                    <el-form-item label="状态" prop="status">
+                        <el-radio-group v-model="formData.status">
+                            <el-radio-button :label="1">正常</el-radio-button>
+                            <el-radio-button :label="0">禁用</el-radio-button>
+                        </el-radio-group>
+                        <div class="el-form-item-msg"></div>
+                    </el-form-item>
+                </el-form>
+            </el-main>
+            <el-footer>
+                <el-button @click="visible=false" size="large">取 消</el-button>
+                <el-button v-if="mode!='show'" size="large" type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
+            </el-footer>
+        </el-container>
+    </el-drawer>
+    <goodsMain ref="goodsMain" :multiple="false" @success="handleGoods"></goodsMain>
+</template>
+
+<script>
+import goodsMain from '@/views/merchant/components/goods';
+export default {
+    components: {
+        goodsMain
+    },
+    data(){
+        return {
+            goodsData:[],
+            starData:[],
+            loading: false,
+            isSaveing: false,
+            mode:"add",
+            titleMap:{
+                add:"新增核销商品",
+                edit:"编辑核销商品"
+            },
+            visible:false,
+            formData:{
+                status:1
+            },
+            rules:{
+                status: [
+                    {required: true, message: '请选择'}
+                ],
+                title: [
+                    {required: true, message: '请输入'}
+                ],
+                image: [
+                    {required: true, message: '请上传'}
+                ],
+                product_name: [
+                    {required: true, message: '请输入'}
+                ]
+            }
+        }
+    },
+    methods:{
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        //表单注入数据
+        setData(data){
+            if (this.mode == 'edit') {
+                data.goods_id = data.goods.goods_id;
+                data.product_name = data.goods.product_name;
+            }
+            this.formData = JSON.parse(JSON.stringify(data));
+        },
+        selectStore(){
+            this.$nextTick(()=>{
+                this.$refs.goodsMain.open('off')
+            })
+        },
+        handleGoods(data){
+            this.formData.goods_id = data.goods_id;
+            this.formData.product_name = data.product_name;
+        },
+        clearStore(){
+            this.formData.goods_id = "";
+            this.formData.product_name = "";
+        },
+        async submit(){
+            var validate = await this.$refs.dialogForm.validate().catch(()=>{});
+            if(!validate){ return false }
+            this.isSaveing = true;
+            let submitData = JSON.parse(JSON.stringify(this.formData));
+            var resp = await this.$API.goodsoff.save.post(submitData);
+            this.isSaveing = false;
+            if (resp.code == 0) {
+                return this.$message.error(resp.msg);
+            }
+            this.$message.success(resp.msg);
+            this.visible = false;
+            this.formData = {};
+            this.$emit("success");
+        }
+    }
+}
+</script>
+
+<style>
+.text-center{text-align: center;}
+.goods-live{width: 100%;margin-top: 10px;}
+</style>

+ 75 - 0
src/views/merchant/goods/off/components/option.vue

@@ -0,0 +1,75 @@
+<template>
+    <fieldset>
+        <legend>
+            <el-tag type="info">按需操作</el-tag>
+        </legend>
+        <div class="op-header">
+            <div class="left-panel">
+                <el-button type="primary" icon="el-icon-plus" @click="table_add()">创建核销商品</el-button>
+            </div>
+        </div>
+    </fieldset>
+    <formMain ref="formMain" @success="handleSuccess"></formMain>
+</template>
+
+<script>
+import formMain from './form';
+export default {
+    components: {
+        formMain
+    },
+    props: {
+        dataSelect: { type: Array, default: () => [] },
+        dataSelectFull: { type: Array, default: () => [] }
+    },
+    data(){
+        return {
+
+        }
+    },
+    methods: {
+        table_add(){
+            this.$nextTick(() => {
+                this.$refs.formMain.open("add")
+            })
+        },
+        table_sync(){
+            this.$confirm(`发起同步后,请耐心等待1-2分钟后再刷新当前数据列表`, '提示', {
+                type: 'warning'
+            }).then(async () => {
+                var resp = await this.$API.category.sync.get();
+                if (resp.code == 0) {
+                    return this.$message.error(resp.msg);
+                }
+                this.$message.success(resp.msg);
+                this.$emit("success");
+            }).catch(() => {
+
+            })
+        },
+        table_batch_status(status){
+            if (this.dataSelect.length == 0) {
+                return this.$message.error("请选择修改数据")
+            }
+            this.$confirm(`审核中的商品不支持该操作,请确认是否有勾选审核中的商品`, '提示', {
+                type: 'warning'
+            }).then(async () => {
+                var loading = this.$loading()
+                let submitData = {"id":this.dataSelect,"value":status,"field":"status","type":"batch"};
+                loading.close()
+                var resp = await this.$API.merGoods.batch.post(submitData);
+                if (resp.code == 0) {
+                    return this.$message.error(resp.msg);
+                }
+                this.$message.success(resp.msg);
+                this.$emit("success");
+            }).catch(() => {
+
+            })
+        },
+        handleSuccess(){
+            this.$emit("success");
+        }
+    }
+}
+</script>

+ 49 - 0
src/views/merchant/goods/off/components/search.vue

@@ -0,0 +1,49 @@
+<template>
+    <fieldset>
+        <legend>
+            <el-tag type="info">条件筛选</el-tag>
+        </legend>
+        <el-form class="lv-form-inline" ref="searchForm" :model="searchKey" label-position="right" label-width="100px">
+            <div class="search-form">
+                <div class="form-left">
+                    <el-row :gutter="10">
+                        <el-col :xs="12" :sm="12" :md="12" :lg="6" :xl="4">
+                            <el-input v-model="searchKey.title" placeholder="商品名称" clearable :style="{ width: '100%' }" @keyup.enter="searchForm()">
+                                <template #prepend>商品名称</template>
+                            </el-input>
+                        </el-col>
+                        <el-col :xs="12" :sm="12" :md="12" :lg="6" :xl="4">
+                            <el-select v-model="searchKey.status" clearable placeholder="请选择状态" @change="searchForm" :style="{width: '100%'}" class="diy-select">
+                                <el-option value="1" label="已冻结"></el-option>
+                                <el-option value="2" label="正常"></el-option>
+                                <template #prefix>状态</template>
+                            </el-select>
+                        </el-col>
+                        <el-col :xs="12" :sm="6" :md="6" :lg="6" :xl="4">
+                            <el-date-picker v-model="searchKey.create" :style="{width: '100%'}" placeholder="请选择创建时间" start-placeholder="开始时间" end-placeholder="结束时间" type="daterange" range-separator="至" @change="searchForm" />
+                        </el-col>
+                    </el-row>
+                </div>
+                <div class="form-line"></div>
+                <div class="form-right">
+                    <el-button type="primary" icon="el-icon-search" @click="searchForm">搜索</el-button>
+                </div>
+            </div>
+        </el-form>
+    </fieldset>
+</template>
+
+<script>
+export default {
+    data(){
+        return {
+            searchKey:{}
+        }
+    },
+    methods: {
+        searchForm(){
+            this.$emit("success",this.searchKey);
+        }
+    }
+}
+</script>

+ 166 - 0
src/views/merchant/goods/off/components/table.vue

@@ -0,0 +1,166 @@
+<template>
+    <scTable ref="table" :apiObj="list.apiObj" :params="searchKey" @selectionChange="selectionChange" row-key="id">
+        <el-table-column type="selection" width="50" fixed="left"></el-table-column>
+        <el-table-column label="商品信息" width="340" prop="name">
+            <template #default="scope">
+                <div class="goods-img">
+                    <div class="img">
+                        <el-image 
+                        style="width: 54px; height: 54px"
+                        :src="scope.row.image"
+                        :zoom-rate="1.2"
+                        :max-scale="7"
+                        :min-scale="0.2"
+                        :preview-src-list="[scope.row.image]"
+                        preview-teleported
+                        z-index="999"
+                        fit="cover"></el-image>
+                    </div>
+                    <div class="name">
+                        <span>{{ scope.row.title }}</span>
+                    </div>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="团单信息" width="340" prop="name">
+            <template #default="scope">
+                <div class="goods-img">
+                    <div class="img">
+                        <el-image 
+                        style="width: 54px; height: 54px"
+                        :src="scope.row.goods.image_list[0].url"
+                        :zoom-rate="1.2"
+                        :max-scale="7"
+                        :min-scale="0.2"
+                        :preview-src-list="[scope.row.goods.image_list[0].url]"
+                        preview-teleported
+                        z-index="999"
+                        fit="cover"></el-image>
+                    </div>
+                    <div class="name">
+                        <span>{{ scope.row.goods.product_name }}</span>
+                        <span class="dec">商品ID:{{ scope.row.goods.goods_id }}</span>
+                    </div>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="价格" width="200" prop="id">
+            <template #default="scope">
+                <div class="price">{{ $TOOL.moneyFormat(scope.row.goods.price) }}</div>
+                <div class="goods-price">
+                    <div class="del">{{ $TOOL.moneyFormat(scope.row.goods.line_price) }}</div>
+                    <span>{{ $TOOL.disFormat(scope.row.goods.price,scope.row.goods.line_price) }}折</span></div>
+            </template>
+        </el-table-column>
+        <el-table-column label="状态" prop="enable" width="120" align="center">
+            <template #default="scope">
+                <div class="status-success" v-if="scope.row.status==1"><sc-status-indicator type="success"></sc-status-indicator> 正常</div>
+                <div class="status-danger" v-if="scope.row.status==0"><sc-status-indicator type="danger"></sc-status-indicator> 已冻结</div>
+            </template>
+        </el-table-column>
+        <el-table-column label="创建时间" prop="create_at" width="180"></el-table-column>
+        <el-table-column label="操作" width="200" align="left" fixed="right">
+            <template #default="scope">
+                <el-button-group>
+                    <el-button size="small" text @click="table_edit(scope.row)">编辑</el-button>
+                    <el-button type="danger" text size="small" @click="table_del(scope.row)">删除</el-button>
+                </el-button-group>
+            </template>
+        </el-table-column>
+    </scTable>
+    <formMain ref="formMain" @success="handleSuccess"></formMain>
+</template>
+
+<script>
+import formMain from './form';
+export default {
+    components: {
+        formMain
+    },
+    data(){
+        return {
+            list: {
+                apiObj: this.$API.goodsoff.list
+            },
+            dataSelect:[],
+            dataSelectFull:[],
+            searchKey:{}
+        }
+    },
+    
+    methods: {
+        table_edit(data){
+            this.$nextTick(() => {
+                this.$refs.formMain.open("edit").setData(data)
+            })
+        },
+        table_del(data){
+            this.$confirm(`删除后,核销订单关联数据将保留,该商品信息无法再次编辑`, '提示', {
+                type: 'warning'
+            }).then(async ()=>{
+                var loading = this.$loading()
+                var resp = await this.$API.goodsoff.del.post({"id":data.id});
+                loading.close();
+                if (resp.code == 0) {
+                    return this.$message.error(resp.msg);
+                }
+                this.$message.success(resp.msg);
+                this.$refs.table.refresh()
+            }).catch(()=>{})
+        },
+        refresh(){
+            this.$refs.table.refresh()
+        },
+        upData(data){
+            this.$refs.table.upData(data)
+        },
+        handleSuccess(){
+            this.$refs.table.refresh()
+        },
+        sortChange(event){
+            if (event.order) {
+                var data = {
+                    "field":event.prop,
+                    "order":event.order
+                }
+                this.$refs.table.upData(data)
+            } else {
+                this.$refs.table.reload(this.searchKey)
+            }
+            return ;
+        },
+        selectionChange(event){
+            this.dataSelect = [];
+            var arr = [];
+            var arrCompany = [];
+            event.forEach(function(val,index){
+                arr[index] = val.id;
+                arrCompany[index] = val;
+            });
+            this.dataSelectFull = arrCompany;
+            this.dataSelect = arr;
+            this.$emit("success",this.dataSelect);
+            this.$emit("successFull",this.dataSelectFull);
+        },
+    }
+}
+</script>
+
+<style>
+.goods-img{display: flex;gap: 5px;}
+.goods-img .name span{display: block;}
+.goods-img .name span.dec{color: #999;font-size: 12px;}
+.goods-price,.price{display: flex;align-items: center;gap: 5px;font-size: 14px;}
+.goods-price .del{text-decoration: line-through;}
+.goods-price span{background-color: #fff !important;
+    border: 1px solid #d3d9e0 !important;
+    border-radius: 4px;
+    box-sizing: border-box;
+    color: #6c737a !important;
+    display: inline-flex;
+    flex: none;
+    font-size: 12px;
+    line-height: 18px !important;
+    padding: 1px 6px !important;text-decoration: none;}
+.flex{display: flex;align-items: center;gap: 5px;}
+</style>

+ 47 - 0
src/views/merchant/goods/off/index.vue

@@ -0,0 +1,47 @@
+<template>
+    <el-container class="flex-column">
+        <div class="table-search">
+            <search @success="handleSuccess"></search>
+            <optionBtn @success="handleSuccess" :dataSelect="dataSelect" :dataSelectFull="dataSelectFull" type="3"></optionBtn>
+        </div>
+        <el-main class="nopadding">
+            <div class="table-container">
+                <tablePage ref="tablePage" @success="tableHandle" @successFull="tableHandleFull" type="3"></tablePage>
+            </div>
+        </el-main>
+    </el-container>  
+</template>
+
+<script>
+import search from './components/search';
+import optionBtn from './components/option';
+import tablePage from './components/table';
+export default {
+    components: {
+        search,tablePage,optionBtn
+    },
+    data(){
+        return {
+            searchKey:{},
+            dataSelect:[],
+            dataSelectFull:[],
+        }
+    },
+    methods: {
+        tableHandle(data){
+            this.dataSelect = data;
+        },
+        tableHandleFull(data){
+            this.dataSelectFull = data;
+        },
+        handleSuccess(data){
+            this.$refs.tablePage.upData(data)
+        },
+        handleClick(name){
+            this.activeName = name;
+            this.searchKey.type = name;
+            this.$refs.tablePage.upData(this.searchKey)
+        }
+    }
+}
+</script>

+ 4 - 0
src/views/merchant/order/index/components/table.vue

@@ -79,6 +79,10 @@
                     <span>部分退款</span>
                     <span class="dec">已退款 <em style="color: #f00;">{{ scope.row.refund_num }}</em> 份</span>
                 </div>
+                <div class="order-name" v-if="scope.row.status == 7">
+                    <span>履约中</span>
+                    <span class="dec">货物运输中</span>
+                </div>
             </template>
         </el-table-column>
         <el-table-column label="佣金" prop="types" width="140" align="right">

+ 153 - 6
src/views/service/chat/components/aside.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="aside-user">
         <div class="user-info">
-            <div class="avatar"><img src="https://washmy.oss-cn-guangzhou.aliyuncs.com/storage/20260713/3f8a9da46c4c8e503e75f2cca4d8c172bdb7de97.jpg" /></div>
+            <div class="avatar"><img :src="userInfo.logo" /></div>
             <div class="info">
                 <div class="name">{{ userInfo.truename }}</div>
                 <div class="status">
@@ -36,13 +36,13 @@
                 <div class="info">用户昵称/聊天记录/订单号</div>
             </div>
             <div class="chat-tab">
-                <div class="chat-item-tab active">当前咨询</div>
-                <div class="chat-item-tab">历史咨询</div>
+                <div :class="chatType==1?'chat-item-tab active':'chat-item-tab'" @click="checkType(1)">当前咨询</div>
+                <div :class="chatType==2?'chat-item-tab active':'chat-item-tab'" @click="checkType(2)">历史咨询</div>
             </div>
         </div>
         <div class="infinite-list-wrapper" style="overflow: auto">
             <div>
-                <div class="chat-loading-sk" v-for="(item,indx) in 20" v-if="userLoading">
+                <div class="chat-loading-sk" v-for="(item,indx) in 10" v-if="chatUserList.loading && chatUserList.list.length == 0">
                     <el-skeleton :animated="true">
                         <template #template>
                             <div class="chat-sk-flex">
@@ -60,6 +60,27 @@
                         </template>
                     </el-skeleton>
                 </div>
+                <div v-infinite-scroll="load" v-else>
+                    <div :class="checkChat==index?'contact-item active':'contact-item'" v-for="(item,index) in chatUserList.list" :key="index" @click="checkChatItem(index)">
+                        <div class="img">
+                            <el-image :src="item.user.avatar" fit="contain" style="width: 40px; height: 40px"></el-image>
+                            <span class="last-num" v-if="item.last&&item.last.num > 0">{{item.last.num}}</span>
+                        </div>
+                        <div class="kefu-store">
+                            <div class="name">{{item.user?item.user.nickname:'-'}}</div>
+                            <div class="desc" v-if="item.last&&item.last.type=='text'">{{item.last.content}}</div>
+                            <div class="desc" v-if="item.last&&item.last.type=='order'">[订单消息]</div>
+                            <div class="desc" v-if="item.last&&item.last.type=='image'">[图片消息]</div>
+                            <div class="desc" v-if="item.last&&item.last.type=='video'">[视频消息]</div>
+                            <div class="desc" v-if="item.last&&item.last.type=='pay'">[支付消息]</div>
+                            <div class="desc" v-if="item.last&&item.last.type=='address'">[收集地址]</div>
+                        </div>
+                        <div class="time">
+                            {{item.lastTime}}
+                        </div>
+                    </div>
+                    <p class="loading-more" v-if="chatUserList.loading">Loading...</p>
+                </div>
             </div>
         </div>
     </div>
@@ -69,14 +90,127 @@
 export default {
     data () {
         return {
-            userLoading:true
+            userLoading:true,
+            chatUserList:{
+                page:1,
+                size:15,
+                loading:false,
+                list:[],
+                old:[],
+                end:true
+            },
+            chatType:1, // 1当前(今天)2历史
+            checkChat:0
         }
     },
     props: {
         userInfo: { type: Object, default: () => {} }
     },
+    created(){
+        this.loadMoreChat()
+    },
     methods: {
-
+        updateItem(data){
+            if (this.chatType == 2) {
+                this.chatUserList.list = []
+            }
+            this.chatType = 1;
+            this.sortItemData(data.item);
+            var msgUserData = this.chatUserList.list;
+            for (var i=0; i <= msgUserData.length;i ++) {
+                var item = msgUserData[i];
+                if (item && item.openid == data.openid) {
+                    item.last.type = data.type;
+                    item.last.content = this.truncateByBytes(data.content,12);
+                    item.last.create_at = data.create_at
+                    item.last.last_at = data.last_at
+                    item.last.num ++;
+                }
+            }
+        },
+        truncateByBytes(str, maxBytes) {
+            let count = 0;
+            let result = '';
+            for (const char of str) {
+                // 中文、全角符号算 2 字节,其他算 1 字节
+                const byteLen = char.match(/[^\x00-\xff]/) ? 2 : 1;
+                if (count + byteLen > maxBytes) break;
+                result += char;
+                count += byteLen;
+            }
+            return result + (count < str.length ? '...' : '');
+        },
+        load(){
+            if (this.chatUserList.end) return ;
+            this.loadMoreChat()
+        },
+        checkType(type){
+            this.chatType = type;
+            this.chatUserList = {
+                page:1,
+                size:15,
+                loading:false,
+                list:[],
+                old:[],
+                end:false
+            }
+            this.loadMoreChat();
+        },
+        async loadMoreChat(){
+            this.chatUserList.end = true;
+            this.chatUserList.loading = true;
+            var resp = await this.$API.serChat.user.get({page:this.chatUserList.page,pageSize:this.chatUserList.size,type:this.chatType})
+            this.chatUserList.loading = false;
+            if (resp.code == 0) {
+                return this.$message.error(resp.msg)
+            }
+            this.chatUserList.list = this.chatUserList.list.concat(resp.data.rows)
+            if (resp.data.total > this.chatUserList.list.length) {
+                this.chatUserList.page++;
+                this.chatUserList.end = false;
+            } else {
+                this.chatUserList.end = true;
+            }
+        },
+        checkChatItem(index){
+            this.checkChat = index;
+            if (this.chatUserList.list[index].last) {
+                this.chatUserList.list[index].last.num = 0;
+            }
+            this.$emit("chatItem",this.chatUserList.list[index],index)
+        },
+        sendSuccess(data,index) {
+            this.chatUserList.list[index].create_at = data.time;
+            this.chatUserList.list[index].last.type = data.type;
+            this.chatUserList.list[index].last.content = this.truncateByBytes(data.content,18);
+            if (this.chatUserList.list[index].last) {
+                this.chatUserList.list[index].last.num = 0;
+            }
+        },
+        sortItemData(data){
+            const existIdx = this.chatUserList.list.findIndex(item => item.id === data.id);
+            if (existIdx !== -1) { // 若已存在,按更新处理
+                this.handleNewMessage(data);
+                return;
+            }
+            // 新增数据到最顶部
+            this.chatUserList.list.unshift(data);
+        },
+        handleNewMessage(updateData) {
+            const idx = this.chatUserList.list.findIndex(item => item.id === updateData.id);
+            this.chatUserList.list.splice(idx, 1, updateData);
+            this.sortSessions();
+        },
+        sortSessions() {
+            this.chatUserList.list.sort((a, b) => {
+                const timeA = a.last_at && a.last_at !== '-' ? new Date(a.last_at).getTime() : 0;
+                const timeB = b.last_at && b.last_at !== '-' ? new Date(b.last_at).getTime() : 0;
+                return timeB - timeA;
+            });
+        },
+        sortBooks() {
+            this.chatUserList.list.sort((a, b) => b.last_at - a.last_at)
+        },
     }
 }
 </script>
@@ -93,8 +227,21 @@ export default {
     width: 300px;
     .infinite-list-wrapper{
         flex: 1;
+        width: 99%;
     }
 }
+.loading-more{line-height: 32px;text-align: center;}
+.contact-item{display: flex;align-items: center;background-color: #fff;padding: 10px;border-bottom: 1px solid #f8f8f8;cursor: pointer;}
+.contact-item.active{background-color: #f8f8f8;}
+.contact-item .img{position: relative;border: 1px solid #f8f8f8;border-radius: 5px;display: flex;align-items: center;justify-content: center;}
+.contact-item span.last-num{background-color: #f00;color: #fff;width: 20px;height: 20px;display: flex;align-items: center;justify-content: center;border-radius: 20px;font-size: 12px;margin-left: 10px;position: absolute;right: 0;top: 0;}
+
+.contact-item .kefu-store{margin-left: 10px;}
+.contact-item .kefu-store .name{font-size: 14px;color: #333;display: flex;align-items: center;}
+
+
+.contact-item .kefu-store .desc{font-size: 12px;color: #666;margin-top: 5px;}
+.contact-item .time{margin-left: auto;margin-right: 0;font-size: 12px;color: #666;}
 .chat-sk-flex{
     display: flex;align-items: center;gap: 10px;padding: 10px;flex: 1;border-bottom: 1px solid #f8f8f8;
     .sk-right{flex: 1;}

+ 261 - 1
src/views/service/chat/components/asideRight.vue

@@ -1,3 +1,263 @@
 <template>
-
+    <div v-if="!chatData"></div>
+    <div class="right-body" v-else>
+        <div class="right-body-left">
+            <template v-if="rightType == 2">
+                <el-container v-loading="orderData.loading">
+                    <el-header>
+                        <div class="order-search-input">
+                            <el-input v-model="orderData.order" placeholder="搜索订单编号..." style="width: 100%;" />
+                            <el-button @click="searchOrder">搜索</el-button>
+                        </div>
+                    </el-header>
+                    <el-main>
+                        <div class="order-list-chat" v-if="orderData.list.length > 0">
+                            <div class="m-order-s" v-for="(item,index) in orderData.list" :key="index">
+                                <div class="m-order-item" @click="viewOrder(item)">
+                                    <div class="order-img"><el-image style="width: 80px;height: 80px;" :src="item.img" fit="fit"></el-image></div>
+                                    <div class="order-info">
+                                        <div class="title">{{item.product.product_name}}</div>
+                                        <div class="desc" v-if="item.sku_name">规格:{{item.sku_name}}</div>
+                                        <div class="desc">{{item.order_sn}}</div>
+                                        <div class="price">
+                                            {{$TOOL.money(item.price)}}
+                                            <text v-if="item.custom_price > 0">差价:{{$TOOL.money(item.custom_price)}}</text>
+                                        </div>
+                                    </div>
+                                    <div class="order-status-btn">
+                                        <div class="status info" v-if="item.status=='0'">未支付</div>
+                                        <div class="status primary" v-if="item.status=='1'">待使用</div>
+                                        <div class="status success" v-if="item.status=='2'">已完成</div>
+                                        <div class="status danger" v-if="item.status=='3'">已退款</div>
+                                        <div class="status warn" v-if="item.status=='4'">退款中</div>
+                                        <div class="status warn" v-if="item.status=='5'">已关闭</div>
+                                        <div class="status success" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==1">差价已付</div>
+                                        <div class="status danger" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0">差价未付</div>
+                                    </div>
+                                </div>
+                                <div class="order-btn-group">
+                                    <div class="status primary cus" @click="sendAddress(item)" v-if="item.status=='1' && user.trade == '2'">发起预约</div>
+                                    <div class="status info cus" @click="editAddress(item)" v-if="item.status=='1' && user.trade == '2'">代客预约</div>
+                                    <!-- <div class="status warn cus" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0" @click="submitPrice(item)">补差价</div>
+                                    <div class="status danger cus" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0" @click="submitPrice(item)">改差价</div> -->
+                                    <div class="status danger cus" @click="sendOrder(item)">发送订单</div>
+                                    <div class="status primary cus" @click="doneOrder(item)" v-if="item.status=='1' && user.trade == '1'">手动核销</div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="order-list-chat" v-else>
+                            <el-empty description="没有订单" />
+                        </div>
+                    </el-main>
+                    <el-footer style="text-align: right;">
+                        <div class="order-page">
+                            <el-pagination small background :default-page-size="orderData.size" layout="prev, pager, next" @size-change="orderPageChange" @current-change="orderPageChange" :total="orderData.total" />
+                        </div>
+                    </el-footer>
+                </el-container>
+            </template>
+            <template v-if="rightType==1">
+                <div class="order-info-user">
+                    <div class="info-item">
+                        <div class="name">昵称</div>
+                        <div class="right">{{ chatData.user.nickname }} </div>
+                    </div>
+                    <div class="info-item">
+                        <div class="name">OpenId</div>
+                        <div class="right">{{ chatData.user.openid }} </div>
+                    </div>
+                    <div class="info-item">
+                        <div class="name">手机号码</div>
+                        <div class="right">{{ chatData.user.mobile }} </div>
+                    </div>
+                    <div class="info-item">
+                        <div class="name">注册时间</div>
+                        <div class="right">{{ chatData.user.create_at }} </div>
+                    </div>
+                </div>
+                <div class="order-info-user" v-if="msgOrder">
+                    <div class="title flex-title">咨询订单
+                        <el-button @click="getMsgOrder" icon="el-icon-refresh" circle />
+                    </div>
+                    <div class="m-order-s" style="margin-top: 15px;" v-loading="msgOrderLoad">
+                        <div class="m-order-item" @click="viewOrder(msgOrder)">
+                            <div class="order-img"><el-image style="width: 80px;height: 80px;" :src="msgOrder.img" fit="fit"></el-image></div>
+                            <div class="order-info">
+                                <div class="title" style="padding: 0;">{{msgOrder.product.product_name}}</div>
+                                <div class="desc" v-if="msgOrder.sku_name">{{msgOrder.sku_name?msgOrder.sku_name:'-'}}</div>
+                                <div class="desc">{{msgOrder.order_sn}}</div>
+                                <div class="price">
+                                    {{$TOOL.money(msgOrder.price)}}
+                                    <text v-if="msgOrder.custom_price > 0">差价:{{$TOOL.money(msgOrder.custom_price)}}</text>
+                                </div>
+                            </div>
+                            <div class="order-status-btn">
+                                <div class="status info" v-if="msgOrder.status=='0'">未支付</div>
+                                <div class="status primary" v-if="msgOrder.status=='1'">待使用</div>
+                                <div class="status success" v-if="msgOrder.status=='2'">已完成</div>
+                                <div class="status danger" v-if="msgOrder.status=='3'">已退款</div>
+                                <div class="status warn" v-if="msgOrder.status=='4'">退款中</div>
+                                <div class="status warn" v-if="msgOrder.status=='5'">已关闭</div>
+                                <div class="status success" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==1">差价已付</div>
+                                <div class="status danger" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0">差价未付</div>
+                            </div>
+                        </div>
+                        <div class="order-btn-group">
+                            <div class="status primary cus" @click="sendAddress(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '2'">发起预约</div>
+                            <div class="status info cus" @click="editAddress(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '2'">代客预约</div>
+                            <!-- <div class="status warn cus" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0" @click="submitPrice(msgOrder)">补差价</div> -->
+                            <!-- <div class="status danger cus" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0" @click="submitPrice(msgOrder)">改差价</div> -->
+                            <div class="status danger cus" @click="sendOrder(msgOrder)">发送订单</div>
+                            <div class="status primary cus" @click="doneOrder(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '1'">手动核销</div>
+                        </div>
+                    </div>
+                </div>
+            </template>
+        </div>
+        <div class="right-body-right">
+            <div :class="rightType==1?'right-item active':'right-item'" @click="checkRightType(1)">
+                <div class="icon"><el-icon size="22"><el-icon-user /></el-icon></div>
+                <div class="name">信息</div>
+            </div>
+            <div :class="rightType==2?'right-item active':'right-item'" @click="checkRightType(2)">
+                <div class="icon"><el-icon size="22"><el-icon-document /></el-icon></div>
+                <div class="name">订单</div>
+            </div>
+            <!-- <div :class="rightType==3?'right-item active':'right-item'" @click="checkRightType(3)">
+                <div class="icon"><el-icon size="22"><el-icon-reading /></el-icon></div>
+                <div class="name">快捷回复</div>
+            </div> -->
+        </div>
+    </div>
 </template>
+
+<script>
+export default {
+    data(){
+        return {
+            rightType:1,
+            msgOrder:null,
+            msgOrderLoad:false,
+            orderData:{
+                page:1,
+                size:10,
+                list:[],
+                loading:false,
+                total:0,
+                tab:"user",
+                order:""
+            },
+        }
+    },
+    props: {
+        userInfo: { type: Object, default: () => {} },
+        chatData: { type: Object, default: () => {} }
+    },
+    watch: {
+        chatData: {
+            handler(newVal) {
+                this.rightType = 1;
+                this.getMsgOrder()
+            },
+            immediate: true
+        }
+    },
+    methods: {
+        async getMsgOrder(){
+            if (!this.chatData) return ;
+            this.msgOrderLoad = true;
+            var resp = await this.$API.chat.msgOrder.get({"msgId":this.chatData.id})
+            this.msgOrderLoad = false;
+            if (resp.code == 0) return ;
+            this.msgOrder = resp.data.order;
+        },
+        checkRightType(type){
+            this.rightType = type;
+            if (type == 2) {
+                this.getOrderList()
+            }
+        },
+        async getOrderList(){
+            this.orderData.loading = true;
+            var resp = await this.$API.merOrder.list.get({"page":this.orderData.page,"pageSize":this.orderData.size,"order":this.orderData.order,"openid":this.chatData.openid});
+            this.orderData.loading = false;
+            if (resp.code == 0) {
+                return ;
+            }
+            this.orderData.list = resp.data.rows;
+            this.orderData.total = resp.data.total;
+        },
+        viewOrder(data){},
+        sendAddress(data){},
+        editAddress(data){},
+        sendOrder(data){
+
+        },
+    }
+}
+</script>
+<style lang="scss" scoped>
+.right-body{
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    height: 100%;
+    .right-body-left{
+        height: 100%;
+        overflow-y: auto;
+        flex: 1;
+    }
+    .right-body-right{
+        background-color: #f7f7f7;
+        width: 64px;
+        height: 100%;
+        border-left: 1px solid var(--el-border-color-light);
+        display: flex;
+        align-items: center;
+        flex-direction: column;
+        .right-item{
+            display: flex;
+            align-items: center;
+            flex-direction: column;
+            padding: 20px 0;
+            cursor: pointer;
+            &.active{
+                color: #3879fc;
+                .name {
+                    color: #3879fc;
+                }
+            }
+            .name {
+                font-size: 12px;
+                color: #999;
+                margin-top: 5px;
+            }
+        }
+    }
+}
+.order-info-user{background-color: #fff;padding: 10px;margin-top: 10px;}
+.order-info-user .title{font-size: 16px;color: #333;font-weight: bold;padding-bottom: 10px;border-bottom: 1px solid #f8f8f8;}
+.order-info-user .info-item{height: 40px;line-height: 40px;font-size: 12px;display: flex;align-items: center;border-bottom: 1px solid #f8f8f8;}
+.order-info-user .info-item .right{color: #666;margin-left: auto;margin-right: 0;}
+.order-info-user .info-item .right text{border: 1px solid #ccc;color: #333;font-size: 12px;padding: 0 15upx;margin-left: 10px;border-radius: 5px;}
+
+.m-order-s{background-color: #fff;border-radius: 5px;margin-bottom: 10px;border: 1px solid #f8f8f8;}
+.m-order-s .m-order-item{display: flex;font-size: 14px;position: relative;background-color: #f8f8f8;}
+.m-order-s .order-btn-group{display: flex;align-items: center;padding: 10px;gap: 10px;}
+.order-search-input{display: flex;align-items: center;gap: 10px;flex: 1;}
+.m-order-s .order-img{display: flex;}
+.m-order-s .order-img image{width: 80px;height: 80px;}
+.m-order-s .order-info {padding: 5px 10px 10px;flex: 1;}
+.m-order-s .order-info .title{font-size: 14px;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;}
+.m-order-s .order-info .desc{font-size: 12px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;color: #999;}
+.m-order-s .order-info .price{font-size: 14px;color: #f00;}
+.m-order-s .order-status-btn{position: absolute;right: 0;bottom: 0;display: flex;align-items: center;gap: 5px;}
+.m-order-s .status{border-radius: 5px 0 5px 0;background-color: #909399;font-size: 12px;color: #fff;padding: 5px;}
+.m-order-s .status.cus{cursor: pointer;}
+.m-order-s .status.info{background-color: #909399;}
+.m-order-s .status.primary{background-color: #409eff;}
+.m-order-s .status.success{background-color: #67c23a;}
+.m-order-s .status.danger{background-color: #f56c6c;}
+.m-order-s .status.warn{background-color: #e6a23c;}
+.flex-title{display: flex;align-items: center;justify-content: space-between;}
+</style>

+ 529 - 20
src/views/service/chat/components/chatBody.vue

@@ -1,33 +1,72 @@
 <template>
-    <div class="chat-body-components" v-if="!isChat">
+    <div class="chat-body-components" v-if="!chatData">
         <div class="empty-data">
             <img src="https://lf-govern-cdn-tos.bytedcdn.com/obj/life-governance-cdn/life-im-pc/static/image/empty.efbaf24f.png" />
             请从左侧列表选择一个用户开始聊天
         </div>
     </div>
-    <div class="chat-body-components" v-else>
+    <div class="chat-body-components" @paste="handlePaste" v-else>
         <div class="custom-info">
-            <div class="name">与xxx对话中</div>
+            <div class="name">与{{chatData.user.nickname}}对话中</div>
         </div>
-        <div class="chat-content">
+        <div class="chat-content" ref="messageListRef" @scroll="handleScroll">
             <div class="chat-content-body">
-                <div class="chat-loading-sk" v-for="(item,indx) in 20" v-if="userLoading">
-                    <el-skeleton :animated="true">
-                        <template #template>
-                            <div class="chat-sk-flex">
-                                <el-skeleton-item variant="image" style="width: 40px; height: 40px" />
-                                <div class="sk-right">
-                                    <el-skeleton-item variant="h3" style="width: 50%" />
-                                    <div style="margin-top: 5px;">
-                                        <el-skeleton-item variant="text" style="width: 30%;" />
+                <div class="scroll-div-msg" style="padding: 10px;">
+                    <div class="text_26 color__ msg-time" v-if="history.loading">加载中...</div>
+                    <div class="text_26 color__ msg-time" v-if="history.end">没有更多啦~</div>
+                    <template v-for="(item, index) in history.messages">
+                        <div class="msgContent" :id="'msg-' + item.messageId">
+                            <div class="text_26 color__ msg-time">
+                                {{ renderMessageDate(item, index) }}
+                            </div>
+                            <div class="message" :class="[item.userType]">
+                                <div v-if="item.userType === 'friend'">
+                                    <el-image style="width: 40px;height: 40px;" :src="chatData.user.avatar" class="avatar" mode="widthFix"></el-image>
+                                </div>
+                                <div class="message-con">
+                                    <div v-if="item.msgType == 'text'">
+                                        <div v-html="renderTextMessage(item.content)"></div>
+                                    </div>
+                                    <div class="m-address" v-if="item.msgType == 'address'">
+                                        <div class="image"><el-image style="width: 40px;" :src="item.content.img" mode="widthFix"></el-image></div>
+                                        <div class="infos">
+                                            <div class="title">{{item.content.name}}</div>
+                                            <div class="desc">{{item.content.order?item.content.order:'-'}}</div>
+                                        </div>
+                                    </div>
+                                    <div class="m-address" v-if="item.msgType == 'order'">
+                                        <div class="image"><el-image style="width: 40px;" :src="item.content.img" mode="widthFix"></el-image></div>
+                                        <div class="infos">
+                                            <div class="title">{{item.content.name}}</div>
+                                            <div class="desc">{{item.content.order?item.content.order:'-'}}</div>
+                                        </div>
+                                    </div>
+                                    <div class="m-image" v-if="item.msgType == 'image'">
+                                        <el-image style="width: 100px;height: 100px;" :src="item.content" :preview-src-list="[item.content]" :preview-teleported="true"></el-image>
+                                    </div>
+                                    <div class="m-address" v-if="item.msgType == 'pay'">
+                                        <div class="image"><el-image style="width: 40px;" :src="item.content.img" mode="widthFix"></el-image></div>
+                                        <div class="infos">
+                                            <div class="title">{{item.content.name}}</div>
+                                            <div class="price">¥ {{item.content.price?item.content.price:'-'}}</div>
+                                        </div>
+                                    </div>
+                                    <div class="" v-if="item.msgType == 'quick'">
+                                        <div class="quickCon">{{item.content}}</div>
+                                        <div class="quickList" v-if="item.list.length > 0">
+                                            <div class="list-items" v-for="(qItem,indx) in item.list" @click="quickQs(qItem)">
+                                            {{qItem.question}}
+                                            </div>
+                                        </div>
                                     </div>
+                                    <div class="nickname" v-if="item.userType === 'self'">{{ item.selfName }}</div>
                                 </div>
-                                <div class="time">
-                                    <el-skeleton-item variant="text" />
+                                <div v-if="item.userType === 'self'">
+                                    <el-image style="width: 40px;height: 40px;" :src="item.avatar" class="avatar" mode="widthFix"></el-image>
                                 </div>
                             </div>
-                        </template>
-                    </el-skeleton>
+                        </div>
+                    </template>
                 </div>
             </div>
         </div>
@@ -65,7 +104,7 @@
                     </el-popover>
                 </div>
             </div>
-            <div class="send-body">
+            <div class="send-body" @dragenter="handleDragEnter" @dragover="handleDragOver" @dragleave="handleDragLeave" @drop="handleDrop">
                 <el-input
                     type="textarea"
                     :rows="5"
@@ -82,22 +121,393 @@
             </div>
         </div>
     </div>
+    <el-dialog v-model="previewDialogVisible" title="图片预览" width="500px" destroy-on-close :close-on-click-modal="false">
+        <div class="preview-image-wrapper">
+            <el-image :src="previewImageUrl" :preview-src-list="[previewImageUrl]" :preview-teleported="true"></el-image>
+        </div>
+		<template #footer>
+			<el-button @click="previewDialogVisible=false" >取 消</el-button>
+			<el-button type="primary" :loading="uploading" @click="confirmUpload">确定发送</el-button>
+		</template>
+    </el-dialog>
+    <el-upload ref="uploadRef" action="" :auto-upload="false" :file-list="fileList" :on-change="handleFileChange" :http-request="customUpload" style="display: none" />
 </template>
 
 <script>
+import {
+	mapState
+} from 'vuex';
+import {
+	show,
+	formatDate,
+	throttle,
+	openimg,
+	getLocation,
+	to as tofn
+} from '@/util/index.js';
+import { EmojiDecoder, emojiMap } from '@/util/EmojiDecoder.js';
+const emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/';
+const decoder = new EmojiDecoder(emojiUrl, emojiMap);
+import { Push } from "@/utils/push";
 export default {
     data () {
         return {
             userLoading:true,
             inputMessage:"",
-            isChat:false
+            isChat:false,
+            emojiList:[],
+            history: {
+				messages: [],
+				allLoaded: false,
+                size:15,
+                page:1,
+                loading:false,
+                end:false
+			},
+            avatar:{
+                self:"",
+                friend:""
+            },
+            previewDialogVisible: false, // 预览弹窗显示状态
+            previewImageUrl: '',         // 预览图片临时 URL
+            pendingFile: null,           // 待上传的文件对象(Blob)
+            uploading: false,            // 上传中状态
+            fileList: [],                // el-upload 的文件列表
+            isDragover:false,
+            uploadImg:"",
+			pushData: {
+				obj:null,
+				auth:"http://192.168.3.16:9881/plugin/webman/push/auth",
+				url: 'ws://192.168.3.16:3131',
+			},
         }
     },
+	computed: mapState({
+		//显示时间
+		renderMessageDate() {
+			return (message, index) => {
+				if (this.history.messages[index + 1]?.timestamp - message.timestamp  > 3 * 60 * 1000) {
+					return formatDate(message.timestamp, 'timestamp');
+				}
+				return '';
+			};
+		},
+	}),
     props: {
-        userInfo: { type: Object, default: () => {} }
+        userInfo: { type: Object, default: () => {} },
+        chatData: { type: Object, default: () => {} }
+    },
+    created(){
+        this.initPush();
+    },
+    watch: {
+        chatData: {
+            handler(newVal) {
+                if (newVal) {
+                    this.$nextTick(() => {
+                        this.$refs.messageListRef?.addEventListener('scroll', this.handleScroll)
+                    })
+                    this.history.messages = [];
+                    this.history.page = 1;
+                    this.history.allLoaded = false;
+                    this.getMessage()
+                }
+            },
+            immediate: true
+        }
+    },
+    beforeDestroy() {
+        this.$refs.messageListRef.removeEventListener('scroll', this.handleScroll)
     },
     methods: {
+        initPush(){
+            var _this = this;
+            if (!this.pushData.obj) {
+                this.pushData.obj = new Push({
+                    "url":this.pushData.url,
+                    "app_key":"265c33b73d5c04f918978577df2c48d2",
+                    "auth":this.pushData.auth
+                });
+            }
+            var user_channel = this.pushData.obj.subscribe('service-' + this.userInfo.userId);
+            user_channel.on('message', function (data) {
+                _this.playAudio()
+                setTimeout(() => {
+                    _this.$emit("pushNew",data)
+                }, 200);
+                if (_this.chatData && _this.chatData.openid == data.openid) { // 当前窗口实时推送
+                    // console.log("推送消息",data);
+                    const pushData = _this.formatMsg(data);
+                    _this.history.messages.push(pushData);
+                    _this.scrollToBottom()
+                }
+            })
+        },
+        playAudio(){
+            const audio = new Audio('/img/msn.mp3');
+            audio.play().catch(error => {
+                console.error("播放音频时出错:", error);
+            });
+        },
+        handleScroll(e){
+            const { scrollTop, clientHeight, scrollHeight } = e.target
+            const isBottom = scrollTop + clientHeight >= scrollHeight - 5   // 5px 缓冲区
+            const isTop = scrollTop <= 5
+            // console.log("滚动",this.history.allLoaded)
+            if (isBottom && !this.history.allLoaded) {
+                // console.log('到达底部')
+                // 加载更多等操作
+            }
+            if (isTop && !this.history.allLoaded) {
+                this.getMessage()
+            }
+        },
+        async uploadSuccess(data){
+            var resp = await this.sendMessageApi("image",data.data.src);
+            if (!resp) {
+                return ;
+            }
+            this.sendMessageData(data.data.src,"image");
+            this.uploadImg = "";
+        },
+        // ---------- 2. 拖拽事件 ----------
+        handleDragEnter(event) {
+            event.preventDefault(); // 必须阻止,才能让 drop 触发
+            this.isDragover = true;
+        },
+
+        handleDragOver(event) {
+            event.preventDefault(); // 必须阻止,允许放置
+            this.isDragover = true;
+        },
+
+        handleDragLeave(event) {
+            // 注意:dragleave 可能因子元素触发,需检查相关目标
+            if (!this.$el.contains(event.relatedTarget)) {
+                this.isDragover = false;
+            }
+        },
 
+        handleDrop(event) {
+            event.preventDefault();
+            this.isDragover = false;
+
+            const files = event.dataTransfer.files;
+            if (files.length > 0) {
+                const file = files[0];
+                if (file.type.startsWith('image/')) {
+                this.showPreview(file);
+                } else {
+                this.$message.warning('请拖拽图片文件');
+                }
+            }
+        },
+        showPreview(file) {
+            const url = URL.createObjectURL(file);
+            this.pendingFile = file;
+            this.previewImageUrl = url;
+            this.previewDialogVisible = true;
+        },
+        handlePaste(event){
+            const items = event.clipboardData?.items;
+            if (!items) return;
+            for (const item of items) {
+                if (item.type.startsWith('image/')) {
+                event.preventDefault(); // 阻止默认粘贴行为
+
+                const file = item.getAsFile();
+                if (file) {
+                    // 生成临时预览 URL
+                    // const url = URL.createObjectURL(file);
+                    this.showPreview(file)
+                }
+                break;
+                }
+            }
+        },
+        async confirmUpload() {
+            if (!this.pendingFile) {
+                this.$message.warning('没有待上传的文件');
+                return;
+            }
+            this.uploading = true;
+            const formData = new FormData();
+            formData.append('file', this.pendingFile);
+            var resp = await this.$API.common.upload.post(formData)
+            if(resp.code !== 200) {
+                this.$message.error('发送失败');
+            }
+            var send = await this.sendMessageApi("image",resp.data.src);
+            if (!send) {
+                return ;
+            }
+            this.sendMessageData(resp.data.src,"image");
+            this.uploading = false;
+            this.previewDialogVisible = false;
+            this.clearFile();
+        },
+        clearFile() {
+            if (this.previewImageUrl) {
+                URL.revokeObjectURL(this.previewImageUrl);
+            }
+            this.pendingFile = null;
+            this.previewImageUrl = '';
+            this.fileList = [];
+        },
+        handleKeyCode(event){
+            if (event.keyCode == 13) {
+                if (!event.ctrlKey) {
+                    this.sendMessage()
+                    event.preventDefault();
+                } else {
+                    this.inputMessage += "\n";
+                }
+            } else {
+                return ;
+            }
+        },
+		sendMessageData(content,type){
+			const message = {
+				content: content,
+				userType: 'self',
+				avatar: this.userInfo.logo,
+				selfName: this.userInfo.truename,
+				friendName: '-',
+				msgType:type,
+				list:[],
+				messageId: Date.now(),
+				timestamp: Date.now(),
+			};
+			this.history.messages.push(message);
+            this.scrollToBottom()
+		},
+        async sendMessage(){
+            if (!this.inputMessage) return ;
+            var resp = await this.sendMessageApi("text",this.inputMessage);
+            if (!resp) {
+                this.inputMessage = "";
+                return ;
+            }
+            this.sendMessageData(this.inputMessage,"text");
+            this.inputMessage = ""
+        },
+		renderTextMessage(text) {
+			if (!text) return "";
+			text = text.replace(/\n/g, '');
+			if (!text) return '<span>' + '[未知内容]' + '</span>';
+			return '<span>' + decoder.decode(text) + '</span>';
+		},
+        getMessage(){
+            let get = async ()=>{
+                this.history.allLoaded = true;
+                this.history.end = false;
+                this.history.loading = true;
+                var resp = await this.$API.serChat.msg.get({"page":this.history.page,"size":this.history.size,"openid":this.chatData.openid})
+                this.history.loading = false;
+                if (this.history.size > resp.data.rows.length) {
+                    this.history.end = true;
+                }
+                if (resp.data.rows.length > 0) {
+                    var msgData = resp.data.rows;
+                    var pushData = [];
+                    if (msgData.length > 0) {
+                        msgData.forEach(item => pushData.push(this.formatMsg(item)))
+                    }
+                    this.history.messages = [...pushData,...this.history.messages];
+                    if (this.history.page == 1) {
+                        this.scrollToBottom();
+                    } else {
+                        const container = this.$refs.messageListRef;
+                        const oldScrollHeight = container.scrollHeight;
+                        const oldScrollTop = container.scrollTop;
+                        const newScrollHeight = container.scrollHeight;
+                        const addedHeight = newScrollHeight - oldScrollHeight;
+                        container.scrollTop = oldScrollTop + addedHeight;
+                    }
+                    // this.history.messages = this.history.messages.concat(pushData);
+                    this.history.page ++;
+                    this.history.allLoaded = false;
+                } else {
+                    this.history.allLoaded = true;
+                }
+            }
+            get();
+        },
+        scrollToBottom() {
+            this.$nextTick(() => {
+                const container = this.$refs.messageListRef;
+                if (container) {
+                    container.scrollTop = container.scrollHeight;
+                }
+            });
+        },
+        formatMsg(item){
+			var payload = "";
+			if (item.type == 'text') {
+				payload = item.content
+			}
+			if (item.type == 'order') {
+				var order = JSON.parse(item.content)
+				payload = order
+			}
+			if (item.type == 'pay') {
+				var order = JSON.parse(item.content)
+				payload = order
+			}
+			if (item.type == 'address') {
+				var order = JSON.parse(item.content)
+				payload = order
+			}
+			if (item.type == 'image') {
+				payload = item.content
+			}
+			const message = {
+				content: payload,
+				selfName: item.nick?item.nick.truename:'-',
+				friendName: item.user.nickname,
+				userType: item.userType,
+				avatar: item.avatar,
+				msgType:item.type,
+				list:[],
+				messageId: item.msgId,
+				timestamp: item.time,
+			};
+			return message
+		},
+        async sendMessageApi(type,content){
+            var resp = await this.$API.serChat.text.post({type:type,content: content,groupId: this.chatData.poi_id,openid:this.chatData.openid})
+            if (resp.code !== 1){
+                this.$message.error(resp.msg)
+                return false;
+            }
+            this.$emit("success",{'type':type,'content':content,'time':resp.data.time})
+            // this.chatContact[this.checkChat].last.type = type;
+            // this.chatContact[this.checkChat].last.content = (type=='text'?this.truncateByBytes(content,18):content);
+            // this.chatContact[this.checkChat].last.create_at = resp.data.time;
+            return true;
+        },
+        initEmoji() {
+			let newList = [];
+			let i = 1;
+			for (let key in emojiMap) {
+				let obj = {};
+				obj['index'] = i;
+				obj['text'] = key;
+				obj['image'] = emojiMap[key];
+				i++;
+				newList.push(obj);
+			}
+			var list = [];
+			let A = newList.filter((item, index) => {
+				return (index + 1) % 8 === 0;
+			});
+			A.unshift({ index: 0 });
+			A.push({ index: newList.length });
+			this.emojiList = A.map((item, index) => {
+				return newList.slice(A[index].index, A[index + 1]?.index || newList.length);
+			});
+			// this.scroll({ detail: { scrollTop: 10 } });
+		}
     }
 }
 </script>
@@ -111,6 +521,13 @@ export default {
         width: 170px;
     }
 }
+.msg-time {
+	width: 100%;
+	color: #a3a3a3;
+	line-height: 100rpx;
+	text-align: center;
+	font-size: 24rpx;
+}
 .chat-body-components{
     background-color: #f8f8f8;
     display: flex;
@@ -164,4 +581,96 @@ export default {
         }
     }
 }
+.scroll-view-msg{
+    display: flex;
+    flex-direction: column-reverse;
+    flex-wrap: nowrap;
+    align-content: flex-start;
+    justify-content: flex-end;
+    align-items: stretch;
+}
+.message {
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 15px;
+    .quickList{
+        margin-top: 10px;
+        .list-items {
+            color: blue;
+            font-size: 14px;
+            line-height: 24px;
+        }
+    }
+    .avatar {
+        width: 40px;
+        height: 40px;
+        border-radius: 5px;
+        margin-right: 15px;
+    }
+    .message-con {
+        min-height: 40px;
+        max-width: 80vw;
+        box-sizing: border-box;
+        font-size: 14px;
+        line-height: 1.3;
+        padding: 10px;
+        border-radius: 5px;
+        background: #fff;
+    }
+    .nickname{
+        font-size: 12px;
+        color: #999;
+        margin-top: 5px;
+    }
+    &.self {
+        justify-content: flex-end;
+        .avatar {
+            margin: 0 0 0 15px;
+        }
+        .message-con {
+            position: relative;
+            &::after {
+                position: absolute;
+                content: '';
+                width: 0;
+                height: 0;
+                border: 8px solid transparent;
+                border-left: 8px solid #fff;
+                right: -14px;
+                top: 12px;
+            }
+        }
+    }
+    &.friend {
+        .message-con {
+            position: relative;
+            &::after {
+                position: absolute;
+                content: '';
+                width: 0;
+                height: 0;
+                border: 8px solid transparent;
+                border-right: 8px solid #fff;
+                left: -14px;
+                top: 12px;
+            }
+        }
+    }
+}
+
+.m-address{
+	display: flex;align-items: center;gap: 10px;min-width: 260px;
+	&.self{
+		transform: rotate(180);
+	}
+	.image {
+		image{width: 120upx;}
+	}
+	.infos{
+		flex: 1;overflow: hidden;
+		.title{font-size: 14px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
+		.desc{font-size: 12px;color: #999;margin-top: 10px;}
+		.price{font-size: 14px;color: #f00;margin-top: 10px;}
+	}
+}
 </style>

+ 27 - 4
src/views/service/chat/web/index.vue

@@ -12,13 +12,13 @@
         <el-main class="nopadding">
             <el-container>
                 <el-aside width="300px">
-                    <asideMain ref="asideMain" :userInfo="userInfo"></asideMain>
+                    <asideMain ref="asideMain" :userInfo="userInfo" @chatItem="chatItem"></asideMain>
                 </el-aside>
                 <el-container>
-                    <chatMain ref="chatMain" :userInfo="userInfo"></chatMain>
+                    <chatMain ref="chatMain" :userInfo="userInfo" :chatData="chatItemData" :msgData="msgData" @success="sendSuccess" @pushNew="newChatPush"></chatMain>
                 </el-container>
                 <el-aside width="400px">
-                    <rightMain ref="rightMain" :userInfo="userInfo"></rightMain>
+                    <rightMain ref="rightMain" :userInfo="userInfo" :chatData="chatItemData"></rightMain>
                 </el-aside>
             </el-container>
         </el-main>
@@ -36,12 +36,35 @@ export default {
     data(){
         return {
             userInfo:null,
+            chatItemData:null,
+            chatItemIndex:null,
+            msgData:null,
+            newChat:null
         }
     },
-
+    beforeDestroy() {
+        window.removeEventListener('beforeunload', this.handleBeforeUnload);
+    },
 	created() {
 		var userInfo = this.$TOOL.data.get("USER_INFO");
         this.userInfo = userInfo;
+        // window.addEventListener('beforeunload', function(e) {
+        //     e.preventDefault();
+        //     e.returnValue = '有未保存的内容';
+        //     return '有未保存的内容';
+        // });
+    },
+    methods: {
+        chatItem(data,index) {
+            this.chatItemData = data;
+            this.chatItemIndex = index;
+        },
+        newChatPush(data){
+            this.$refs.asideMain.updateItem(data,this.chatItemIndex);
+        },
+        sendSuccess(data) {
+            this.$refs.asideMain.sendSuccess(data,this.chatItemIndex);
+        }
     }
 }
 </script>

+ 4 - 0
src/views/service/order/index/components/table.vue

@@ -106,6 +106,10 @@
                     <span>部分退款</span>
                     <span class="dec">已退款 <em style="color: #f00;">{{ scope.row.refund_num }}</em> 份</span>
                 </div>
+                <div class="order-name" v-if="scope.row.status == 7">
+                    <span>履约中</span>
+                    <span class="dec">货物运输中</span>
+                </div>
             </template>
         </el-table-column>
         <el-table-column label="发货状态" prop="types" width="160" align="left">