瀏覽代碼

问题反馈及订单修改

zuypeng 1 年之前
父節點
當前提交
6fc6568209

+ 1 - 1
admin-web/index.html

@@ -10,7 +10,7 @@
     <title>iWash管理后台</title>
 
 </head>
-<body>
+<body style="border-top: 1px solid #ccc;">
 <div id="app"></div>
 <script type="module" src="/src/main.ts"></script>
 </body>

+ 2 - 1
admin-web/src/components/form/ExtRender.vue

@@ -5,13 +5,14 @@ export default {
   name: 'ExtRender',
   props: {
     data: Object|String,
+    rowData: Object|String,
     renderFunc:Function
   },
   //h(节点名,属性,子组件[]或文本)
   //通过render创建组件而不是通过template。没有:绑定参数的操作而是直接{}绑定参数
   render() {
     return (
-        this.renderFunc(h,this.data)
+        this.renderFunc(h,this.data,this.rowData)
     )
   }
 }

+ 1 - 0
admin-web/src/components/form/ExtTable.vue

@@ -62,6 +62,7 @@
               <ext-render
                   class="z-w150"
                   :data="row[field.prop]"
+                  :row-data="row"
                   :render-func="field.render"/>
             </template>
             <template v-else-if="field.type==='rich'">

+ 3 - 3
admin-web/src/utils/field.ts

