Explorar el Código

0717-0215-h001

Zory hace 4 días
padre
commit
7454399643
Se han modificado 27 ficheros con 1828 adiciones y 16 borrados
  1. 26 0
      src/api/model/complaint.js
  2. 26 0
      src/api/model/merComplaint.js
  3. 54 0
      src/api/model/print.js
  4. 14 0
      src/api/model/store.js
  5. 9 7
      src/components/imFloat/order.vue
  6. 2 2
      src/components/scFileExport/index.vue
  7. 154 0
      src/views/manage/order/after/components/detail.vue
  8. 96 0
      src/views/manage/order/after/components/form.vue
  9. 84 0
      src/views/manage/order/after/components/search.vue
  10. 158 0
      src/views/manage/order/after/components/table.vue
  11. 47 0
      src/views/manage/order/after/index.vue
  12. 17 0
      src/views/manage/setting/basic/index.vue
  13. 178 0
      src/views/manage/shop/index/components/express.vue
  14. 1 1
      src/views/manage/shop/index/components/form.vue
  15. 7 3
      src/views/manage/shop/index/components/table.vue
  16. 86 0
      src/views/manage/shop/print/components/form.vue
  17. 53 0
      src/views/manage/shop/print/components/option.vue
  18. 47 0
      src/views/manage/shop/print/components/search.vue
  19. 197 0
      src/views/manage/shop/print/components/table.vue
  20. 48 0
      src/views/manage/shop/print/index.vue
  21. 154 0
      src/views/merchant/order/after/components/detail.vue
  22. 96 0
      src/views/merchant/order/after/components/form.vue
  23. 63 0
      src/views/merchant/order/after/components/search.vue
  24. 146 0
      src/views/merchant/order/after/components/table.vue
  25. 45 1
      src/views/merchant/order/after/index.vue
  26. 10 1
      src/views/merchant/order/index/components/search.vue
  27. 10 1
      src/views/service/order/index/components/search.vue

+ 26 - 0
src/api/model/complaint.js

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

+ 26 - 0
src/api/model/merComplaint.js

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

+ 54 - 0
src/api/model/print.js

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

+ 14 - 0
src/api/model/store.js

@@ -68,4 +68,18 @@ export default {
             return await http.post(this.url, data);
         },
     },
+    express: {
+        url: `${config.API_URL}/store/express`,
+        name: "-",
+        get: async function (data = {}) {
+            return await http.get(this.url, data);
+        },
+    },
+    expressSave: {
+        url: `${config.API_URL}/store/express/save`,
+        name: "-",
+        post: async function (data = {}) {
+            return await http.post(this.url, data);
+        },
+    },
 }

+ 9 - 7
src/components/imFloat/order.vue

@@ -11,8 +11,8 @@
                     </div>
                 </div>
             </div>
-            <el-main class="nopadding" v-if="orderData" v-loading="loading">
-                <div class="order-detail">
+            <el-main class="nopadding" v-loading="loading">
+                <div class="order-detail" v-if="orderData">
                     <div class="order-status">
                         <div class="status-name" v-if="orderData.status == 0 && orderTime">
                             待支付
@@ -90,7 +90,7 @@
                         </div>
                     </div>
                     <div class="order-info" v-if="orderData.address">
-                        <div class="title">收货地址</div>
+                        <div class="title">收货地址<span v-copy="orderData.address.nickname+','+orderData.address.mobile+','+orderData.address.city_text+orderData.address.address">一键复制</span></div>
                         <div class="info-item">
                             <div class="name">收件人</div>
                             <div class="right">{{ orderData.address.nickname }} </div>
@@ -100,12 +100,12 @@
                             <div class="right">{{ orderData.address.mobile }}</div>
                         </div>
                         <div class="info-item">
-                            <div class="name">收货地</div>
-                            <div class="right">{{ orderData.address }}</div>
+                            <div class="name">收货地</div>
+                            <div class="right">{{ orderData.address.city_text }}</div>
                         </div>
                         <div class="info-item">
                             <div class="name">详细地址</div>
-                            <div class="right">{{ orderData.address }}</div>
+                            <div class="right">{{ orderData.address.address }}</div>
                         </div>
                     </div>
                     <div class="order-info" v-else>