@@ -59,9 +59,9 @@ const fieldUtil = {
             //查询框分类  文本、数字、下拉、部门、用户、搜索框
             let fconfig = {
                 ...f,
-                xs: 4,
-                sm: 3,
-                md: 3,
+                xs: 6,
+                sm: 4,
+                md: 4,
                 lg: 2,
                 xl: 1,
                 rules: [],

+ 5 - 5
admin-web/src/utils/u.ts

@@ -583,7 +583,7 @@ const u = {
             }
         },
         fmtDateTime(t: any) {
-            if (t == null) {
+            if (!t) {
                 return "-";
             }
             let date = new Date(t);
@@ -600,7 +600,7 @@ const u = {
             }).join(':')
         },
         fmtDate(t: any) {
-            if (t == null) {
+            if (!t) {
                 return "-";
             }
             let date = new Date(t);
@@ -646,13 +646,13 @@ const u = {
             // 构建时长字符串
             let durationStr = '';
             if (days > 0) {
-                durationStr += `${days}天 `;
+                durationStr += `${days}天`;
             }
             if (hours > 0) {
-                durationStr += `${hours} `;
+                durationStr += `${hours}时`;
             }
             if (minutes > 0) {
-                durationStr += `${minutes}分`;
+                durationStr += `${minutes}分`;
             }
             if (seconds > 0 || !durationStr) { // 如果没有小时和分钟,至少显示秒
                 durationStr += `${seconds}秒`;

+ 42 - 45
admin-web/src/views/admin/feedback/dialog.vue

@@ -20,31 +20,53 @@
           size="default"
           label-width="100px"
           class="mt5">
-        <el-form-item label="附件列表" prop="attachList">
+        <el-form-item label="反馈标题" prop="title" class="w100">
           <el-input
+              v-model="state.ruleForm.title"
+              placeholder="反馈标题"
+              clearable
+              readonly
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="纠错类型" prop="type">
+          <ext-d-label type="Feedback.type" :model-value="state.ruleForm.type"></ext-d-label>
+        </el-form-item>
+        <el-form-item label="提交时间" prop="submitTime">
+          {{u.fmt.fmtDateTime(state.ruleForm.submitTime)}}
+        </el-form-item>
+        <el-form-item label="反馈人" prop="submitUserId">
+          {{state.ruleForm.submitUserId}}
+        </el-form-item>
+        <el-form-item label="附件列表" prop="attachList" class="w100">
+          <ext-image :src-list="state.attachList"></ext-image>
+<!--          <el-input
               v-model="state.ruleForm.attachList"
               placeholder="附件列表"
               clearable
               class="wd350">
-          </el-input>
+          </el-input>-->
         </el-form-item>
-        <el-form-item label="详情描述" prop="content">
+        <el-form-item label="问题描述" prop="content" class="w100">
           <el-input
               v-model="state.ruleForm.content"
-              placeholder="详情描述"
+              placeholder="问题描述"
               clearable
+              type="textarea"
+              readonly
               class="wd350">
           </el-input>
         </el-form-item>
-        <el-form-item label="回复内容" prop="replyContent">
+        <el-form-item label="回复内容" prop="replyContent" class="w100">
           <el-input
               v-model="state.ruleForm.replyContent"
-              placeholder="回复内容"
+              placeholder="请填写回复内容"
               clearable
+              type="textarea"
               class="wd350">
           </el-input>
         </el-form-item>
-        <el-form-item label="回复时间" prop="replyTime">
+        <el-form-item label="回复时间" prop="replyTime" v-if="state.ruleForm.replyTime">
           <el-input
               v-model="state.ruleForm.replyTime"
               placeholder="回复时间"
@@ -52,7 +74,7 @@
               class="wd350">
           </el-input>
         </el-form-item>
-        <el-form-item label="回复人" prop="replyUserId">
+        <el-form-item label="回复人" prop="replyUserId"  v-if="state.ruleForm.replyTime">
           <el-input
               v-model="state.ruleForm.replyUserId"
               placeholder="回复人"
@@ -60,52 +82,22 @@
               class="wd350">
           </el-input>
         </el-form-item>
-        <el-form-item label="状态" prop="status">
+<!--        <el-form-item label="状态" prop="status">
           <el-input
               v-model="state.ruleForm.status"
               placeholder="状态"
               clearable
               class="wd350">
           </el-input>
-        </el-form-item>
-        <el-form-item label="提交时间" prop="submitTime">
-          <el-input
-              v-model="state.ruleForm.submitTime"
-              placeholder="提交时间"
-              clearable
-              class="wd350">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="反馈人" prop="submitUserId">
-          <el-input
-              v-model="state.ruleForm.submitUserId"
-              placeholder="反馈人"
-              clearable
-              class="wd350">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="反馈标题" prop="title">
-          <el-input
-              v-model="state.ruleForm.title"
-              placeholder="反馈标题"
-              clearable
-              class="wd350">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="纠错类型" prop="type">
-          <el-input
-              v-model="state.ruleForm.type"
-              placeholder="纠错类型"
-              clearable
-              class="wd350">
-          </el-input>
-        </el-form-item>
+        </el-form-item>-->
+
+
       </el-form>
 
       <template #footer>
 				<span class="dialog-footer">
 					<el-button @click="onCancel" size="default">取 消</el-button>
-					<el-button :loading="state.btnLoading" type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
+					<el-button  v-if="state.dialog.type!='view'" :loading="state.btnLoading" type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
 				</span>
       </template>
     </el-dialog>
@@ -117,6 +109,8 @@ import {defineAsyncComponent, reactive, onMounted, ref} from 'vue';
 import {Msg} from "/@/utils/message";
 import {$body, $get} from "/@/utils/request";
 import u from '/@/utils/u'
+import ExtImage from "/@/components/form/ExtImage.vue";
+import ExtDLabel from "/@/components/form/ExtDLabel.vue";
 
 
 // 定义子组件向父组件传值/事件
@@ -135,6 +129,7 @@ const initState = () => ({
     submitTxt: '',
   },
   rules: {},
+  attachList:[]
 })
 
 // 定义变量内容
@@ -143,8 +138,9 @@ const state = reactive(initState());
 
 // 打开弹窗
 const open = (action: string = 'add', row: any) => {
-  state.dialog.title = u.dialog.actions[action].title + "『纠错反馈』"
-  state.dialog.submitTxt = u.dialog.actions[action].btn + "『纠错反馈』"
+  state.dialog.title =  "回复『纠错反馈』"
+  state.dialog.submitTxt =  "回复『纠错反馈』"
+  state.dialog.type=action;
   state.dialog.isShowDialog = true;
   if (action !== 'add') {
     loadData(row.id);
@@ -189,6 +185,7 @@ const handleFormChange = (formData: any) => {
 const loadData = (id: number) => {
   $get(`feedback/detail/${id}`).then((res: any) => {
     state.ruleForm = res;
+    state.attachList = JSON.parse(res.attachList).map(k=>k.url)
   })
 }
 

+ 22 - 23
admin-web/src/views/admin/feedback/index.vue

@@ -57,8 +57,8 @@
 </template>
 
 <script setup lang="ts" name="FeedbackList">
-import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurrentInstance,nextTick,onBeforeUnmount} from 'vue';
-import {$body,$get} from "/@/utils/request";
+import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurrentInstance, nextTick, onBeforeUnmount} from 'vue';
+import {$body, $get} from "/@/utils/request";
 import u from '/@/utils/u'
 import {Msg} from "/@/utils/message";
 import {Session} from "/@/utils/storage";
@@ -89,28 +89,21 @@ const state = reactive({
     total: 0
   },
   tableData: {
-    height:500,
-    data: [] as Array < any >,
+    height: 500,
+    data: [] as Array<any>,
     loading: false
   },
   importConfig: {},
   exportConfig: {},
   columns: [
-    {type: 'selection', width: 60, align: 'center', fixed: 'left'},
-    {label: '详情描述', prop: 'content', query: true, type: 'text', resizable: true},
-    {label: '创建时间', prop: 'createTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
-    {label: '回复内容', prop: 'replyContent', query: true, type: 'text', resizable: true},
-    {label: '回复时间', prop: 'replyTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
-    {label: '状态', prop: 'status', sortable: 'custom', align: 'center', query: true, type: 'dict', conf: {dict: 'Feedback.status'}},
-    {label: '提交时间', prop: 'submitTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
     {
-      label: '反馈标题', prop: 'title', query: true, type: "text", resizable: true,
+      label: '反馈标题', width:180,prop: 'title', query: true, type: "text", resizable: true,
       render: (h: any, row: any) => {
         return h('div', null, [
           h('div', {
-            style:{
-              cursor:'pointer',
-              color:'var(--el-color-primary-light-1)'
+            style: {
+              cursor: 'pointer',
+              color: 'var(--el-color-primary-light-1)'
             },
             onClick: () => {
               onRowClick('view', row)
@@ -118,10 +111,16 @@ const state = reactive({
           }, row)])
       }
     },
-    {label: '纠错类型', prop: 'type', query: true, type: '', resizable: true},
+    {label: '纠错类型', width:120, prop: 'type', query: true, type: 'dict', resizable: true,conf: {dict: 'Feedback.type'}},
+    {label: '详情描述', width:300,prop: 'content', query: false, type: 'text', resizable: true},
+    {label: '状态', width:120, prop: 'status', sortable: 'custom', align: 'center', query: true, type: 'dict', conf: {dict: 'Feedback.status'}},
+    {label: '回复内容', width:300,prop: 'replyContent', query: false, type: 'text', resizable: true},
+    {label: '回复时间', prop: 'replyTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
+    {label: '提交时间', prop: 'submitTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
     {label: '更新时间', prop: 'updateTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
-    {                label: '操作', prop: 'action', type: 'render', width: 180, align: 'center', fixed: 'right',
-      render: (h: any, row: any) => {
+    {
+      label: '操作', prop: 'action', type: 'render1', width: 180, align: 'center', fixed: 'right',
+      render: (h: any, row: any,rowData:any) => {
         return (
             h('div', null, [
               proxy.$auth('feedback.modify') ?
@@ -130,9 +129,9 @@ const state = reactive({
                     text: true,
                     size: 'small',
                     onClick: () => {
-                      onRowClick('edit', row)
+                      onRowClick('edit', rowData)
                     }
-                  }, () => '编辑') : '',
+                  }, () => '回复') : '',
               proxy.$auth('feedback.remove') ?
                   h(ElButton, {
                     type: 'danger',
@@ -178,18 +177,18 @@ onBeforeMount(() => {
 onMounted(() => {
   loadData();
 
-  nextTick(()=>{
+  nextTick(() => {
     let bodyHeight = document.body.clientHeight;
     let queryHeight = queryRef.value.$el.clientHeight;
     state.tableData.height = bodyHeight - queryHeight - 220
   })
 
-  mittBus.on("feedback.refresh",()=>{
+  mittBus.on("feedback.refresh", () => {
     loadData();
   })
 });
 
-onBeforeUnmount(()=>{
+onBeforeUnmount(() => {
   mittBus.off("feedback.refresh")
 })
 

+ 26 - 13
admin-web/src/views/admin/ordering/index.vue

@@ -26,6 +26,20 @@
 .order-detail{
   margin-left: 20px;
 }
+
+.el-table {
+  &:deep(.el-table__body-wrapper) {
+    width: 100% !important;
+    height: calc(100% - 108px) !important; // 表格高度减去表头的高度
+
+    .el-scrollbar__bar.is-horizontal {
+      height: 10px;
+      left: 2px;
+    }
+  }
+
+
+}
 </style>
 <template>
   <div class="system-container layout-padding">
@@ -143,8 +157,6 @@ import mittBus from '/@/utils/mitt';
 import ExtDLabel from "/@/components/form/ExtDLabel.vue";
 
 
-const WashOrderDialog = defineAsyncComponent(() => import("/@/views/admin/ordering/dialog.vue"));
-
 //定义引用
 const queryRef = ref();
 const washOrderDialogRef = ref();
@@ -167,14 +179,15 @@ const state = reactive({
   columns: [
     {type: 'expand', width: 60, align: 'center', fixed: 'left'},
     {width: 260, label: '订单号', prop: 'orderId', query: true, type: 'text', resizable: true, fixed: 'left'},
-    {width: 160, label: '消费总额', prop: 'amount', query: false, resizable: true, fixed: 'left'},
+    {width: 260, label: '用户', prop: 'userId', query: true, type: 'text', resizable: true},
+    {width: 160, label: '消费总额', prop: 'amount', query: false, resizable: true},
     {width: 160, label: '应收金额', prop: 'amountReceivable', query: false, type: '', resizable: true},
     {width: 160, label: '实收金额', prop: 'amountReceived', query: false, type: 'text', resizable: true},
     {width: 160, label: '卡内余额', prop: 'cardBalance', query: false, type: 'text', resizable: true},
     {
       width: 160, label: '卡过期时间',
       prop: 'cardExpired',
-      query: true,
+      query: false,
       sortable: 'custom',
       type: 'datetime',
       resizable: true,
@@ -205,7 +218,7 @@ const state = reactive({
     {width: 160, label: '设备名称', prop: 'deviceName', query: false, type: 'text', resizable: true},
     {width: 160, label: '优惠金额', prop: 'discountAmount', query: false, type: '', resizable: true},
     {width: 160, label: '优惠金额', prop: 'discountMoney', query: false, type: '', resizable: true},
-    {width: 160, label: '优惠方式', prop: 'discountType', query: true, type: 'dict', conf: {dict: 'Activity.discountType'}, resizable: true},
+    {width: 160, label: '优惠方式', prop: 'discountType', query: false, type: 'dict', conf: {dict: 'Activity.discountType'}, resizable: true},
     {width: 160, label: '结束时间', prop: 'endTime', query: false, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDateTime(val)}},
     {
       width: 160, label: '设备空闲关机倒计时剩余时间',
@@ -214,18 +227,18 @@ const state = reactive({
       type: 'text',
       resizable: true
     },
-    {width: 160, label: '发票状态', prop: 'invoiceStatus', query: true, type: 'dict', conf: {dict: 'Invoice.status'}, resizable: true},
+    {width: 160, label: '发票状态', prop: 'invoiceStatus', query: false, type: 'dict', conf: {dict: 'Invoice.status'}, resizable: true},
     {width: 160, label: '会员折扣比例', prop: 'memberDiscount', query: false, type: 'text', resizable: true},
-    {width: 160, label: '开机方式', prop: 'openType', query: true, type: 'dict', conf: {dict: 'Order.openType'}, resizable: true},
+    {width: 160, label: '开机方式', prop: 'openType', query: false, type: 'dict', conf: {dict: 'Order.openType'}, resizable: true},
     {width: 160, label: '订单操作剩余操作时间(单位秒)', prop: 'operationRemainTime', query: false, type: 'text', resizable: true},
     {width: 160, label: '本机订单号', prop: 'orderIdLocal', query: false, type: 'text', resizable: true},
-    {width: 160, label: '订单状态', prop: 'orderStatus', query: true, type: 'dict', conf: {dict: 'Order.status'}, resizable: true},
+    {width: 160, label: '订单状态', prop: 'orderStatus', query: false, type: 'dict', conf: {dict: 'Order.status'}, resizable: true},
     {width: 160, label: '支付状态', prop: 'payStatus', query: true, type: 'dict', conf: {dict: 'Order.pay'}, resizable: true},
     {width: 160, label: '本次开机的预付金额', prop: 'prepayMoney', query: true, type: '', resizable: true},
-    {width: 160, label: '产品key', prop: 'productKey', query: true, type: 'text', resizable: true},
+    {width: 160, label: '产品key', prop: 'productKey', query: false, type: 'text', resizable: true},
     {width: 160, label: '开始时间', prop: 'startTime', query: false, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
     {width: 160, label: '站点ID', prop: 'stationId', query: true, type: 'text', resizable: true},
-    {width: 160, label: '停机原因', prop: 'stopReason', query: false, type: 'text', resizable: true},
+    {label: '停机原因', prop: 'stopReason', query: false, type: 'text', resizable: true},
     {
       width: 160,
       label: '更新时间',
@@ -307,11 +320,11 @@ const loadData = (refresh: boolean = false) => {
 
     list.forEach((item:any)=>{
       if(item.detail){
-        let parse = JSON.parse(item.detail);
-        item.detail = parse.filter((k:any)=>k.amount>0||k.seconds>0).map((s:any)=>{
+        // let parse = JSON.parse(item.detail);
+        item.detail = item.detail.filter((k:any)=>k.amount>0||k.seconds>0).map((s:any)=>{
           return {
             name:u.fmt.fmtDict(s.name,'Order.feeType'),
-            seconds:u.fmt.fmtDuration(s.seconds),
+            seconds:u.fmt.fmtDuration(s.seconds*1000),
             price:u.fmt.fmtMoney(s.price),
             amount:u.fmt.fmtMoney(s.amount),
           }

+ 0 - 2
admin-web/src/views/admin/station/device/dialog.vue

@@ -123,8 +123,6 @@ import u from '/@/utils/u'
 import ExtDSelect from "/@/components/form/ExtDSelect.vue";
 import ExtSelect from "/@/components/form/ExtSelect.vue";
 
-// 引入组件
-const ExtDetailForm = defineAsyncComponent(() => import('/@/components/form/ExtDetailForm.vue'));
 
 // 定义子组件向父组件传值/事件
 const emit = defineEmits(['refresh']);

+ 1 - 0
car-wash-miniapp/src/main/java/com/kym/miniapp/config/SaTokenConfigure.java

@@ -23,6 +23,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
                 .excludePathPatterns(
                         "/error",
                         "/user/wxLogin",
+                        "/dict/list",
                         "/user/refresh",
                         "/payment/notify",
                         "/payment/refundNotify"

+ 9 - 1
car-wash-service/src/main/java/com/kym/service/admin/impl/FeedbackServiceImpl.java

@@ -12,6 +12,8 @@ import com.kym.service.admin.FeedbackService;
 import com.kym.service.mybatisplus.MyBaseServiceImpl;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
+
 /**
  * <p>
  * 纠错反馈 服务实现类
@@ -26,6 +28,8 @@ public class FeedbackServiceImpl extends MyBaseServiceImpl<FeedbackMapper, Feedb
     @Override
     public int add(Feedback feedback) {
         StpUtil.checkLogin();
+        feedback.setSubmitUserId(StpUtil.getLoginIdAsLong());
+        feedback.setSubmitTime(LocalDateTime.now());
         save(feedback);
         return 0;
     }
@@ -37,7 +41,11 @@ public class FeedbackServiceImpl extends MyBaseServiceImpl<FeedbackMapper, Feedb
 
     @Override
     public void reply(Feedback feedback) {
-
+        Feedback old = getById(feedback.getId());
+        old.setReplyContent(feedback.getReplyContent());
+        old.setReplyUserId(StpUtil.getLoginIdAsLong());
+        old.setReplyTime(LocalDateTime.now());
+        updateById(old);
     }
 
     @Override