@@ -207,7 +207,9 @@ export default {
 .order-detail .goods-info .desc{font-size: 12px;color: #999;display: flex;align-items: center;justify-content: space-between;margin: 10px 0;}
 .order-detail .goods-info .total{font-size: 16px;color: #333;text-align: right;font-weight: bold;}
 .order-detail .order-info{background-color: #fff;padding: 10px;margin-top: 10px;}
-.order-detail .order-info .title{font-size: 16px;color: #333;font-weight: bold;padding-bottom: 10px;border-bottom: 1px solid #f8f8f8;}
+.order-detail .order-info .title{font-size: 16px;color: #333;font-weight: bold;padding-bottom: 10px;border-bottom: 1px solid #f8f8f8;display: flex;align-items: center;justify-content: space-between;}
+.order-detail .order-info .title span{font-size: 12px;color: #333;font-weight: normal;border: 1px solid #ededed;padding: 5px 10px;cursor: pointer;}
+
 .order-detail .order-info .info-item{height: 40px;line-height: 40px;font-size: 12px;display: flex;align-items: center;border-bottom: 1px solid #f8f8f8;}
 .order-detail .order-info .info-item .right{color: #666;margin-left: auto;margin-right: 0;}
 .order-detail .order-info .info-item .right text{border: 1px solid #ccc;color: #333;font-size: 12px;padding: 0 15upx;margin-left: 10px;border-radius: 5px;}

+ 2 - 2
src/components/scFileExport/index.vue

@@ -19,12 +19,12 @@
 				</div>
 				<el-tabs>
 					<el-tab-pane label="常规" lazy>
-						<el-form label-width="100px" label-position="left" style="margin: 10px 0 20px 0;">
+						<el-form label-width="100px" label-position="top" style="margin: 10px 0 20px 0;">
 							<el-form-item label="文件名">
 								<el-input v-model="formData.fileName" placeholder="请输入文件名" />
 							</el-form-item>
 							<el-form-item label="文件类型">
-								<el-select v-model="formData.fileType" placeholder="请选择文件类型">
+								<el-select v-model="formData.fileType" placeholder="请选择文件类型" style="width: 100%;">
 									<el-option v-for="item in fileTypes" :key="item" :label="'*.'+item" :value="item" />
 								</el-select>
 							</el-form-item>

+ 154 - 0
src/views/manage/order/after/components/detail.vue

@@ -0,0 +1,154 @@
+<template>
+    <el-drawer v-model="visible" title="在线客服" size="500px" destroy-on-close :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 class="nopadding" v-loading="loading">
+                <div class="order-detail" v-if="orderData">
+                    <div class="ts-header">
+                        <div class="ts-h-title">投诉门店</div>
+                        <div class="store-info">
+                            <div class="store-left">
+                                <div class="name">{{orderData.store.poi_name}}</div>
+                                <div class="address">{{orderData.store.poi_address}}</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="ts-body">
+                        <div class="order-status" v-if="orderData.status == 0">
+                            <img src="https://jymini.oss-cn-guangzhou.aliyuncs.com/mini/complaint-2.svg" />
+                        </div>
+                        <div class="order-status" v-if="orderData.status == 1">
+                            <img src="https://jymini.oss-cn-guangzhou.aliyuncs.com/mini/complaint-3.svg" />
+                        </div>
+                        <div class="ts-form">
+                            <div class="ts-form-item">
+                                <div class="form-title">具体问题</div>
+                                <div class="form-input">
+                                    {{orderData.question}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">问题订单</div>
+                                <div class="form-input">
+                                    {{orderData.order_sn}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">联系方式</div>
+                                <div class="form-input">
+                                    {{orderData.mobile}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">详细补充</div>
+                                <div class="form-input-text">
+                                    {{orderData.content}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">回复结果 </div>
+                                <div class="form-input-text">
+                                    <div class="data" v-html="orderData.remark"></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </el-main>
+        </el-container>
+    </el-drawer>
+</template>
+
+<script>
+export default {
+    data(){
+        return {
+            loading: false,
+            isSaveing: false,
+            isDoneing:false,
+            mode:"add",
+            titleMap:{
+                add:"投诉详情",
+                edit:"投诉详情"
+            },
+            visible:false,
+            orderData:null,
+			orderTime:null
+        }
+    },
+    methods: {
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        //表单注入数据
+        setData(data){
+            this.formData = JSON.parse(JSON.stringify(data));
+            this.getOrderDetail()
+        },
+        doneOrder(){
+            this.$confirm(`手动核销订单后,该订单不支持线上退款,如需退款请线下沟通后进行`, '提示', {
+                type: 'warning'
+            }).then(async ()=>{
+                this.isDoneing = true;
+                var resp = await this.$API.merOrder.done.post({"id":this.orderData.order_sn});
+                this.isDoneing = false;
+                if (resp.code == 0) {
+                    return this.$message.error(resp.msg);
+                }
+                this.$message.success(resp.msg);
+                this.visible = false;
+                this.$emit("success");
+            }).catch(()=>{})
+        },
+        async getOrderDetail(){
+            this.loading = true;
+            var resp = await this.$API.complaint.detail.get({"id":this.formData.id});
+            this.loading = false;
+            if (resp.code == 0) {
+                this.$message.error(resp.msg);
+                this.visible = false;
+                return ;
+            }
+            this.orderData = resp.data;
+            this.orderTime = resp.data.end_time
+        },
+        sendAddress(){
+            this.$emit("success",'address',this.orderData);
+        }
+    }
+}
+</script>
+
+<style>
+.nopadding{background: #f8f8f8 !important;}
+.ts-header{padding: 10px 0;margin-bottom: 10px;}
+.ts-header .ts-h-title{font-size: 14px;color: #666;padding: 10px;}
+.ts-header .store-info{background-color: #fff;padding: 10px;}
+.ts-header .store-info .store-left .name{font-size: 30rpx;color: #333;font-weight: bold;line-height: 40px;}
+.ts-header .store-info .store-left .address{font-size: 12px;color: #999;}
+.ts-body{background-color: #fff;padding: 10px;position: relative;}
+
+.ts-body .order-status{position: absolute;right: 10px;top: 10px;}
+.ts-body .order-status img{width: 110px;height: 110px;transform: rotate(45deg);}
+
+.ts-form .ts-form-item{border-bottom: 1px solid #f8f8f8;padding-bottom: 10upx;}
+.ts-form .ts-form-item:last-child{border-bottom: 0;}
+.ts-form .ts-form-item .form-title{color: #000;font-size: 14px;padding: 10px 0;}
+.ts-form .ts-form-item .form-title text{color: #f00;margin-left: 10upx;}
+.ts-form .ts-form-item .form-input{font-size: 28upx;color: #333;height: 40px;display: flex;align-items: center;}
+.ts-form .ts-form-item .ts-input{height: 100%;border: 0;box-shadow: none;width: 100%;}
+.ts-form .form-input-textarea{height: 200upx;font-size: 14px;color: #333;}
+.form-input-text{padding: 10px 0;}
+.form-input-text img{width: 100%;}
+</style>

+ 96 - 0
src/views/manage/order/after/components/form.vue

@@ -0,0 +1,96 @@
+<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="投诉事项">
+                        <el-input v-model="formData.question" readonly disabled clearable placeholder="请输入"></el-input>
+                        <div class="el-form-item-msg"></div>
+                    </el-form-item>
+                    <el-form-item label="投诉订单">
+                        <el-input v-model="formData.order_sn" readonly disabled clearable placeholder="请输入"></el-input>
+                        <div class="el-form-item-msg"></div>
+                    </el-form-item>
+                    <el-form-item label="回复" prop="remark" v-if="formData.status == 0">
+                        <sc-editor v-model="formData.remark" placeholder="请输入" :height="400"></sc-editor>
+                        <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 size="large" type="primary" :loading="isSaveing" @click="submitEdit()">保 存</el-button>
+            </el-footer>
+        </el-container>
+    </el-drawer>
+</template>
+
+<script>
+import { defineAsyncComponent } from 'vue';
+const scEditor = defineAsyncComponent(() => import('@/components/scEditor'));
+export default {
+    components: {
+        scEditor
+    },
+    data(){
+        return {
+            loading: false,
+            isSaveing: false,
+            mode:"add",
+            titleMap:{
+                add:"投诉回复",
+                edit:"投诉回复"
+            },
+            visible:false,
+            formData:{},
+            rules:{
+                remark: [
+                    {required: true, message: '请输入'}
+                ],
+            }
+        }
+    },
+    methods:{
+        quickGoods(){
+            this.quickData.visible = true;
+        },
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        //表单注入数据
+        setData(data){
+            this.formData = JSON.parse(JSON.stringify(data));
+        },
+        async submitEdit(){
+            var loading = this.$loading()
+            this.isSaveing = true;
+            var resp = await this.$API.complaint.save.post(this.formData)
+            this.isSaveing = false;
+            loading.close();
+            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>
+.goods-live{width: 100%;margin-top: 10px;}
+.flex-loading{display: flex;flex-direction: column;flex: 1;}
+</style>

+ 84 - 0
src/views/manage/order/after/components/search.vue

@@ -0,0 +1,84 @@
+<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="6">
+                            <el-input v-model="searchKey.poi_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>
+                                <template #prepend>所属商家</template>
+                            </el-input>
+                        </el-col>
+                        <el-col :xs="12" :sm="12" :md="12" :lg="6" :xl="4">
+                            <el-input v-model="searchKey.order_sn" 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-input v-model="searchKey.mobile" 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>
+    <storeData ref="storeData" :multiple="false" @success="handleStore"></storeData>
+</template>
+
+<script>
+import storeData from '@/views/manage/components/agent';
+export default {
+    components: {
+        storeData
+    },
+    data(){
+        return {
+            searchKey:{}
+        }
+    },
+    methods: {
+        selectStore(){
+            this.$nextTick(()=>{
+                this.$refs.storeData.open()
+            })
+        },
+        handleStore(data){
+            this.searchKey.poi_id = data.poi_id;
+            this.searchKey.poi_name = data.poi_name;
+            this.$emit("success",this.searchKey);
+        },
+        clearStore(){
+            this.searchKey.poi_id = "";
+            this.searchKey.poi_name = "";
+            this.$emit("success",this.searchKey);
+        },
+        searchForm(){
+            this.$emit("success",this.searchKey);
+        }
+    }
+}
+</script>

+ 158 - 0
src/views/manage/order/after/components/table.vue

@@ -0,0 +1,158 @@
+<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="260" fixed="left" prop="goods_id">
+            <template #default="scope">
+                <div class="order-name">
+                    <span>{{ scope.row.order_sn }}</span>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="所属商家" prop="id" width="320">
+            <template #default="scope">
+                <div class="goods-img">
+                    <div class="name" v-if="scope.row.store">
+                        <span>{{ scope.row.store.poi_name }}</span>
+                        <span class="dec">{{ scope.row.store.poi_address }}</span>
+                    </div>
+                    <div v-else>-</div>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="投诉事项" prop="types" width="220">
+            <template #default="scope">
+                {{ scope.row.question?scope.row.question:'-' }}
+            </template>
+        </el-table-column>
+        <el-table-column label="联系方式" prop="types" width="100">
+            <template #default="scope">
+                {{ scope.row.mobile?scope.row.mobile:'-' }}
+            </template>
+        </el-table-column>
+        <el-table-column label="商品编码" width="340" prop="name">
+            <template #default="scope">
+                <div class="goods-img">
+                    <div class="name">
+                        <span>{{ scope.row.orders?scope.row.orders.product_id:'-' }}</span>
+                    </div>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="状态" prop="types" width="160" align="left">
+            <template #default="scope">
+                <div class="order-name" v-if="scope.row.status == 0">
+                    <span>待处理</span>
+                </div>
+                <div class="order-name" v-if="scope.row.status == 1">
+                    <span>已完成</span>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="发起时间" prop="create_at" width="160"></el-table-column>
+        <el-table-column label="处理时间" prop="last_at" width="160">
+            <template #default="scope">
+                {{ scope.row.last_at?scope.row.last_at:'-' }}
+            </template>
+        </el-table-column>
+        <el-table-column label="操作" width="140" align="left" fixed="right">
+            <template #default="scope">
+                <el-button-group>
+                    <el-button size="small" text @click="table_view(scope.row)">详情</el-button>
+                    <el-button type="danger" v-if="scope.row.status==0" text size="small" @click="table_del(scope.row)">回复</el-button>
+                </el-button-group>
+            </template>
+        </el-table-column>
+    </scTable>
+    <orderDetail ref="orderDetail" @success="orderSuccess"></orderDetail>
+    <formMain ref="formMain" @success="orderSuccess"></formMain>
+</template>
+
+<script>
+import orderDetail from "./detail";
+import formMain from "./form";
+export default {
+    components:{
+        orderDetail,formMain
+    },
+    data(){
+        return {
+            list: {
+                apiObj: this.$API.complaint.list
+            },
+            dataSelect:[],
+            dataSelectFull:[],
+            searchKey:{}
+        }
+    },
+    methods: {
+        orderSuccess(){
+            this.$refs.table.refresh()
+        },
+        table_view(data){
+            this.$nextTick(() => {
+                data.order = data.out_order_no
+                this.$refs.orderDetail.open("edit").setData(data)
+            })
+        },
+        table_del(data){
+            this.$nextTick(() => {
+                this.$refs.formMain.open("edit").setData(data)
+            })
+        },
+        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>
+.order-name span{display: block;}
+.order-name span.dec{color: #999;font-size: 12px;}
+.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;}
+</style>

+ 47 - 0
src/views/manage/order/after/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
+    },
+    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>

+ 17 - 0
src/views/manage/setting/basic/index.vue

@@ -108,6 +108,23 @@
                                         </el-form-item>
                                     </fieldset>
                                 </el-col>
+                                <el-col :span="this.$store.state.global.ismobile?24:12">
+                                    <fieldset>
+                                        <legend><el-tag>云打印机</el-tag></legend>
+                                        <el-form-item label="云打印机AppId" prop="ex_appid">
+                                            <el-input v-model="sys.ex_appid" show-password clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg"></div>
+                                        </el-form-item>
+                                        <el-form-item label="云打印机密钥" prop="ex_secret">
+                                            <el-input v-model="sys.ex_secret" show-password clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg"></div>
+                                        </el-form-item>
+                                        <el-form-item label="云打印机模版ID" prop="ex_print">
+                                            <el-input v-model="sys.ex_print" clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg"></div>
+                                        </el-form-item>
+                                    </fieldset>
+                                </el-col>
                             </el-row>
                         </el-form>
                     </el-tab-pane>

+ 178 - 0
src/views/manage/shop/index/components/express.vue

@@ -0,0 +1,178 @@
+<template>
+    <el-dialog :title="titleMap[mode]" v-model="visible" :close-on-click-modal="false" :width="600" destroy-on-close @closed="$emit('closed')">
+        <el-form ref="dialogForm" :model="submitData" :rules="rules" label-width="100px" label-position="top" v-loading="loading">
+            <el-form-item label="自动呼叫快递" prop="status">
+                <el-radio-group v-model="submitData.status">
+                    <el-radio-button border label="1">开启</el-radio-button>
+                    <el-radio-button border label="2">关闭</el-radio-button>
+                </el-radio-group>
+                <div class="el-form-item-msg"></div>
+            </el-form-item>
+            <template v-if="submitData.status==1">
+                <el-row :gutter="10">
+                    <el-col :span="24">
+                        <el-form-item label="快递公司" prop="type">
+                            <el-select v-model="submitData.type" clearable placeholder="请选择" :style="{width: '100%'}">
+                                <el-option value="sfexpress" label="顺丰速运"></el-option>
+                            </el-select>
+                            <div class="el-form-item-msg"></div>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="快递类型" prop="rule.expressTypeId">
+                            <el-select v-model="submitData.rule.expressTypeId" clearable placeholder="请选择" :style="{width: '100%'}">
+                                <el-option value="1" label="顺丰特快"></el-option>
+                                <el-option value="2" label="顺丰标快"></el-option>
+                                <el-option value="201" label="冷运标快"></el-option>
+                                <el-option value="150" label="标快D类"></el-option>
+                                <el-option value="66" label="特快D类"></el-option>
+                                <el-option value="283" label="填舱标快"></el-option>
+                                <el-option value="247" label="电商标快"></el-option>
+                                <el-option value="231" label="陆运包裹"></el-option>
+                            </el-select>
+                            <div class="el-form-item-msg"></div>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="月结帐号" prop="rule.customId">
+                            <el-input v-model="submitData.rule.customId" clearable placeholder="请输入"></el-input>
+                            <div class="el-form-item-msg"></div>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <fieldset>
+                    <legend><el-tag type="danger">收货地址信息</el-tag></legend>
+                    <el-row :gutter="10">
+                        <el-col :span="12">
+                            <el-form-item label="联系人" prop="rule.name">
+                                <el-input v-model="submitData.rule.name" clearable placeholder="请输入"></el-input>
+                                <div class="el-form-item-msg">用于呼叫快递</div>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :span="12">
+                            <el-form-item label="联系电话" prop="rule.mobile">
+                                <el-input v-model="submitData.rule.mobile" clearable placeholder="请输入"></el-input>
+                                <div class="el-form-item-msg">用于呼叫快递</div>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :span="12">
+                            <el-form-item label="物品名称" prop="rule.goods">
+                                <el-input v-model="submitData.rule.goods" clearable placeholder="请输入"></el-input>
+                                <div class="el-form-item-msg">用于呼叫快递</div>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :span="12">
+                            <el-form-item label="物品单位" prop="rule.unit">
+                                <el-input v-model="submitData.rule.unit" clearable placeholder="请输入"></el-input>
+                                <div class="el-form-item-msg">用于呼叫快递</div>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :span="24">
+                            <el-form-item label="发货地址" prop="rule.address">
+                                <el-input v-model="submitData.rule.address" clearable placeholder="请输入"></el-input>
+                                <div class="el-form-item-msg">必须是真实地址</div>
+                            </el-form-item>
+                        </el-col>
+                    </el-row>
+                </fieldset>
+            </template>
+        </el-form>
+		<template #footer>
+			<el-button @click="visible=false" >取 消</el-button>
+			<el-button type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
+		</template>
+    </el-dialog>
+</template>
+
+<script>
+export default {
+    data(){
+        return {
+            loading: false,
+            isSaveing: false,
+            mode:"add",
+            titleMap:{
+                add:"快递权限设置",
+                edit:"快递权限设置"
+            },
+            visible:false,
+            formData:{},
+            submitData:{
+                rule:{}
+            },
+            rules:{
+                status: [
+                    {required: true, message: '请选择'}
+                ],
+                type: [
+                    {required: true, message: '请选择'}
+                ],
+                'rule.expressTypeId': [
+                    {required: true, message: '请选择'}
+                ],
+                'rule.customId': [
+                    {required: true, message: '请输入'}
+                ],
+                'rule.name': [
+                    {required: true, message: '请输入'}
+                ],
+                'rule.mobile': [
+                    {required: true, message: '请输入'}
+                ],
+                'rule.goods': [
+                    {required: true, message: '请输入'}
+                ],
+                'rule.unit': [
+                    {required: true, message: '请输入'}
+                ],
+                'rule.address': [
+                    {required: true, message: '请输入'}
+                ],
+            }
+        }
+    },
+    methods:{
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        //表单注入数据
+        setData(data){
+            this.formData = JSON.parse(JSON.stringify(data));
+            this.getExpress()
+        },
+        async getExpress(){
+            this.loading = true;
+            var resp = await this.$API.store.express.get({"poi":this.formData.poi_id});
+            this.loading = false;
+            if (resp.code == 0) {
+                return this.$message.error(resp.msg)
+            }
+            if (resp.data.return) {
+                this.submitData = resp.data.return;
+            } else {
+                this.submitData = {rule:{}}
+            }
+        },
+        async submit(){
+            var validate = await this.$refs.dialogForm.validate().catch(()=>{});
+            if(!validate){ return false }
+            this.isSaveing = true;
+            let submitData = JSON.parse(JSON.stringify(this.submitData));
+            submitData.poi_id = this.formData.poi_id;
+            var resp = await this.$API.store.expressSave.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 = {
+                rule:{}
+            };
+            this.$emit("success");
+        }
+    }
+}
+</script>

+ 1 - 1
src/views/manage/shop/index/components/form.vue

@@ -1,5 +1,5 @@
 <template>
-    <el-dialog :title="titleMap[mode]" v-model="visible" :width="800" destroy-on-close @closed="$emit('closed')">
+    <el-dialog :title="titleMap[mode]" v-model="visible" :width="800" :close-on-click-modal="false" destroy-on-close @closed="$emit('closed')">
         <el-form ref="dialogForm" :model="formData" :rules="rules" label-width="100px" label-position="top">
             
             <el-row :gutter="10">

+ 7 - 3
src/views/manage/shop/index/components/table.vue

@@ -62,7 +62,8 @@
     <formMain ref="formMain" @success="handleSuccess"></formMain>
     <comboMain ref="comboMain" @success="handleSuccess"></comboMain>
     <categoryMain ref="categoryMain" @success="handleSuccess"></categoryMain>
-    <el-dialog :title="qrcodeData.title" v-model="qrVisible" :width="400" destroy-on-close>
+    <expressMain ref="expressMain" @success="handleSuccess"></expressMain>
+    <el-dialog :title="qrcodeData.title" :close-on-click-modal="false" v-model="qrVisible" :width="400" destroy-on-close>
         <div class="qrcode-img">
             <el-image :src="qrcodeData.img"></el-image>
         </div>
@@ -73,9 +74,10 @@
 import formMain from './form';
 import comboMain from './combo';
 import categoryMain from './category';
+import expressMain from './express';
 export default {
     components: {
-        formMain,comboMain,categoryMain
+        formMain,comboMain,categoryMain,expressMain
     },
     data(){
         return {
@@ -94,7 +96,9 @@ export default {
     },
     methods: {
         table_express(data){
-            
+            this.$nextTick(() => {
+                this.$refs.expressMain.open("add").setData(data)
+            })
         },
         async table_qrcode(data){
             var loading = this.$loading()

+ 86 - 0
src/views/manage/shop/print/components/form.vue

@@ -0,0 +1,86 @@
+<template>
+    <el-dialog :title="titleMap[mode]" v-model="visible" :width="600" destroy-on-close @closed="$emit('closed')" :close-on-click-modal="false" :close-on-press-escape="false">
+        <el-form ref="dialogForm" :model="formData" :rules="rules" label-width="100px" label-position="top">
+            <el-form-item label="序列号" prop="sn">
+                <el-input v-model="formData.sn" clearable placeholder="请输入"></el-input>
+                <div class="el-form-item-msg"></div>
+            </el-form-item>
+            <el-form-item label="打印机名称" prop="name">
+                <el-input v-model="formData.name" clearable placeholder="请输入"></el-input>
+                <div class="el-form-item-msg">可自定义</div>
+            </el-form-item>
+            <el-form-item label="设备密钥" prop="device_key">
+                <el-input v-model="formData.device_key" clearable placeholder="请输入"></el-input>
+                <div class="el-form-item-msg">在打印机底部可以查看</div>
+            </el-form-item>
+        </el-form>
+		<template #footer>
+			<el-button @click="visible=false" >取 消</el-button>
+			<el-button type="primary" :loading="isSaveing" @click="submit()">提交</el-button>
+		</template>
+    </el-dialog>
+</template>
+<script>
+export default {
+    data(){
+        return {
+            loading: false,
+            mode:"add",
+            titleMap:{
+                add:"新增打印机",
+                edit:"编辑打印机信息"
+            },
+            visible: false,
+            isSaveing: false,
+            formData:{},
+            submitState:false,
+            disabled:false,
+            rules:{
+                sn: [
+                    {required: true, message: '请输入'}
+                ],
+                name: [
+                    {required: true, message: '请输入'}
+                ],
+                device_key: [
+                    {required: true, message: '请输入'}
+                ]
+            },
+            sms:""
+        }
+    },
+    methods:{
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        //表单注入数据
+        setData(data){
+            data.address = data.region+data.address;
+            this.formData = data;
+        },
+        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.print.save.post(submitData);
+            this.isSaveing = false;
+            if (resp.code !== 1) {
+                return this.$message.error(resp.msg);
+            }
+            this.$message.success(resp.msg);
+            this.visible = false;
+            this.formData = {};
+            this.submitState = false;
+            this.$emit("success");
+        }
+    }
+}
+</script>
+<style>
+.mt20 {margin-top: 20px;}
+.login-msg-yzm {display: flex;width: 100%;}
+.login-msg-yzm .el-button {margin-left: 10px;--el-button-size:42px;}
+</style>

+ 53 - 0
src/views/manage/shop/print/components/option.vue

@@ -0,0 +1,53 @@
+<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>
+                <el-button type="warning" icon="el-icon-refresh" @click="table_refresh()">查询状态</el-button>
+            </div>
+        </div>
+    </fieldset>
+    <formMain ref="formMain" @success="handleSuccess"></formMain>
+</template>
+
+<script>
+import formMain from './form';
+export default {
+    components: {
+        formMain
+    },
+    props: {
+        type: { type: String, default: "1" },
+        dataSelect: { type: Array, default: () => [] },
+        searchKey: { type: Object, default: () => {} },
+        dataSelectFull: { type: Array, default: () => [] }
+    },
+    data(){
+        return {
+
+        }
+    },
+    methods: {
+        async table_refresh(){
+            var loading = this.$loading();
+            var resp = await this.$API.print.refresh.post({});
+            loading.close();
+            if (resp.code == 0) {
+                return this.$message.error(resp.msg);
+            }
+            this.$message.success(resp.msg);
+        },
+        table_add(){
+            this.$nextTick(() => {
+                this.$refs.formMain.open()
+            })
+        },
+        handleSuccess(){
+            this.$emit("success");
+        },
+    }
+}
+</script>

+ 47 - 0
src/views/manage/shop/print/components/search.vue

@@ -0,0 +1,47 @@
+<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 :span="this.$store.state.global.ismobile?12:4">
+                            <el-input v-model="searchKey.sn" placeholder="请输入" clearable :style="{ width: '100%' }" @keyup.enter="searchForm()">
+                                <template #prepend>序列号</template>
+                            </el-input>
+                        </el-col>
+                        <el-col :span="this.$store.state.global.ismobile?12:4">
+                            <el-input v-model="searchKey.name" placeholder="名称" clearable :style="{ width: '100%' }" @keyup.enter="searchForm()">
+                                <template #prepend>名称</template>
+                            </el-input>
+                        </el-col>
+                        <el-col :span="this.$store.state.global.ismobile?12: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>

+ 197 - 0
src/views/manage/shop/print/components/table.vue

@@ -0,0 +1,197 @@
+<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="序列号" prop="sn" width="220" fixed="left">
+            <template #default="scope">
+                <span v-if="scope.row.sn">{{ scope.row.sn }}</span>
+                <span class="status-danger" v-else>未设置</span>
+            </template>
+        </el-table-column>
+        <el-table-column label="打印机名称" prop="name" width="220">
+            <template #default="scope">
+                <span v-if="scope.row.name">{{ scope.row.name }}</span>
+                <span class="status-danger" v-else>未设置</span>
+            </template>
+        </el-table-column>
+        <el-table-column label="所属商家" prop="id" width="360">
+            <template #default="scope">
+                <div class="goods-img">
+                    <div class="name" v-if="scope.row.poi">
+                        <div class="flex-end">
+                            {{ scope.row.poi.poi_name }}
+                        </div>
+                        <span class="dec">{{ scope.row.poi.poi_address }}</span>
+                    </div>
+                    <div v-else>-</div>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="设备状态" prop="device_state" width="160">
+            <template #default="scope">
+                <div class="status-success" v-if="scope.row.device_state==0"><sc-status-indicator type="success"></sc-status-indicator> 正常</div>
+                <div class="status-danger" v-if="scope.row.device_state==1"><sc-status-indicator type="danger"></sc-status-indicator> 开盖</div>
+                <div class="status-info" v-if="scope.row.device_state==2"><sc-status-indicator type="info"></sc-status-indicator> 粘纸</div>
+                <div class="status-info" v-if="scope.row.device_state==3"><sc-status-indicator type="info"></sc-status-indicator> 缺纸</div>
+            </template>
+        </el-table-column>
+        <el-table-column label="在线状态" prop="status" width="160">
+            <template #default="scope">
+                <div class="status-success" v-if="scope.row.online=='ONLINE'"><sc-status-indicator type="success"></sc-status-indicator> 正常</div>
+                <div class="status-danger" v-if="scope.row.online=='OFFLINE'"><sc-status-indicator type="danger"></sc-status-indicator> 离线</div>
+                <div class="status-info" v-if="scope.row.online=='UNACTIVE'"><sc-status-indicator type="info"></sc-status-indicator> 未激活</div>
+                <div class="status-info" v-if="scope.row.online=='DISABLE'"><sc-status-indicator type="info"></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="220" align="right" fixed="right">
+            <template #default="scope">
+                <el-button-group>
+                    <el-button text type="danger" size="small" @click="cancelPeint(scope.row)" v-if="scope.row.poi">解除绑定</el-button>
+                    <el-button text type="warning" size="small" @click="table_bind(scope.row)" v-else>绑定商家</el-button>
+                    <el-button text type="success" size="small" @click="table_print(scope.row)">打印测试</el-button>
+                    <el-popconfirm title="确定删除并解绑该打印机吗?" @confirm="table_del(scope.row, scope.$index)">
+                        <template #reference>
+                            <el-button text type="danger" size="small">删除</el-button>
+                        </template>
+                    </el-popconfirm>                    
+                </el-button-group>
+            </template>
+        </el-table-column>
+    </scTable>
+    <formMain ref="formMain" @success="handleSuccess"></formMain>
+    <storeData ref="storeData" :multiple="false" @success="handleStore"></storeData>
+</template>
+
+<script>
+import formMain from './form';
+import storeData from "@/views/manage/components/agent";
+export default {
+    components: {
+        formMain,storeData
+    },
+    data(){
+        return {
+            list: {
+                apiObj: this.$API.print.list
+            },
+            dataSelect:[],
+            dataSelectFull:[],
+            searchKey:{},
+            bindData:{}
+        }
+    },
+    methods: {
+        async handleStore(data){
+            var loading = this.$loading();
+            var resp = await this.$API.print.edit.post({"sn":this.bindData.sn,'id':this.bindData.id,'poi_id':data.poi_id});
+            loading.close();
+            if (resp.code == 0) {
+                return this.$message.error(resp.msg);
+            }
+            this.$message.success(resp.msg);
+            var that = this;
+            setTimeout(function(){
+                that.refresh()
+            },1500);
+        },
+        cancelPeint(data){
+            this.$confirm(`店铺解绑打印机后,所有核销完后订单将无法正常打印订单,确定执行解绑吗?`, '提示', {
+                type: 'warning'
+            }).then(async ()=>{
+                var resp = await this.$API.print.edit.post({"sn":data.sn,'id':data.id,'poi_id':""});
+                if (resp.code == 0) {
+                    return this.$message.error(resp.msg);
+                }
+                this.$message.success(resp.msg);
+                var that = this;
+                setTimeout(function(){
+                    that.refresh()
+                },1500);
+            }).catch(()=>{})
+        },
+        table_bind(data){
+            this.bindData = data;
+            this.$nextTick(() => {
+                this.$refs.storeData.open()
+            })
+        },
+        async table_print(data){
+            var loading = this.$loading();
+            var resp = await this.$API.print.test.post({'sn':data.sn});
+            loading.close();
+            if (resp.code == 0) {
+                return this.$message.error(resp.msg);
+            }
+            this.$message.success(resp.msg);
+        },
+        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>
+.order-name span{display: block;}
+.order-name span.dec{color: #999;font-size: 12px;}
+.goods-img{display: flex;gap: 5px;}
+.goods-img .name span{display: block;}
+.goods-img .name span.dec{color: #999;font-size: 12px;}
+.desc{
+    color: #999;
+    font-size: 12px;
+}
+.flex-btn{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 5px;
+}
+.flex-tags {
+    display: flex;
+    gap: 2px;
+    flex-wrap: wrap;
+}
+.flex-btn .el-button.is-text{
+    margin-left: 0;
+}
+.flex-tags-size{
+    font-size: 12px;
+}
+.flex-tags-size span{
+    display: block;
+    margin-bottom: 5px;
+    color: #999;
+}
+.flex-end{display: flex;align-items: center;justify-content: space-between;}
+</style>

+ 48 - 0
src/views/manage/shop/print/index.vue

@@ -0,0 +1,48 @@
+<template>
+    <el-container class="flex-column">
+        <div class="table-search">
+            <search @success="handleSuccess"></search>
+            <el-alert type="warning" show-icon title="栏目提醒" description="1、一台打印机可绑定多个门店,但一个门店只可绑定一台打印机。2、打印机状态并非实时更新,需要查看状态的时候请点击【查询状态】按钮,等待1-3分钟后刷新列表查看" :closable="false" style="margin-bottom: 10px;"></el-alert>
+            <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>

+ 154 - 0
src/views/merchant/order/after/components/detail.vue

@@ -0,0 +1,154 @@
+<template>
+    <el-drawer v-model="visible" title="在线客服" size="500px" destroy-on-close :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 class="nopadding" v-loading="loading">
+                <div class="order-detail" v-if="orderData">
+                    <div class="ts-header">
+                        <div class="ts-h-title">投诉门店</div>
+                        <div class="store-info">
+                            <div class="store-left">
+                                <div class="name">{{orderData.store.poi_name}}</div>
+                                <div class="address">{{orderData.store.poi_address}}</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="ts-body">
+                        <div class="order-status" v-if="orderData.status == 0">
+                            <img src="https://jymini.oss-cn-guangzhou.aliyuncs.com/mini/complaint-2.svg" />
+                        </div>
+                        <div class="order-status" v-if="orderData.status == 1">
+                            <img src="https://jymini.oss-cn-guangzhou.aliyuncs.com/mini/complaint-3.svg" />
+                        </div>
+                        <div class="ts-form">
+                            <div class="ts-form-item">
+                                <div class="form-title">具体问题</div>
+                                <div class="form-input">
+                                    {{orderData.question}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">问题订单</div>
+                                <div class="form-input">
+                                    {{orderData.order_sn}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">联系方式</div>
+                                <div class="form-input">
+                                    {{orderData.mobile}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">详细补充</div>
+                                <div class="form-input-text">
+                                    {{orderData.content}}
+                                </div>
+                            </div>
+                            <div class="ts-form-item">
+                                <div class="form-title">回复结果 </div>
+                                <div class="form-input-text">
+                                    <div class="data" v-html="orderData.remark"></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </el-main>
+        </el-container>
+    </el-drawer>
+</template>
+
+<script>
+export default {
+    data(){
+        return {
+            loading: false,
+            isSaveing: false,
+            isDoneing:false,
+            mode:"add",
+            titleMap:{
+                add:"投诉详情",
+                edit:"投诉详情"
+            },
+            visible:false,
+            orderData:null,
+			orderTime:null
+        }
+    },
+    methods: {
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        //表单注入数据
+        setData(data){
+            this.formData = JSON.parse(JSON.stringify(data));
+            this.getOrderDetail()
+        },
+        doneOrder(){
+            this.$confirm(`手动核销订单后,该订单不支持线上退款,如需退款请线下沟通后进行`, '提示', {
+                type: 'warning'
+            }).then(async ()=>{
+                this.isDoneing = true;
+                var resp = await this.$API.merOrder.done.post({"id":this.orderData.order_sn});
+                this.isDoneing = false;
+                if (resp.code == 0) {
+                    return this.$message.error(resp.msg);
+                }
+                this.$message.success(resp.msg);
+                this.visible = false;
+                this.$emit("success");
+            }).catch(()=>{})
+        },
+        async getOrderDetail(){
+            this.loading = true;
+            var resp = await this.$API.merComplaint.detail.get({"id":this.formData.id});
+            this.loading = false;
+            if (resp.code == 0) {
+                this.$message.error(resp.msg);
+                this.visible = false;
+                return ;
+            }
+            this.orderData = resp.data;
+            this.orderTime = resp.data.end_time
+        },
+        sendAddress(){
+            this.$emit("success",'address',this.orderData);
+        }
+    }
+}
+</script>
+
+<style>
+.nopadding{background: #f8f8f8 !important;}
+.ts-header{padding: 10px 0;margin-bottom: 10px;}
+.ts-header .ts-h-title{font-size: 14px;color: #666;padding: 10px;}
+.ts-header .store-info{background-color: #fff;padding: 10px;}
+.ts-header .store-info .store-left .name{font-size: 30rpx;color: #333;font-weight: bold;line-height: 40px;}
+.ts-header .store-info .store-left .address{font-size: 12px;color: #999;}
+.ts-body{background-color: #fff;padding: 10px;position: relative;}
+
+.ts-body .order-status{position: absolute;right: 10px;top: 10px;}
+.ts-body .order-status img{width: 110px;height: 110px;transform: rotate(45deg);}
+
+.ts-form .ts-form-item{border-bottom: 1px solid #f8f8f8;padding-bottom: 10upx;}
+.ts-form .ts-form-item:last-child{border-bottom: 0;}
+.ts-form .ts-form-item .form-title{color: #000;font-size: 14px;padding: 10px 0;}
+.ts-form .ts-form-item .form-title text{color: #f00;margin-left: 10upx;}
+.ts-form .ts-form-item .form-input{font-size: 28upx;color: #333;height: 40px;display: flex;align-items: center;}
+.ts-form .ts-form-item .ts-input{height: 100%;border: 0;box-shadow: none;width: 100%;}
+.ts-form .form-input-textarea{height: 200upx;font-size: 14px;color: #333;}
+.form-input-text{padding: 10px 0;}
+.form-input-text img{width: 100%;}
+</style>

+ 96 - 0
src/views/merchant/order/after/components/form.vue

@@ -0,0 +1,96 @@
+<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="投诉事项">
+                        <el-input v-model="formData.question" readonly disabled clearable placeholder="请输入"></el-input>
+                        <div class="el-form-item-msg"></div>
+                    </el-form-item>
+                    <el-form-item label="投诉订单">
+                        <el-input v-model="formData.order_sn" readonly disabled clearable placeholder="请输入"></el-input>
+                        <div class="el-form-item-msg"></div>
+                    </el-form-item>
+                    <el-form-item label="回复" prop="remark" v-if="formData.status == 0">
+                        <sc-editor v-model="formData.remark" placeholder="请输入" :height="400"></sc-editor>
+                        <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 size="large" type="primary" :loading="isSaveing" @click="submitEdit()">保 存</el-button>
+            </el-footer>
+        </el-container>
+    </el-drawer>
+</template>
+
+<script>
+import { defineAsyncComponent } from 'vue';
+const scEditor = defineAsyncComponent(() => import('@/components/scEditor'));
+export default {
+    components: {
+        scEditor
+    },
+    data(){
+        return {
+            loading: false,
+            isSaveing: false,
+            mode:"add",
+            titleMap:{
+                add:"投诉回复",
+                edit:"投诉回复"
+            },
+            visible:false,
+            formData:{},
+            rules:{
+                remark: [
+                    {required: true, message: '请输入'}
+                ],
+            }
+        }
+    },
+    methods:{
+        quickGoods(){
+            this.quickData.visible = true;
+        },
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        //表单注入数据
+        setData(data){
+            this.formData = JSON.parse(JSON.stringify(data));
+        },
+        async submitEdit(){
+            var loading = this.$loading()
+            this.isSaveing = true;
+            var resp = await this.$API.merComplaint.save.post(this.formData)
+            this.isSaveing = false;
+            loading.close();
+            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>
+.goods-live{width: 100%;margin-top: 10px;}
+.flex-loading{display: flex;flex-direction: column;flex: 1;}
+</style>

+ 63 - 0
src/views/merchant/order/after/components/search.vue

@@ -0,0 +1,63 @@
+<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.order_sn" 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-input v-model="searchKey.mobile" 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" style="flex: 0 0 186px;">
+                    <el-button type="primary" icon="el-icon-search" @click="searchForm">搜索</el-button>
+                    <sc-file-export :apiObj="$API.common.exportFile" :data="searchKey" fileName="订单列表" async>
+						<template #default="{open}">
+							<el-button type="danger" icon="sc-icon-download" @click="open">导出</el-button>
+						</template>
+					</sc-file-export>
+                </div>
+            </div>
+        </el-form>
+    </fieldset>
+</template>
+
+<script>
+import scFileExport from '@/components/scFileExport'
+export default {
+    components: {
+        scFileExport
+    },
+    data(){
+        return {
+            searchKey:{}
+        }
+    },
+    methods: {
+        searchForm(){
+            this.$emit("success",this.searchKey);
+        }
+    }
+}
+</script>

+ 146 - 0
src/views/merchant/order/after/components/table.vue

@@ -0,0 +1,146 @@
+<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="260" fixed="left" prop="goods_id">
+            <template #default="scope">
+                <div class="order-name">
+                    <span>{{ scope.row.order_sn }}</span>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="投诉事项" prop="types" width="220">
+            <template #default="scope">
+                {{ scope.row.question?scope.row.question:'-' }}
+            </template>
+        </el-table-column>
+        <el-table-column label="联系方式" prop="types" width="100">
+            <template #default="scope">
+                {{ scope.row.mobile?scope.row.mobile:'-' }}
+            </template>
+        </el-table-column>
+        <el-table-column label="商品编码" width="340" prop="name">
+            <template #default="scope">
+                <div class="goods-img">
+                    <div class="name">
+                        <span>{{ scope.row.orders?scope.row.orders.product_id:'-' }}</span>
+                    </div>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="状态" prop="types" width="160" align="left">
+            <template #default="scope">
+                <div class="order-name" v-if="scope.row.status == 0">
+                    <span>待处理</span>
+                </div>
+                <div class="order-name" v-if="scope.row.status == 1">
+                    <span>已完成</span>
+                </div>
+            </template>
+        </el-table-column>
+        <el-table-column label="发起时间" prop="create_at" width="160"></el-table-column>
+        <el-table-column label="处理时间" prop="last_at" width="160">
+            <template #default="scope">
+                {{ scope.row.last_at?scope.row.last_at:'-' }}
+            </template>
+        </el-table-column>
+        <el-table-column label="操作" width="140" align="left" fixed="right">
+            <template #default="scope">
+                <el-button-group>
+                    <el-button size="small" text @click="table_view(scope.row)">详情</el-button>
+                    <el-button type="danger" v-if="scope.row.status==0" text size="small" @click="table_del(scope.row)">回复</el-button>
+                </el-button-group>
+            </template>
+        </el-table-column>
+    </scTable>
+    <orderDetail ref="orderDetail" @success="orderSuccess"></orderDetail>
+    <formMain ref="formMain" @success="orderSuccess"></formMain>
+</template>
+
+<script>
+import orderDetail from "./detail";
+import formMain from "./form";
+export default {
+    components:{
+        orderDetail,formMain
+    },
+    data(){
+        return {
+            list: {
+                apiObj: this.$API.merComplaint.list
+            },
+            dataSelect:[],
+            dataSelectFull:[],
+            searchKey:{}
+        }
+    },
+    methods: {
+        orderSuccess(){
+            this.$refs.table.refresh()
+        },
+        table_view(data){
+            this.$nextTick(() => {
+                this.$refs.orderDetail.open("edit").setData(data)
+            })
+        },
+        table_del(data){
+            this.$nextTick(() => {
+                this.$refs.formMain.open("edit").setData(data)
+            })
+        },
+        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>
+.order-name span{display: block;}
+.order-name span.dec{color: #999;font-size: 12px;}
+.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;}
+</style>

+ 45 - 1
src/views/merchant/order/after/index.vue

@@ -1,3 +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>
 
-</template>
+<script>
+import search from './components/search';
+// import optionBtn from './components/option';
+import tablePage from './components/table';
+export default {
+    components: {
+        search,tablePage
+    },
+    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>

+ 10 - 1
src/views/merchant/order/index/components/search.vue

@@ -42,8 +42,13 @@
                     </el-row>
                 </div>
                 <div class="form-line"></div>
-                <div class="form-right">
+                <div class="form-right" style="flex: 0 0 186px;">
                     <el-button type="primary" icon="el-icon-search" @click="searchForm">搜索</el-button>
+                    <sc-file-export :apiObj="$API.common.exportFile" :data="searchKey" fileName="订单列表" async>
+						<template #default="{open}">
+							<el-button type="danger" icon="sc-icon-download" @click="open">导出</el-button>
+						</template>
+					</sc-file-export>
                 </div>
             </div>
         </el-form>
@@ -51,7 +56,11 @@
 </template>
 
 <script>
+import scFileExport from '@/components/scFileExport'
 export default {
+    components: {
+        scFileExport
+    },
     data(){
         return {
             searchKey:{}

+ 10 - 1
src/views/service/order/index/components/search.vue

@@ -42,8 +42,13 @@
                     </el-row>
                 </div>
                 <div class="form-line"></div>
-                <div class="form-right">
+                <div class="form-right" style="flex: 0 0 186px;">
                     <el-button type="primary" icon="el-icon-search" @click="searchForm">搜索</el-button>
+                    <sc-file-export :apiObj="$API.common.exportFile" :data="searchKey" fileName="订单列表" async>
+						<template #default="{open}">
+							<el-button type="danger" icon="sc-icon-download" @click="open">导出</el-button>
+						</template>
+					</sc-file-export>
                 </div>
             </div>
         </el-form>
@@ -51,7 +56,11 @@
 </template>
 
 <script>
+import scFileExport from '@/components/scFileExport'
 export default {
+    components: {
+        scFileExport
+    },
     data(){
         return {
             searchKey:{}