Prechádzať zdrojové kódy

管理后台优化修改

zuypeng 1 rok pred
rodič
commit
62206cc68e
29 zmenil súbory, kde vykonal 1854 pridanie a 355 odobranie
  1. 2 2
      admin-web/src/components/form/ExtBoolean.vue
  2. 20 7
      admin-web/src/components/form/ExtQueryForm.vue
  3. 78 0
      admin-web/src/components/qrcode/index.vue
  4. 1 1
      admin-web/src/layout/logo/index.vue
  5. 0 1
      admin-web/src/main.ts
  6. 78 30
      admin-web/src/router/route.ts
  7. 4 3
      admin-web/src/utils/authFunction.ts
  8. 7 3
      admin-web/src/views/admin/account/index.vue
  9. 5 4
      admin-web/src/views/admin/finance/index.vue
  10. 12 6
      admin-web/src/views/admin/finance/splitRecord.vue
  11. 15 65
      admin-web/src/views/admin/ordering/index.vue
  12. 444 0
      admin-web/src/views/admin/platform/deviceConfig/dialog.vue
  13. 233 0
      admin-web/src/views/admin/platform/deviceConfig/index.vue
  14. 159 0
      admin-web/src/views/admin/platform/rate/dialog.vue
  15. 255 0
      admin-web/src/views/admin/platform/rate/index.vue
  16. 29 44
      admin-web/src/views/admin/station/account/index.vue
  17. 111 0
      admin-web/src/views/admin/station/device/config.vue
  18. 29 0
      admin-web/src/views/admin/station/device/index.vue
  19. 306 172
      admin-web/src/views/admin/station/list/dialog.vue
  20. 5 3
      admin-web/src/views/admin/station/list/index.vue
  21. 8 0
      car-wash-admin/src/main/java/com/kym/admin/controller/DeviceConfigController.java
  22. 2 2
      car-wash-admin/src/main/java/com/kym/admin/controller/StationFeeRateController.java
  23. 4 0
      car-wash-entity/src/main/java/com/kym/entity/DeviceConfig.java
  24. 13 0
      car-wash-entity/src/main/java/com/kym/entity/queryParams/DeviceConfigQueryParams.java
  25. 3 1
      car-wash-mapper/src/main/resources/mappers/DeviceConfigMapper.xml
  26. 2 1
      car-wash-miniapp/src/main/resources/application-dev.yml
  27. 4 0
      car-wash-service/src/main/java/com/kym/service/DeviceConfigService.java
  28. 15 0
      car-wash-service/src/main/java/com/kym/service/impl/DeviceConfigServiceImpl.java
  29. 10 10
      car-wash-service/src/main/java/com/kym/service/wechat/impl/WxPayServiceImpl.java

+ 2 - 2
admin-web/src/components/form/ExtBoolean.vue

@@ -1,7 +1,7 @@
 <style scoped lang="scss">
 .text {
   display: inline-block;
-  padding: 3px 6px;
+  padding: 5px 6px 1px 6px;
   font-size: 11px;
   font-weight: 600;
   line-height: 1;
@@ -9,7 +9,7 @@
   text-align: center;
   white-space: nowrap;
   vertical-align: baseline;
-  border-radius: 10px;
+  border-radius: 2px;
   margin-top: 3px;
   width: auto;
   color: #FF5722;

+ 20 - 7
admin-web/src/components/form/ExtQueryForm.vue

@@ -175,7 +175,7 @@
 </template>
 
 <script setup lang="ts" name="ExtForm">
-import {computed, defineAsyncComponent, onMounted, ref} from 'vue';
+import {computed, defineAsyncComponent, onMounted, ref,watch,nextTick} from 'vue';
 import type {FormInstance} from 'element-plus';
 import fieldUtil from "/@/utils/field";
 import ExtDSelect from "/@/components/form/ExtDSelect.vue";
@@ -220,8 +220,6 @@ const rules = computed(() => {
 })
 
 // 定义变量内容
-const import_ref = ref();
-const formRulesOneRef = ref<FormInstance>();
 const state = ref({
   cols: [] as Array<any>,
   form: {},
@@ -229,6 +227,21 @@ const state = ref({
   exportEnable: false,
 });
 
+watch(()=>props.modelValue,(nv,ov)=>{
+  if(nv!=ov){
+    console.log(nv)
+    // nextTick(()=>{
+    //   let val = {...props.modelValue}
+    //   state.value.cols.forEach((col: any) => {
+    //     val[col.prop] = null;
+    //   })
+    //   //console.log(val)
+    //   state.value.form = val;
+    // })
+  }
+
+})
+
 onMounted(() => {
   if (props.importConfig && props.importConfig.url) {
     state.value.importEnable = true;
@@ -241,10 +254,10 @@ onMounted(() => {
   state.value.cols = fieldUtil.toFormQueryField(props.columns);
   //console.log(state.value.cols)
   let val = {...props.modelValue}
-  state.value.cols.forEach((col: any) => {
-    val[col.prop] = null;
-  })
-  //console.log(val)
+  // state.value.cols.forEach((col: any) => {
+  //   val[col.prop] = null;
+  // })
+  console.log(val)
   state.value.form = val;
 })
 

+ 78 - 0
admin-web/src/components/qrcode/index.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="login-qrcode-container">
+    <div ref="qrcodeRef"></div>
+    <div class="font12 mt20 login-msg">
+      <i class="iconfont icon-saoyisao mr5"></i>
+      <span>{{ text }}</span>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="Qrcode">
+import {ref, onMounted, nextTick} from 'vue';
+import QRCode from 'qrcodejs2-fixes';
+
+// 定义变量内容
+const qrcodeRef = ref<HTMLElement | null>(null);
+
+
+const props = defineProps({
+  link: {
+    type:String,
+  },
+  text:{
+    type:String
+  }
+
+})
+
+// 初始化生成二维码
+const initQrcode = () => {
+  nextTick(() => {
+    (<HTMLElement>qrcodeRef.value).innerHTML = '';
+    new QRCode(qrcodeRef.value, {
+      text: `${props.link}`,
+      width: 120,
+      height: 120,
+      colorDark: '#000000',
+      colorLight: '#ffffff',
+    });
+  });
+};
+// 页面加载时
+onMounted(() => {
+  initQrcode();
+});
+</script>
+
+<style scoped lang="scss">
+.login-qrcode-animation {
+  opacity: 0;
+  animation-name: error-num;
+  animation-duration: 0.5s;
+  animation-fill-mode: forwards;
+}
+
+.login-qrcode-container {
+  margin-top: 20px;
+  padding: 0 20px 20px;
+  display: flex;
+  flex-direction: column;
+  text-align: center;
+  @extend .login-qrcode-animation;
+  animation-delay: 0.1s;
+
+  :deep(img) {
+    margin: auto;
+  }
+
+  .login-msg {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: var(--el-text-color-placeholder);
+    @extend .login-qrcode-animation;
+    animation-delay: 0.2s;
+  }
+}
+</style>

+ 1 - 1
admin-web/src/layout/logo/index.vue

@@ -1,7 +1,7 @@
 <template>
 	<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
 		<img :src="logoMini" class="layout-logo-medium-img" />
-    <span><i color="#ffffff" ><b>{{ themeConfig.globalTitle }}</b></i></span>
+    <span><i  ><b style="color: white;">{{ themeConfig.globalTitle }}</b></i></span>
 	</div>
 <!--	<div class="layout-logo-size" v-else @click="onThemeConfigChange">-->
 <!--		<img :src="logoMini" class="layout-logo-size-img" />-->

+ 0 - 1
admin-web/src/main.ts

@@ -29,7 +29,6 @@ import {useUserInfo} from '/@/stores/userInfo';
 app.config.globalProperties.$bus = new mitt();
 //权限校验
 app.config.globalProperties.$auth = (auth: any, all: boolean = false) => {
-    // console.log("$auth:", auth)
     if (!auth) {
         return true;
     }

+ 78 - 30
admin-web/src/router/route.ts

@@ -115,7 +115,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isAffix: false,
                     isIframe: false,
                     icon: 'ele-MapLocation',
-                    perm:"equipment.list,station.list,stationStatMonth.list,statement.list",
+                    perm: "equipment.list,station.list,stationStatMonth.list,statement.list",
                 },
                 children: [
                     {
@@ -129,7 +129,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             isKeepAlive: true,
                             isAffix: false,
                             isIframe: false,
-                            perm:"station.list",
+                            perm: "station.list",
                             icon: 'ele-OfficeBuilding',
                         },
                     },
@@ -144,7 +144,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             isKeepAlive: true,
                             isAffix: false,
                             isIframe: false,
-                            perm:"equipment.list",
+                            perm: "equipment.list",
                             icon: 'ele-User',
                         },
                     },
@@ -163,7 +163,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isAffix: false,
                     isIframe: false,
                     icon: 'ele-PictureRounded',
-                    perm:"banner.list",
+                    perm: "banner.list",
                 }
             },
             {
@@ -178,7 +178,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isAffix: false,
                     isIframe: false,
                     icon: 'ele-Money',
-                    perm:"order.list",
+                    perm: "order.list",
                 }
             },
             {
@@ -193,7 +193,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isAffix: false,
                     isIframe: false,
                     icon: 'ele-User',
-                    perm:"account.list",
+                    perm: "account.list",
                 }
             },
             {
@@ -208,7 +208,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isAffix: false,
                     isIframe: false,
                     icon: 'ele-Wallet',
-                    perm:"recharge.list",
+                    perm: "recharge.list",
                 },
             },
             {
@@ -223,7 +223,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isAffix: false,
                     isIframe: false,
                     icon: 'ele-Wallet',
-                    perm:"recharge.list",
+                    perm: "recharge.list",
                 },
             },
             {
@@ -237,15 +237,63 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isKeepAlive: true,
                     isAffix: false,
                     isIframe: false,
-                    perm:"equipment.list",
+                    perm: "equipment.list",
                     icon: 'ele-Wallet',
                 },
             },
+            {
+                path: '/platform',
+                name: 'adminPlatform',
+                component: () => import('/@/layout/routerView/parent.vue'),
+                redirect: '/platform/rate',
+                meta: {
+                    title: '平台配置',
+                    isLink: '',
+                    isHide: false,
+                    isKeepAlive: true,
+                    isAffix: false,
+                    isIframe: false,
+                    icon: 'ele-Wallet',
+
+                },
+                children: [
+                    {
+                        path: '/platform/rate',
+                        name: 'adminPlatformRate',
+                        component: () => import('/@/views/admin/platform/rate/index.vue'),
+                        meta: {
+                            title: '平台费率',
+                            isLink: '',
+                            isHide: false,
+                            isKeepAlive: true,
+                            isAffix: false,
+                            isIframe: false,
+                            perm: "platformFeeRate.list",
+                            icon: 'ele-Wallet',
+                        },
+                    },
+                    {
+                        path: '/platform/deviceConfig',
+                        name: 'adminPlatformDeviceConfig',
+                        component: () => import('/@/views/admin/platform/deviceConfig/index.vue'),
+                        meta: {
+                            title: '设备配置',
+                            isLink: '',
+                            isHide: false,
+                            isKeepAlive: true,
+                            isAffix: false,
+                            isIframe: false,
+                            perm: "platformFeeRate.list",
+                            icon: 'ele-Wallet',
+                        },
+                    }
+                ]
+            },
             {
                 path: '/org',
                 name: 'adminOrg',
                 component: () => import('/@/layout/routerView/parent.vue'),
-                redirect: '/admin/org/department',
+                redirect: '/org/user',
                 meta: {
                     title: '系统配置',
                     isLink: '',
@@ -254,7 +302,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                     isAffix: false,
                     isIframe: false,
                     icon: 'ele-Tools',
-                    perm:"user.list,role.list,dict.list",
+                    perm: "user.list,role.list,dict.list",
                 },
                 children: [
                     {
@@ -268,7 +316,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             isKeepAlive: true,
                             isAffix: false,
                             isIframe: false,
-                            perm:"user.list",
+                            perm: "user.list",
                             icon: 'ele-User',
                         },
                     },
@@ -283,7 +331,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             isKeepAlive: true,
                             isAffix: false,
                             isIframe: false,
-                            perm:"role.list",
+                            perm: "role.list",
                             icon: 'ele-Compass',
                         },
                     },
@@ -298,7 +346,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             isKeepAlive: true,
                             isAffix: false,
                             isIframe: false,
-                            perm:"dict.list",
+                            perm: "dict.list",
                             icon: 'ele-Collection',
                         },
                     },
@@ -314,7 +362,7 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             isAffix: false,
                             isIframe: false,
                             icon: 'ele-Tickets',
-                            perm:"",
+                            perm: "",
                         },
                     },
                     {
@@ -329,27 +377,27 @@ export const adminRoutes: Array<RouteRecordRaw> = [
                             isAffix: false,
                             isIframe: false,
                             icon: 'ele-Tickets',
-                            perm:"",
+                            perm: "",
                         },
                     },
                 ]
             },
-   /*         {
-                path: '/optList',
-                name: 'adminOptList',
-                component: () => import('/@/views/admin/log/opt/index.vue'),
-                meta: {
-                    title: '操作日志',
-                    isLink: '',
-                    isHide: false,
-                    isKeepAlive: true,
-                    isAffix: false,
-                    isIframe: false,
+            /*         {
+                         path: '/optList',
+                         name: 'adminOptList',
+                         component: () => import('/@/views/admin/log/opt/index.vue'),
+                         meta: {
+                             title: '操作日志',
+                             isLink: '',
+                             isHide: false,
+                             isKeepAlive: true,
+                             isAffix: false,
+                             isIframe: false,
 
-                    icon: 'ele-Cpu',
+                             icon: 'ele-Cpu',
 
-                }
-            },*/
+                         }
+                     },*/
 
         ],
     },

+ 4 - 3
admin-web/src/utils/authFunction.ts

@@ -7,8 +7,9 @@ import { judementSameArr } from '/@/utils/arrayOperation';
  * @returns 有权限,返回 `true`,反之则反
  */
 export function auth(value: string): boolean {
+	console.log(value)
 	const stores = useUserInfo();
-	return stores.userInfos.authBtnList.some((v: string) => v === value);
+	return stores.userInfos.permissions.some((v: string) => v === value);
 }
 
 /**
@@ -19,7 +20,7 @@ export function auth(value: string): boolean {
 export function auths(value: Array<string>): boolean {
 	let flag = false;
 	const stores = useUserInfo();
-	stores.userInfos.authBtnList.map((val: string) => {
+	stores.userInfos.permissions.map((val: string) => {
 		value.map((v: string) => {
 			if (val === v) flag = true;
 		});
@@ -34,5 +35,5 @@ export function auths(value: Array<string>): boolean {
  */
 export function authAll(value: Array<string>): boolean {
 	const stores = useUserInfo();
-	return judementSameArr(value, stores.userInfos.authBtnList);
+	return judementSameArr(value, stores.userInfos.permissions);
 }

+ 7 - 3
admin-web/src/views/admin/account/index.vue

@@ -145,7 +145,6 @@ import ExtPage from '/@/components/form/ExtPage.vue'
 
 import mittBus from '/@/utils/mitt';
 import ExtDLabel from "/@/components/form/ExtDLabel.vue";
-import ExtDSelect from "/@/components/form/ExtDSelect.vue";
 import ExtSelect from "/@/components/form/ExtSelect.vue";
 
 const AdminUserDialog = defineAsyncComponent(() => import("/@/views/admin/account/detail.vue"));
@@ -242,16 +241,21 @@ const loadData = (refresh: boolean = false) => {
 };
 
 const handleGotoRecharge=(row:any)=>{
+  router.push(`/finance?mobilePhone=${row.mobilePhone}`)
+  return
   let url = router.resolve(`/finance?mobilePhone=${row.mobilePhone}`);
   window.open(url.href, '_blank');
 }
 
 const handleGotoCharge=(row:any)=>{
-  let url = router.resolve(`/ordering?mobilePhone=${row.mobilePhone}`);
-  window.open(url.href, '_blank');
+  router.push(`/ordering?mobilePhone=${row.mobilePhone}`)
+  // let url = router.resolve(`/ordering?mobilePhone=${row.mobilePhone}`);
+  // window.open(url.href, '_blank');
 }
 
 const handleGotoRefund=(row:any)=>{
+  router.push(`/refund?mobilePhone=${row.mobilePhone}`)
+  return;
   let url = router.resolve(`/refund?mobilePhone=${row.mobilePhone}`);
   window.open(url.href, '_blank');
 }

+ 5 - 4
admin-web/src/views/admin/finance/index.vue

@@ -177,14 +177,15 @@ const state = reactive({
 
 //生命周期钩子
 onBeforeMount(() => {
-})
-
-onMounted(() => {
-  var query = route.query;
+  let query = route.query;
   //console.log(route.params, route.query)
   if (query.mobilePhone) {
     state.formQuery.mobilePhone = query.mobilePhone;
   }
+})
+
+onMounted(() => {
+
 
   loadData();
 

+ 12 - 6
admin-web/src/views/admin/finance/splitRecord.vue

@@ -159,13 +159,11 @@ import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurren
 import {$body, $get} from "/@/utils/request";
 import u from '/@/utils/u'
 import {Msg} from "/@/utils/message";
-import {Session} from "/@/utils/storage";
 
-const {proxy}: any = getCurrentInstance();
+import {useRoute} from "vue-router";
+const route = useRoute();
 
 import ExtPage from '/@/components/form/ExtPage.vue'
-import ExtQueryForm from "/@/components/form/ExtQueryForm.vue";
-import ExtTable from "/@/components/form/ExtTable.vue";
 
 import mittBus from '/@/utils/mitt';
 
@@ -180,7 +178,9 @@ const queryRef = ref();
 
 //定义变量
 const state = reactive({
-  formQuery: {},
+  formQuery: {
+    stationId:null
+  },
   pageQuery: {
     pageNum: 1,
     pageSize: 10,
@@ -215,9 +215,15 @@ const state = reactive({
 //         }
 // );
 
+//生命周期钩子
 //生命周期钩子
 onBeforeMount(() => {
 
+  let query = route.query;
+  console.log(route.params, route.query)
+  if (query.stationId) {
+    state.formQuery.stationId = query.stationId;
+  }
 })
 
 onMounted(() => {
@@ -243,7 +249,7 @@ onBeforeUnmount(() => {
 // 初始化表格数据
 const loadData = (refresh: boolean = false) => {
   if (refresh) {
-    state.pageQuery.pageIndex = 1;
+    state.pageQuery.pageNum = 1;
   }
   state.tableData.loading = true;
   $body(`/finance/splitRecords`, {...state.formQuery, ...state.pageQuery}).then((res: any) => {

+ 15 - 65
admin-web/src/views/admin/ordering/index.vue

@@ -172,13 +172,15 @@ export default {
 }
 </script>
 <script setup lang="ts" name="adminOrdering">
-import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurrentInstance, nextTick, onBeforeUnmount} from 'vue';
+import {reactive, onMounted, onBeforeMount, ref, 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";
+import {useRoute} from "vue-router";
+
+const route = useRoute();
 
-const {proxy}: any = getCurrentInstance();
 
 import ExtPage from '/@/components/form/ExtPage.vue'
 import ExtQueryForm from "/@/components/form/ExtQueryForm.vue";
@@ -194,7 +196,9 @@ const washOrderDialogRef = ref();
 
 //定义变量
 const state = reactive({
-  formQuery: {},
+  formQuery: {
+    mobilePhone:null
+  },
   pageQuery: {
     pageNum: 1,
     pageSize: 10,
@@ -209,8 +213,8 @@ const state = reactive({
   exportConfig: {},
   columns: [
     {type: 'expand', width: 60, align: 'center', fixed: 'left'},
-    {label: '消费站点', prop: 'stationId', width: 140,query: true, type: 'select', resizable: true},
-    {label: '归属站点', prop: 'userStationId', width: 140,query: true, type: 'select', resizable: true},
+    {label: '消费站点', prop: 'stationId', width: 140, query: true, type: 'select', resizable: true},
+    {label: '归属站点', prop: 'userStationId', width: 140, query: true, type: 'select', resizable: true},
     {width: 220, label: '订单号', prop: 'orderId', query: true, type: 'text', resizable: true, fixed: 'left'},
     {width: 120, label: '用户手机号', prop: 'mobilePhone', query: true, type: 'text', fixed: 'left'},
     {width: 100, label: '设备编号', prop: 'shortId', query: false, type: 'text', resizable: true},
@@ -219,19 +223,6 @@ const state = reactive({
     {width: 100, label: '消费总额', prop: 'amount', query: false, resizable: true},
     {width: 100, label: '应收金额', prop: 'amountReceivable', query: false, type: '', resizable: true},
     {width: 100, 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: false,
-    //   sortable: 'custom',
-    //   type: 'datetime',
-    //   resizable: true,
-    //   conf: {format: (val: any) => u.fmt.fmtDateTime(val)}
-    // },
-    // {width: 160, label: '卡内码', prop: 'cardId', query: false, type: 'text', resizable: true},
-    // {width: 160, label: '卡串号', prop: 'cardSn', query: false, type: 'text', resizable: true},
-    // {width: 160, label: '卡类型', prop: 'cardType', query: false, type: '', resizable: true},
     {
       width: 120, label: '关机方式',
       prop: 'closeType',
@@ -240,37 +231,9 @@ const state = reactive({
       conf: {dict: 'Order.closeType'},
       resizable: true
     },
-    // {width: 160, label: '投币的累计金额', prop: 'coinMoney', query: false, type: 'text', resizable: true},
-    // {width: 160, label: '投币的次数', prop: 'coinNum', query: false, type: 'text', resizable: true},
-    // {width: 160, label: '开单时间', prop: 'createTime', query: false, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDateTime(val)}},
-    // {
-    //   width:160,label: '费用明细',
-    //   prop: 'detail',
-    //   query: true,
-    //   type: 'text',
-    //   resizable: true
-    // },
-
-    // {width: 160, label: '优惠金额', prop: 'discountAmount', query: false, type: '', resizable: true},
-    // {width: 160, label: '优惠方式', prop: 'discountType', query: false, type: 'dict', conf: {dict: 'Activity.discountType'}, resizable: true},
-    // {
-    //   width: 160, label: '设备空闲关机倒计时剩余时间',
-    //   prop: 'idleRemainTime',
-    //   query: true,
-    //   type: 'text',
-    //   resizable: true
-    // },
-    // {width: 160, label: '会员折扣比例', prop: 'memberDiscount', query: false, type: 'text', 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: 100, label: '订单状态', prop: 'orderStatus', query: false, type: 'dict', conf: {dict: 'Order.status'}, resizable: true},
     {width: 100, label: '支付状态', prop: 'payStatus', query: true, type: 'dict', conf: {dict: 'Order.pay'}, resizable: true},
     {width: 100, label: '发票状态', prop: 'invoiceStatus', query: false, type: 'dict', conf: {dict: 'Invoice.status'}, resizable: true},
-    // {width: 160, label: '本次开机的预付金额', prop: 'prepayMoney', query: true, type: '', resizable: true},
-    // {width: 160, label: '产品key', prop: 'productKey', query: false, type: 'text', resizable: true},
-    // {width: 160, label: '站点ID', prop: 'stationId', query: true, type: 'text', resizable: true},
-    // {label: '停机原因', prop: 'stopReason', query: false, type: 'text', resizable: true},
     {
       width: 160,
       label: '更新时间',
@@ -286,24 +249,6 @@ const state = reactive({
 })
 
 
-// 监听双向绑定 modelValue 的变化
-// watch(
-//         () => state.pageNum,
-//         () => {
-// //:' +
-// 'name 名称 price 单价(单位分) ' +
-// 'seconds 时长(单位秒) ' +
-// 'amount 费用(单位分)' +
-// ' space⻋位或场地,' +
-// 'water清水,' +
-// 'foam泡沫,' +
-// 'leaner吸尘,' +
-// 'tap水龙头,' +
-// 'user_ext用户扩展,消毒或吹干等功能,' +
-// 'coat镀膜,' +
-// 'blow吹气
-//         }
-// );
 
 //生命周期钩子
 onBeforeMount(() => {
@@ -318,10 +263,15 @@ onBeforeMount(() => {
   }
 
   state.exportConfig = {url: exportUrl,}
-
+  let query = route.query;
+  console.log(route.params, route.query)
+  if (query.mobilePhone) {
+    state.formQuery.mobilePhone = query.mobilePhone;
+  }
 })
 
 onMounted(() => {
+
   console.log("ordering mounted")
   loadData();
 

+ 444 - 0
admin-web/src/views/admin/platform/deviceConfig/dialog.vue

@@ -0,0 +1,444 @@
+<style scoped lang="scss">
+
+</style>
+<template>
+  <div class="system-dialog-container">
+    <el-dialog
+        :title="state.dialog.title"
+        v-model="state.dialog.isShowDialog"
+        width="820px"
+        draggable
+        destroy-on-close
+        :close-on-click-modal="false"
+        align-center>
+      <el-form
+          inline
+          :model="state.ruleForm"
+          :rules="state.rules"
+          label-position="top"
+          ref="formRef"
+          size="default"
+          label-width="100px"
+          class="mt5">
+        <div class="sub-group-bottom">基本参数</div>
+        <el-form-item label="配置名称" prop="name" class="w100">
+          <el-input
+              v-model="state.ruleForm.name"
+              placeholder="配置名称"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="配置备注" prop="remark" class="w100">
+          <el-input
+              v-model="state.ruleForm.remark"
+              placeholder="配置备注"
+              clearable
+              type="textarea"
+              :rows="3"
+              maxlength="500"
+              show-word-limit
+              class="w100">
+          </el-input>
+        </el-form-item>
+
+        <div class="sub-group-bottom">选项参数</div>
+        <el-form-item label="灯光工作模式" prop="lightMode" class="w100">
+          <el-input
+              v-model="state.ruleForm.lightMode"
+              placeholder="灯光工作模式 0:全天暂停营业 1:全天营业 2:根据时间段开始营业"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="维护模式" prop="maintenanceMode" class="w100">
+          <el-input
+              v-model="state.ruleForm.maintenanceMode"
+              placeholder="维护模式 0:未设置,1:已设置(屏幕显示设备维护界面)"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="工作模式" prop="workMode" class="w100">
+          <el-input
+              v-model="state.ruleForm.workMode"
+              placeholder="工作模式 0:全天暂停营业 1:全天营业 2:根据时间段开始营业"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+
+        <el-form-item label="屏幕类型" prop="screenType" class="w100">
+          <el-input
+              v-model="state.ruleForm.screenType"
+              placeholder="屏幕类型(0:不支持视频播放的屏幕,1:支持视频播放的屏幕,两种屏型号不同,填写错误会导致没有语音。)"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+
+        <el-form-item label="是否安装了水流量传感器" prop="sensorWater" class="wd300">
+          <ext-boolean
+              v-model="state.ruleForm.sensorWater"
+              placeholder="是否安装了水流量传感器"
+              clearable
+              class="w100"/>
+        </el-form-item>
+
+        <el-form-item label="屏幕左下方文本" prop="userMessage1" class="wd300">
+          <el-input
+              v-model="state.ruleForm.userMessage1"
+              placeholder="屏幕左下方文本"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="屏幕右下方文本" prop="userMessage2" class="wd300">
+          <el-input
+              v-model="state.ruleForm.userMessage2"
+              placeholder="屏幕右下方文本"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <div class="sub-group-bottom">时间参数</div>
+
+        <el-form-item label="自动关闭" prop="motorFlowOff" class="wd300">
+          <ext-boolean
+              v-model="state.ruleForm.motorFlowOff"
+              placeholder="自动关闭,没有流量的时候是否自动关闭水泵,推荐开启"
+              clearable
+              class="w100">
+          </ext-boolean>
+        </el-form-item>
+        <el-form-item label="自动启动" prop="motorFlowOn" class="wd300">
+          <ext-boolean
+              v-model="state.ruleForm.motorFlowOn"
+              placeholder="自动启动,有流量的时候是否自动启动水泵,推荐关闭"
+              clearable
+              class="w100">
+          </ext-boolean>
+        </el-form-item>
+        <el-form-item label="关闭延时,电机每次关闭最少要关闭多少毫秒" prop="motorOffDelay" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.motorOffDelay"
+              placeholder="关闭延时,电机每次关闭最少要关闭多少毫秒"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="启动延时,电机每次启动最少要开多少毫秒" prop="motorOnDelay" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.motorOnDelay"
+              placeholder="启动延时,电机每次启动最少要开多少毫秒"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="关闭灵敏度" prop="motorOnInterval" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.motorOnInterval"
+              placeholder="关闭灵敏度,多久收不到流量信号就关闭电机"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="空闲超时提示音倒计时" prop="noticeThresholdIdle" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.noticeThresholdIdle"
+              placeholder="空闲超时提示音倒计时,60表示当空闲超时倒计时不足60秒的时候发出提示音,提示用户按任意功能键阻止设备关闭"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="操作超时提示音倒计时" prop="noticeThresholdOperation" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.noticeThresholdOperation"
+              placeholder="操作超时提示音倒计时,300表示当剩余操作时间不足5分钟的时候发出提示音,提示用户合理安排剩余时间"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="操作超时关机" prop="operationTimeout" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.operationTimeout"
+              placeholder="操作超时关机,3600表示每次开机后有60分钟的操作时间,60分钟到了会强制结束订单。"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="洗车结束后" prop="billDelay" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.billDelay"
+              placeholder="洗车结束后,费用明细页面显示多久(单位秒)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="空闲超时关" prop="idleTimeout" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.idleTimeout"
+              placeholder="空闲超时关,1200表示20分钟不操作机器自动结束订单,用户操作一次就重新开始倒计时一下"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+
+        <el-form-item label='照明时间段1' prop="lightTimePeriod1" class="w100">
+          <el-input
+              v-model="state.ruleForm.lightTimePeriod1"
+              placeholder='时间段1:"18:00 - 22:00" 下午6点到晚上10点打开照明'
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label='照明时间段2' prop="lightTimePeriod2" class="w100">
+          <el-input
+              v-model="state.ruleForm.lightTimePeriod2"
+              placeholder='时间段2:"18:00 - 08:00" 下午6点到早上8点打开照明'
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label='营业时间段1' prop="workTimePeriod1" class="w100">
+          <el-input
+              v-model="state.ruleForm.workTimePeriod1"
+              placeholder='时间段1:"08:00 - 12:00" 上午8点到中午12点营业'
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label='营业间段2' prop="workTimePeriod2" class="w100">
+          <el-input
+              v-model="state.ruleForm.workTimePeriod2"
+              placeholder='时间段2:"14:00 - 21:00" 下午2点到晚上9点营业'
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="水龙头自动关闭延时" prop="tapOnDelay" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.tapOnDelay"
+              placeholder="水龙头自动关闭延时,30表示打开水龙头后30秒自动关闭水龙头,0表示不允许自动关闭水龙头"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+
+        <el-form-item label="视频播放延时(单位秒)" prop="videoPlayDelay" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.videoPlayDelay"
+              placeholder="视频播放延时(单位秒),设备空闲多久开始循环播放⼴告视频"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="视频源" prop="videoSource" class="w100">
+          <el-input
+              v-model="state.ruleForm.videoSource"
+              placeholder="视频源(0:内置视频,1:TF卡内的视频,2:U盘内的视频)"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="工作指示灯(室内照明)关闭延时" prop="workLightDelay" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.workLightDelay"
+              placeholder="工作指示灯(室内照明)关闭延时,30表示洗车结束关机后延时30秒关闭工作指示灯,0表示立刻关闭指示灯"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <div class="sub-group-bottom">声音参数</div>
+        <el-form-item label="提示音音量(0-100)" prop="soundVolume" class="wd300">
+          <el-input-number
+              :min="0"
+              :max="100"
+              v-model="state.ruleForm.soundVolume"
+              placeholder="提示音音量(0-100)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <div class="sub-group-bottom">价格参数</div>
+
+        <el-form-item label="吹气单价(分/分钟)" prop="priceBlow" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceBlow"
+              placeholder="吹气单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="吸尘单价(分/分钟)" prop="priceCleaner" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceCleaner"
+              placeholder="吸尘单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="镀膜单价(分/分钟)" prop="priceCoat" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceCoat"
+              placeholder="镀膜单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="泡沫单价(分/分钟)" prop="priceFoam" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceFoam"
+              placeholder="泡沫单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="场地费单价(分/分钟)" prop="priceSpace" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceSpace"
+              placeholder="场地费单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="洗手单价(分/分钟)" prop="priceTap" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceTap"
+              placeholder="洗手单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="扩展项目单价(分/分钟)" prop="priceUserExt" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceUserExt"
+              placeholder="扩展项目单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="清水单价(分/分钟)" prop="priceWater" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.priceWater"
+              placeholder="清水单价(分/分钟)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="快速开机金额" prop="quickOpenMoney" class="w100">
+          <el-input-number
+              v-model="state.ruleForm.quickOpenMoney"
+              placeholder="快速开机金额,按机箱内部的维护按键直接开机(设置为0可以关闭这个功能)"
+              clearable
+              class="w100">
+          </el-input-number>
+        </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>
+				</span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="DeviceConfigDialog">
+import {defineAsyncComponent, reactive, onMounted, ref} from 'vue';
+import {Msg} from "/@/utils/message";
+import {$body, $get, $post} from "/@/utils/request";
+import u from '/@/utils/u'
+import ExtBoolean from "/@/components/form/ExtBoolean.vue";
+
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const formRef = ref();
+//定义初始变量,重置使用
+const initState = () => ({
+  ruleForm: {
+    id: 0
+  },
+  btnLoading: false,
+  dialog: {
+    isShowDialog: false,
+    type: '',
+    title: '',
+    submitTxt: '',
+  },
+  rules: {
+    name:[u.validator.required],
+    remark:[u.validator.required]
+  },
+})
+
+// 定义变量内容
+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.isShowDialog = true;
+  if (action !== 'add') {
+    loadData(row.id);
+  } else {
+    state.ruleForm = Object.assign(state.ruleForm, row);
+  }
+};
+// 关闭弹窗
+const onClose = () => {
+  state.dialog.isShowDialog = false;
+  Object.assign(state, initState())
+};
+// 取消
+const onCancel = () => {
+  onClose();
+};
+// 提交
+const onSubmit = () => {
+  formRef.value.validate((v: boolean) => {
+    if (v) {
+      state.btnLoading = true;
+      const url = !!state.ruleForm.id ? "device-config/modify" : "device-config/add"
+      $body(url, state.ruleForm).then(() => {
+        state.btnLoading = false;
+        Msg.message('操作成功');
+        console.log('submit!')
+        onClose();
+        emit('refresh');
+      })
+    } else {
+      state.btnLoading = false;
+      Msg.message('请先完整填写表单', 'error');
+    }
+  })
+};
+
+const handleFormChange = (formData: any) => {
+  console.log(formData)
+}
+
+// 初始化数据
+const loadData = (shortId: string) => {
+  $post(`device-config/${shortId}`).then((res: any) => {
+    state.ruleForm = res;
+  })
+}
+
+// 暴露变量
+defineExpose({
+  open
+});
+
+
+</script>

+ 233 - 0
admin-web/src/views/admin/platform/deviceConfig/index.vue

@@ -0,0 +1,233 @@
+<style scoped lang="scss">
+.system-container {
+
+  :deep(.el-card__body) {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    flex: 1;
+    overflow: auto;
+
+    .el-table {
+      flex: 1;
+    }
+
+  }
+}
+
+.page-content {
+  margin-bottom: 20px;
+}
+
+.page-pager {
+  background-color: #fff;
+  height: 24px;
+}
+</style>
+<template>
+  <div class="system-container layout-padding">
+    <el-card shadow="hover" class="layout-padding-auto">
+      <ext-query-form
+          class="page-search"
+          ref="queryRef"
+          v-model="state.formQuery"
+          :columns="state.columns"
+          :import-config="state.importConfig"
+          :export-config="state.exportConfig"
+          @on-change="loadData(true)"
+          @imported="loadData(true)">
+        <!--  <template #extraQuery></template>
+          <template #extraLeft></template>
+          <template #extraRight></template>-->
+        <template #extQuery>
+          <el-button v-auth="'deviceConfig.add'" size="default" plain type="success" class="ml10" @click="handleRowClick('add',null)">
+            <SvgIcon name="ele-FolderAdd"/>
+            新增
+          </el-button>
+        </template>
+      </ext-query-form>
+
+      <el-table
+          border
+          stripe="stripe"
+          :height="state.tableData.height"
+          highlight-current-row
+          current-row-key="id"
+          row-key="id"
+          @on-row-click="handleRowClick('view',$event)"
+          :data="state.tableData.data"
+          v-loading="state.tableData.loading">
+        <template #empty>
+          <el-empty></el-empty>
+        </template>
+        <el-table-column
+            v-for="field in state.columns"
+            :key="field.prop"
+            :type="field.type"
+            :label="field.label"
+            :column-key="field.prop"
+            :width="field.width"
+            :min-width="field.minWidth"
+            :fixed="field.fixed"
+            :sortable="field.sortable"
+            :show-overflow-tooltip="!field.fixed&&field.width>150">
+
+          <template #default="{row}">
+            <template v-if="field.prop==='name'">
+              <div class="cursor-pointer" style="color:var(--el-color-primary-light-1)"
+                   @click="handleRowClick('view', row)">
+                {{ row[field.prop] }}
+              </div>
+            </template>
+            <template v-else-if="field.prop==='type'">
+              <ext-d-label type="Object.type" :model-value="row[field.prop]"></ext-d-label>
+            </template>
+            <template v-else-if="['motorFlowOff','motorFlowOn'].includes(field.prop)">
+              <ext-boolean disabled :model-value="row[field.prop]"></ext-boolean>
+            </template>
+            <template v-else-if="field.prop==='idleRemainTime'||field.prop==='operationRemainTime'">
+              {{ u.fmt.fmtDuration(row[field.prop]) }}
+            </template>
+            <template v-else-if="field.prop==='idleRemainTime'||field.prop==='operationRemainTime'">
+              {{ u.fmt.fmtDuration(row[field.prop]) }}
+            </template>
+            <template v-else-if="['createTime','updateTime'].includes(field.prop)">
+              {{ row[field.prop].join("") }}
+            </template>
+            <template v-else-if="field.prop==='action'">
+              <el-button v-auth="'deviceConfig.modify'" type="warning" size="small" text @click="handleRowClick('edit',row)"> 编辑</el-button>
+              <el-button v-auth="'deviceConfig.remove'" type="danger" size="small" text @click="handleRowDelete(row)"> 删除</el-button>
+            </template>
+            <template v-else>
+              <div>{{ row[field.prop] }}</div>
+            </template>
+
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <ext-page class="page-pager" v-model:value="state.pageQuery" @change="loadData(false)"/>
+    </el-card>
+  </div>
+  <DeviceConfigDialog ref="deviceConfigDialogRef" @refresh="loadData(true)"/>
+</template>
+
+<script setup lang="ts" name="adminPlatformDeviceConfig">
+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 ExtPage from '/@/components/form/ExtPage.vue'
+import ExtQueryForm from "/@/components/form/ExtQueryForm.vue";
+
+import {ElButton} from 'element-plus'
+import ExtBoolean from "/@/components/form/ExtBoolean.vue";
+
+
+const DeviceConfigDialog = defineAsyncComponent(() => import("/@/views/admin/platform/deviceConfig/dialog.vue"));
+
+//定义引用
+const queryRef = ref();
+const deviceConfigDialogRef = ref();
+
+//定义变量
+const state = reactive({
+  formQuery: {},
+  pageQuery: {
+    pageIndex: 1,
+    pageSize: 10,
+    total: 0
+  },
+  tableData: {
+    height: 500,
+    data: [] as Array<any>,
+    loading: false
+  },
+  importConfig: {},
+  exportConfig: {},
+  columns: [
+    {label: 'ID', width: 180, prop: 'id', query: false, type: '', resizable: true},
+    {label: '配置名称', width: 180, prop: 'name', query: true, type: '', resizable: true},
+    {label: '配置备注', width: 180, prop: 'remark', query: true, type: '', resizable: true},
+    {label: '自动关闭', width: 180, prop: 'motorFlowOff', query: false, type: '', resizable: true},
+    {label: '自动启动', width: 180, prop: 'motorFlowOn', query: false, type: '', resizable: true},
+    {label: '创建时间', width: 180, prop: 'createTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
+    {label: '更新时间', width: 180, 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',
+    }
+  ],
+})
+
+
+// 监听双向绑定 modelValue 的变化
+// watch(
+//         () => state.pageIndex,
+//         () => {
+//
+//         }
+// );
+
+//生命周期钩子
+onBeforeMount(() => {
+
+})
+
+onMounted(() => {
+  loadData();
+
+  nextTick(() => {
+    let bodyHeight = document.body.clientHeight;
+    let queryHeight = queryRef.value.$el.clientHeight;
+    state.tableData.height = bodyHeight - queryHeight - 220
+  })
+
+});
+
+onBeforeUnmount(() => {
+})
+
+
+//region 方法区
+// 初始化表格数据
+const loadData = (refresh: boolean = false) => {
+  if (refresh) {
+    state.pageQuery.pageIndex = 1;
+  }
+  state.tableData.loading = true;
+  $body(`/device-config/list`, {...state.formQuery, ...state.pageQuery}).then((res: any) => {
+    let {list, count} = res;
+    state.tableData.data = list;
+    state.pageQuery.total = count;
+    state.tableData.loading = false;
+  }).catch(e => {
+    console.error(e)
+    state.tableData.loading = false;
+  })
+};
+
+// 打开详情页弹窗
+const handleRowClick = (type: string, row: any) => {
+  deviceConfigDialogRef.value.open(type, row);
+};
+// 删除点击
+const handleRowDelete = (row: any) => {
+  Msg.confirm(`此操作将永久删除:『${row.name}』,是否继续?`).then(() => {
+    $get(`/deviceConfig/delete/${row.id}`).then(() => {
+      Msg.message("删除成功", 'success')
+    }).catch(() => {
+      Msg.message("删除失败", 'error')
+    })
+  });
+};
+
+//endregion
+
+
+// 暴露变量
+// defineExpose({
+//     loadData,
+// });
+</script>

+ 159 - 0
admin-web/src/views/admin/platform/rate/dialog.vue

@@ -0,0 +1,159 @@
+<style scoped lang="scss">
+
+</style>
+<template>
+  <div class="system-dialog-container">
+    <el-dialog
+        :title="state.dialog.title"
+        v-model="state.dialog.isShowDialog"
+        width="750px"
+        draggable
+        destroy-on-close
+        :close-on-click-modal="false"
+        align-center>
+      <el-form
+          inline
+          :model="state.ruleForm"
+          :rules="state.rules"
+          label-position="top"
+          ref="formRef"
+          size="default"
+          label-width="100px"
+          class="mt5">
+        <el-form-item label="费率名称" prop="name" class="wd300">
+          <el-input
+              v-model="state.ruleForm.name"
+              placeholder="费率名称"
+              clearable
+              class="w100">
+          </el-input>
+        </el-form-item>
+        <el-form-item label="平台费率(0.1代表10%)" prop="feeRate" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.feeRate"
+              placeholder="平台费率(0.1代表10%)"
+              clearable
+              step="0.1"
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+        <el-form-item label="充值冻结金额比例(0.3代表30%)" prop="frozenRatio" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.frozenRatio"
+              placeholder="充值冻结金额比例(0.3代表30%)"
+              clearable
+              step="0.1"
+              class="w100">
+          </el-input-number>
+        </el-form-item>
+
+        <el-form-item label="提现手续费率(0.006代表6‰)" prop="withdrawalFeeRate" class="wd300">
+          <el-input-number
+              v-model="state.ruleForm.withdrawalFeeRate"
+              placeholder="提现手续费率(0.006代表6‰)"
+              clearable
+              step="0.001"
+              class="w100">
+          </el-input-number>
+        </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>
+				</span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="PlatformFeeRateDialog">
+import {defineAsyncComponent, reactive, onMounted, ref} from 'vue';
+import {Msg} from "/@/utils/message";
+import {$body, $get} from "/@/utils/request";
+import u from '/@/utils/u'
+
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const formRef = ref();
+//定义初始变量,重置使用
+const initState = () => ({
+  ruleForm: {
+    id: 0
+  },
+  btnLoading: false,
+  dialog: {
+    isShowDialog: false,
+    type: '',
+    title: '',
+    submitTxt: '',
+  },
+  rules: {
+    name:[u.validator.required]
+  },
+})
+
+// 定义变量内容
+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.isShowDialog = true;
+  if (action !== 'add') {
+    state.ruleForm = Object.assign(state.ruleForm, row);
+  } else {
+    state.ruleForm = Object.assign(state.ruleForm, row);
+  }
+};
+// 关闭弹窗
+const onClose = () => {
+  state.dialog.isShowDialog = false;
+  Object.assign(state, initState())
+};
+// 取消
+const onCancel = () => {
+  onClose();
+};
+// 提交
+const onSubmit = () => {
+  formRef.value.validate((v: boolean) => {
+    if (v) {
+      state.btnLoading = true;
+      const url = state.ruleForm.id > 0 ? "platform-fee-rate/modify" : "platform-fee-rate/add"
+      $body(url, state.ruleForm).then(() => {
+        state.btnLoading = false;
+        Msg.message('操作成功');
+        console.log('submit!')
+        onClose();
+        emit('refresh');
+      })
+    } else {
+      state.btnLoading = false;
+      Msg.message('请先完整填写表单', 'error');
+    }
+  })
+};
+
+const handleFormChange = (formData: any) => {
+  console.log(formData)
+}
+
+// 初始化数据
+const loadData = (id: number) => {
+  $get(`platform-fee-rate/detail/${id}`).then((res: any) => {
+    state.ruleForm = res;
+  })
+}
+
+// 暴露变量
+defineExpose({
+  open
+});
+
+
+</script>

+ 255 - 0
admin-web/src/views/admin/platform/rate/index.vue

@@ -0,0 +1,255 @@
+<style scoped lang="scss">
+.system-container {
+
+  :deep(.el-card__body) {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    flex: 1;
+    overflow: auto;
+
+    .el-table {
+      flex: 1;
+    }
+
+  }
+}
+
+.page-content {
+  margin-bottom: 20px;
+}
+
+.page-pager {
+  background-color: #fff;
+  height: 24px;
+}
+</style>
+<template>
+  <div class="system-container layout-padding">
+    <el-card shadow="hover" class="layout-padding-auto">
+      <ext-query-form
+          class="page-search"
+          ref="queryRef"
+          v-model="state.formQuery"
+          :columns="state.columns"
+          :import-config="state.importConfig"
+          :export-config="state.exportConfig"
+          @on-change="loadData(true)"
+          @imported="loadData(true)">
+        <template #extQuery>
+          <el-button  v-auth="'platformFeeRate.add'"   size="default" plain  type="success" class="ml10" @click="handleRowClick('add',null)">
+            <SvgIcon name="ele-FolderAdd"/>
+            新增
+          </el-button></template>
+        <!--  <template #extraQuery></template>
+          <template #extraLeft></template>
+          <template #extraRight></template>-->
+      </ext-query-form>
+
+      <el-table
+          border
+          stripe="stripe"
+          :height="state.tableData.height"
+          highlight-current-row
+          current-row-key="id"
+          row-key="id"
+          @on-row-click="handleRowClick('view',$event)"
+          :data="state.tableData.data"
+          v-loading="state.tableData.loading">
+        <template #empty>
+          <el-empty></el-empty>
+        </template>
+
+        <el-table-column
+            v-for="field in state.columns"
+            :key="field.prop"
+            :type="field.type"
+            :label="field.label"
+            :column-key="field.prop"
+            :width="field.width"
+            :min-width="field.minWidth"
+            :fixed="field.fixed"
+            :sortable="field.sortable"
+            :show-overflow-tooltip="!field.fixed&&field.width>150">
+
+
+          <template #default="{row}">
+            <template v-if="field.prop==='name'">
+              <div class="cursor-pointer" style="color:var(--el-color-primary-light-1)"
+                   @click="handleRowClick('view', row)">
+                {{ row[field.prop] }}
+              </div>
+            </template>
+            <template v-else-if="field.prop==='type'">
+              <ext-d-label type="Object.type" :model-value="row[field.prop]"></ext-d-label>
+            </template>
+            <template v-else-if="['prepayMoney','amount','amountReceivable','amountReceived','cardBalance','coinMoney','discountAmount','discountMoney'].includes(field.prop)">
+              {{ u.fmt.fmtMoney(row[field.prop]) }}
+            </template>
+            <template v-else-if="field.prop==='idleRemainTime'||field.prop==='operationRemainTime'">
+              {{ u.fmt.fmtDuration(row[field.prop]) }}
+            </template>
+            <template v-else-if="['createTime','updateTime'].includes(field.prop)">
+              {{ u.fmt.fmtDateTime(row[field.prop]) }}
+            </template>
+            <template v-else-if="field.prop==='feeRate'">
+              {{ row[field.prop] }}
+            </template>
+
+            <template v-else-if="field.prop==='action'">
+              <el-button v-auth="'platformFeeRate.modify'" type="warning" size="small" text @click="handleRowClick('edit',row)"> 编辑</el-button>
+<!--              <el-button v-auth="'platformFeeRate.remove'" type="danger" size="small" text @click="handleRowDelete(row)"> 删除</el-button>-->
+            </template>
+            <template v-else>
+              <div>{{ row[field.prop] }}</div>
+            </template>
+
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <ext-page class="page-pager" v-model:value="state.pageQuery" @change="loadData(false)"/>
+    </el-card>
+  </div>
+  <PlatformFeeRateDialog ref="platformFeeRateDialogRef" @refresh="loadData(true)"/>
+</template>
+
+<script setup lang="ts" name="AdminPlatformRate">
+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";
+
+
+import ExtPage from '/@/components/form/ExtPage.vue'
+import ExtQueryForm from "/@/components/form/ExtQueryForm.vue";
+import ExtTable from "/@/components/form/ExtTable.vue";
+
+import mittBus from '/@/utils/mitt';
+
+import {ElButton} from 'element-plus'
+import ExtTip from "/@/components/form/ExtTip.vue";
+
+
+const PlatformFeeRateDialog = defineAsyncComponent(() => import("/@/views/admin/platform/rate/dialog.vue"));
+
+//定义引用
+const queryRef = ref();
+const platformFeeRateDialogRef = ref();
+
+//定义变量
+const state = reactive({
+  formQuery: {},
+  pageQuery: {
+    pageIndex: 1,
+    pageSize: 10,
+    total: 0
+  },
+  tableData: {
+    height: 500,
+    data: [] as Array<any>,
+    loading: false
+  },
+  importConfig: {},
+  exportConfig: {},
+  columns: [
+    {
+      label: '费率名称', width: 180, prop: 'name', 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)'
+            },
+            onClick: () => {
+              handleRowClick('view', row)
+            }
+          }, row.name || row.title)])
+      }
+    },
+    {label: '平台费率(0.1代表10%)', width: 180, prop: 'feeRate', query: true, type: '', resizable: true},
+    {label: '充值冻结金额比例(0.3代表30%)', width: 180, prop: 'frozenRatio', query: true, type: '', resizable: true},
+
+    {label: '提现手续费率(0.006代表6‰)', width: 180, prop: 'withdrawalFeeRate', query: true, type: '', resizable: true},
+
+    {label: '创建时间', width: 180, prop: 'createTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
+
+    {label: '更新时间', width: 180, 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',
+    }
+  ],
+})
+
+
+// 监听双向绑定 modelValue 的变化
+// watch(
+//         () => state.pageIndex,
+//         () => {
+//
+//         }
+// );
+
+//生命周期钩子
+onBeforeMount(() => {
+
+})
+
+onMounted(() => {
+  loadData();
+
+  nextTick(() => {
+    let bodyHeight = document.body.clientHeight;
+    let queryHeight = queryRef.value.$el.clientHeight;
+    state.tableData.height = bodyHeight - queryHeight - 220
+  })
+
+});
+
+onBeforeUnmount(() => {
+})
+
+
+//region 方法区
+// 初始化表格数据
+const loadData = (refresh: boolean = false) => {
+  if (refresh) {
+    state.pageQuery.pageIndex = 1;
+  }
+  state.tableData.loading = true;
+  $body(`/platform-fee-rate/list`, {...state.formQuery, ...state.pageQuery}).then((res: any) => {
+    let {list, count} = res;
+    state.tableData.data = list;
+    state.pageQuery.total = count;
+    state.tableData.loading = false;
+  }).catch(e => {
+    console.error(e)
+    state.tableData.loading = false;
+  })
+};
+
+// 打开详情页弹窗
+const handleRowClick = (type: string, row: any) => {
+  platformFeeRateDialogRef.value.open(type, row);
+};
+// 删除点击
+const handleRowDelete = (row: any) => {
+  Msg.confirm(`此操作将永久删除:『${row.name}』,是否继续?`).then(() => {
+    $get(`/platform-fee-rate/delete/${row.id}`).then(() => {
+      Msg.message("删除成功", 'success')
+    }).catch(() => {
+      Msg.message("删除失败", 'error')
+    })
+  });
+};
+
+//endregion
+
+
+// 暴露变量
+// defineExpose({
+//     loadData,
+// });
+</script>

+ 29 - 44
admin-web/src/views/admin/station/account/index.vue

@@ -84,6 +84,13 @@
                 </el-table>
               </div>
             </template>
+            <template v-else-if="field.prop==='stationName'">
+              <div class="text-align-center cursor-pointer" @click="handleGotoSplitPage(row)">
+                <el-button link type="primary" >{{ row.stationId }}</el-button>
+                <hr>
+                <el-button link type="primary" >{{ row.stationName }}</el-button>
+              </div>
+            </template>
             <template v-else-if="field.prop==='type'">
               <ext-d-label type="Object.type" :model-value="row[field.prop]"></ext-d-label>
             </template>
@@ -110,15 +117,16 @@
 </template>
 
 <script setup lang="ts" name="adminStationAccount">
-import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurrentInstance,nextTick,onBeforeUnmount} from 'vue';
-import {$body,$get} from "/@/utils/request";
+import {reactive, onMounted, onBeforeMount, ref, nextTick, onBeforeUnmount} from 'vue';
+import {$body, $get} from "/@/utils/request";
 import u from '/@/utils/u'
 import {Msg} from "/@/utils/message";
 
-const {proxy}: any = getCurrentInstance();
 
 import ExtPage from '/@/components/form/ExtPage.vue'
-import ExtQueryForm from "/@/components/form/ExtQueryForm.vue";
+import {useRouter} from "vue-router";
+
+const router = useRouter();
 
 import mittBus from '/@/utils/mitt';
 
@@ -126,7 +134,6 @@ import {ElButton} from 'element-plus'
 import ExtSelect from "/@/components/form/ExtSelect.vue";
 
 
-
 //定义引用
 const queryRef = ref();
 
@@ -139,47 +146,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: '站点ID', width: 100,prop: 'stationId', query: true, type: 'text', resizable: true},
-    {label: '站点名称', width: 200,prop: 'stationName', query: true, type: 'text', resizable: true},
-    {label: '可提现金额(元)', width: 180,prop: 'balance', query: true, type: '', resizable: true},
-    {label: '充值未消费金额(元)', width: 180,prop: 'frozenAmount', query: true, type: '', resizable: true},
-    {label: '未消费可分账金额(元)', width: 200,prop: 'frozenAmountSplit', query: true, type: '', resizable: true},
-    {label: '创建时间', width: 180,prop: 'createTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
-    {label: '更新时间', width: 180,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) => {
-    //     return (
-    //         h('div', null, [
-    //           proxy.$auth('stationAccount.modify') ?
-    //               h(ElButton, {
-    //                 type: 'warning',
-    //                 text: true,
-    //                 size: 'small',
-    //                 onClick: () => {
-    //                   handleRowClick('edit', row)
-    //                 }
-    //               }, () => '编辑') : '',
-    //           proxy.$auth('stationAccount.remove') ?
-    //               h(ElButton, {
-    //                 type: 'danger',
-    //                 text: true,
-    //                 size: 'small',
-    //                 onClick: () => {
-    //                   handleRowDelete(row)
-    //                 }
-    //               }, () => '删除') : '',
-    //         ])
-    //     )
-    //   }
-    // }
+    // {label: '站点ID', width: 100,prop: 'stationId', query: true, type: 'text', resizable: true},
+    {label: '站点名称', width: 200, prop: 'stationName', query: true, type: 'text', resizable: true},
+    {label: '可提现金额(元)', width: 180, prop: 'balance', query: true, type: '', resizable: true},
+    {label: '充值未消费金额(元)', width: 180, prop: 'frozenAmount', query: true, type: '', resizable: true},
+    {label: '未消费可分账金额(元)', width: 200, prop: 'frozenAmountSplit', query: true, type: '', resizable: true},
+    {label: '创建时间', width: 180, prop: 'createTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
+    {label: '更新时间', width: 180, prop: 'updateTime', query: true, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
   ],
 })
 
@@ -200,27 +181,31 @@ onBeforeMount(() => {
 onMounted(() => {
   loadData();
 
-  nextTick(()=>{
+  nextTick(() => {
     let bodyHeight = document.body.clientHeight;
     let queryHeight = queryRef.value.$el.clientHeight;
     state.tableData.height = bodyHeight - queryHeight - 320
   })
 
-  mittBus.on("stationAccount.refresh",()=>{
+  mittBus.on("stationAccount.refresh", () => {
     loadData();
   })
 });
 
-onBeforeUnmount(()=>{
+onBeforeUnmount(() => {
   mittBus.off("stationAccount.refresh")
 })
 
+const handleGotoSplitPage = (row: any) => {
+  router.push(`/financeSR?stationId=${row.stationId}`)
+}
+
 
 //region 方法区
 // 初始化表格数据
 const loadData = (refresh: boolean = false) => {
   if (refresh) {
-    state.pageQuery.pageIndex = 1;
+    state.pageQuery.pageNum = 1;
   }
   state.tableData.loading = true;
   $body(`/finance/stationAccounts`, {...state.formQuery, ...state.pageQuery}).then((res: any) => {

+ 111 - 0
admin-web/src/views/admin/station/device/config.vue

@@ -0,0 +1,111 @@
+<style scoped lang="scss">
+
+</style>
+<template>
+  <div class="system-dialog-container">
+    <el-dialog
+        title="批量绑定设备配置"
+        v-model="state.dialog.isShowDialog"
+        width="420px"
+        draggable
+        destroy-on-close
+        :close-on-click-modal="false"
+        align-center>
+      <el-form
+          inline
+          :model="state.ruleForm"
+          :rules="rules"
+          label-position="top"
+          ref="formRef"
+          size="default"
+          label-width="100px"
+          class="mt5">
+        <ext-select
+            v-model="state.ruleForm.deviceConfigId"
+            placeholder="请选择要绑定的配置"
+            url="device-config/list"
+            url-method="post"
+            label-key="name"
+            value-key="id"
+            data-key="list"
+            clearable
+            class="wd300 ml10"/>
+      </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>
+				</span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="DeviceConfigDialog">
+import {defineAsyncComponent, reactive, onMounted, ref} from 'vue';
+import {Msg} from "/@/utils/message";
+import {$body, $get} from "/@/utils/request";
+import u from '/@/utils/u'
+import ExtSelect from "/@/components/form/ExtSelect.vue";
+
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+const formRef = ref();
+//定义初始变量,重置使用
+const initState = () => ({
+  ruleForm: {
+    id: 0,
+    deviceIds:[]
+  },
+  btnLoading: false,
+  dialog: {
+    isShowDialog: false,
+    type: '',
+    title: '',
+    submitTxt: '',
+  },
+  rules: {},
+})
+
+// 定义变量内容
+const state = reactive(initState());
+
+
+// 打开弹窗
+const open = ( deviceIds: any) => {
+  state.ruleForm.deviceIds = deviceIds
+  state.dialog.submitTxt ="绑定"
+  state.dialog.isShowDialog = true;
+};
+// 关闭弹窗
+const onClose = () => {
+  state.dialog.isShowDialog = false;
+  Object.assign(state, initState())
+};
+// 取消
+const onCancel = () => {
+  onClose();
+};
+// 提交
+const onSubmit = () => {
+  state.btnLoading = true;
+  $body(`device-config/batchModify`, state.ruleForm).then(() => {
+    state.btnLoading = false;
+    Msg.message('绑定成功');
+    onClose();
+    emit('refresh');
+  }).catch(e => {
+    state.btnLoading = false;
+  })
+};
+
+
+// 暴露变量
+defineExpose({
+  open
+});
+
+
+</script>

+ 29 - 0
admin-web/src/views/admin/station/device/index.vue

@@ -39,6 +39,14 @@
             <SvgIcon name="ele-FolderAdd"/>
             新增
           </el-button>
+
+          <el-button v-auth="'washDevice.modify'" size="default"
+                     plain type="success" class="ml10"
+
+                     @click="handleBatchDeviceConfig">
+            <SvgIcon name="ele-Setting"/>
+            配置
+          </el-button>
         </template>
         <!--  <template #extraQuery></template>
           <template #extraLeft></template>
@@ -46,6 +54,8 @@
       </ext-query-form>
 
       <ext-table
+          selectable
+          @on-check-change="handleChooseDeviceChange"
           class="page-content"
           :height="state.tableData.height"
           :data-list="state.tableData.data"
@@ -58,6 +68,7 @@
     </el-card>
   </div>
   <DeviceDialog ref="device_dialog_ref" @refresh="loadData(true)"/>
+  <DeviceConfigDialog ref="device_config_dialog_ref" @refresh="loadData(true)"/>
 </template>
 
 <script setup lang="ts" name="adminStationDevice">
@@ -78,14 +89,17 @@ import mittBus from '/@/utils/mitt';
 import {ElButton} from 'element-plus'
 
 import DeviceDialog from '/@/views/admin/station/device/dialog.vue'
+import DeviceConfigDialog from '/@/views/admin/station/device/config.vue'
 
 
 //定义引用
 const queryRef = ref();
 const device_dialog_ref = ref();
+const device_config_dialog_ref = ref();
 
 //定义变量
 const state = reactive({
+  chooseDeviceList: [],
   formQuery: {},
   pageQuery: {
     pageNum: 1,
@@ -246,6 +260,11 @@ const loadData = (refresh: boolean = false) => {
   })
 };
 
+// 配置详情页
+const handleRowConfigClick = (type: string, row: any) => {
+  device_config_dialog_ref.value.open(type, row);
+}
+
 // 打开详情页弹窗
 const handleRowClick = (type: string, row: any) => {
   device_dialog_ref.value.open(type, row);
@@ -261,6 +280,16 @@ const handleRowDelete = (row: any) => {
   });
 };
 
+const handleBatchDeviceConfig = () => {
+  device_config_dialog_ref.value.open(state.chooseDeviceList);
+}
+
+const handleChooseDeviceChange = (deviceList) => {
+  console.log(deviceList)
+  state.chooseDeviceList = deviceList.map(k=>k.id);
+
+}
+
 //endregion
 
 

+ 306 - 172
admin-web/src/views/admin/station/list/dialog.vue

@@ -6,163 +6,232 @@
     <el-dialog
         :title="state.dialog.title"
         v-model="state.dialog.isShowDialog"
-        width="620px"
+        width="720px"
         draggable
         destroy-on-close
         :close-on-click-modal="false"
         align-center>
-      <el-form
-          inline
-          :model="state.ruleForm"
-          :rules="rules"
-          label-position="top"
-          ref="formRef"
-          size="default"
-          label-width="100px"
-          class="mt5">
-        <div class="sub-group-bottom">基本信息</div>
-        <el-form-item label="站点ID:" prop="stationId" class="wd350">
-          <el-input
-              v-model="state.ruleForm.stationId"
-              placeholder="站点id"
-              clearable
-              class="wd100">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="工位数量:" prop="parkingNum">
-          <el-input
-              v-model="state.ruleForm.parkingNum"
-              placeholder="工位数量"
-              clearable
-              class="wd100">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="站点名称:" prop="stationName" >
-          <el-input
-              v-model="state.ruleForm.stationName"
-              placeholder="站点名称"
-              clearable
-              class="wd200">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="站点照片:" prop="pictures">
-          <ext-upload v-model="state.ruleForm.pictures" multiple max="6"></ext-upload>
-<!--          <el-input
-              v-model="state.ruleForm.pictures"
-              placeholder="站点照片"
-              clearable
-              class="wd350">
-          </el-input>-->
-        </el-form-item>
-
-        <el-form-item class="w100"></el-form-item>
-        <el-form-item label="站点状态:" prop="stationStatus">
-          <ext-d-select type="WashStation.status" class="wd200" v-model="state.ruleForm.stationStatus"></ext-d-select>
-        </el-form-item>
-
-        <el-form-item label="站点类型:" prop="stationType">
-          <ext-d-select type="WashStation.type" class="wd200" v-model="state.ruleForm.stationType"></ext-d-select>
-
-        </el-form-item>
-
-        <el-form-item label="地址:" prop="address" class="w100">
-          <el-input
-              v-model="state.ruleForm.address"
-              placeholder="地址"
-              clearable
-
-              class="w100">
-          </el-input>
-        </el-form-item>
-
-        <el-form-item label="经度坐标:" prop="location">
-          <el-input
-              v-model="state.location.stationLng"
-              placeholder="经度坐标"
-              clearable
-              type="number"
-              class="wd200">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="纬度坐标:" prop="location">
-          <el-input
-              v-model="state.location.stationLat"
-              placeholder="纬度坐标"
-              clearable
-              type="number"
-              class="wd200">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="服务电话:" prop="serviceTel">
-          <el-input
-              v-model="state.ruleForm.serviceTel"
-              placeholder="服务电话"
-              clearable
-              class="wd350">
-          </el-input>
-        </el-form-item>
-        <el-form-item label="站点电话:" prop="stationTel">
-          <el-input
-              v-model="state.ruleForm.stationTel"
-              placeholder="站点电话"
-              clearable
-              class="wd350">
-          </el-input>
-        </el-form-item>
-
-
-        <el-form-item label="营业时间描述:" prop="businessHours"  class="w100">
-          <el-input
-              v-model="state.ruleForm.businessHours"
-              placeholder="营业时间描述"
-              clearable
-              type="textarea"
-              class="w100">
-          </el-input>
-        </el-form-item>
-
-        <el-form-item label="停车费描述:" prop="parkingFee" class="w100">
-          <el-input
-              v-model="state.ruleForm.parkingFee"
-              type="textarea"
-              placeholder="停车费描述:eg:洗车费用满10元减免1小时停车费"
-              clearable
-              class="w100">
-          </el-input>
-        </el-form-item>
-
-        <el-form-item label="停车费减免二维码文本:" prop="parkingQrCode" class="w100">
-          <el-input
-              v-model="state.ruleForm.parkingQrCode"
-              type="textarea"
-              placeholder="停车费减免二维码文本"
-              clearable
-              class="w100">
-          </el-input>
-        </el-form-item>
-
-
-        <el-form-item label="备注:" prop="remark" class="w100">
-          <el-input
-              v-model="state.ruleForm.remark"
-              placeholder="备注"
-              clearable
-              type="textarea"
-              class="w100">
-          </el-input>
-        </el-form-item>
-
-<!--        <el-form-item label="站点引导" prop="siteGuide">
-          <el-input
-              v-model="state.ruleForm.siteGuide"
-              placeholder="站点引导"
-              clearable
-              class="wd350">
-          </el-input>
-        </el-form-item>-->
-
-
-      </el-form>
+
+      <el-tabs v-model="state.tab" class="demo-tabs" style="width: 100%;" @tab-change="handleTabChange">
+        <el-tab-pane label="基本信息" name="basic">
+          <el-form
+              inline
+              :model="state.ruleForm"
+              :rules="state.rules"
+              label-position="top"
+              ref="formRef"
+              size="default"
+              label-width="100px"
+              class="mt5">
+            <el-form-item label="站点ID:" prop="stationId" class="wd350">
+              <el-input
+                  v-model="state.ruleForm.stationId"
+                  placeholder="站点id"
+                  clearable
+                  class="wd100">
+              </el-input>
+            </el-form-item>
+            <el-form-item label="工位数量:" prop="parkingNum">
+              <el-input
+                  v-model="state.ruleForm.parkingNum"
+                  placeholder="工位数量"
+                  clearable
+                  class="wd100">
+              </el-input>
+            </el-form-item>
+            <el-form-item label="站点名称:" prop="stationName">
+              <el-input
+                  v-model="state.ruleForm.stationName"
+                  placeholder="站点名称"
+                  clearable
+                  class="wd200">
+              </el-input>
+            </el-form-item>
+            <el-form-item label="站点照片:" prop="pictures">
+              <ext-upload v-model="state.ruleForm.pictures" multiple max="6"></ext-upload>
+              <!--          <el-input
+                            v-model="state.ruleForm.pictures"
+                            placeholder="站点照片"
+                            clearable
+                            class="wd350">
+                        </el-input>-->
+            </el-form-item>
+
+            <el-form-item class="w100"></el-form-item>
+            <el-form-item label="站点状态:" prop="stationStatus">
+              <ext-d-select type="WashStation.status" class="wd200" v-model="state.ruleForm.stationStatus"></ext-d-select>
+            </el-form-item>
+
+            <el-form-item label="站点类型:" prop="stationType">
+              <ext-d-select type="WashStation.type" class="wd200" v-model="state.ruleForm.stationType"></ext-d-select>
+
+            </el-form-item>
+
+            <el-form-item label="地址:" prop="address" class="w100">
+              <el-input
+                  v-model="state.ruleForm.address"
+                  placeholder="地址"
+                  clearable
+
+                  class="w100">
+              </el-input>
+            </el-form-item>
+
+            <el-form-item label="经度坐标:" prop="location">
+              <el-input
+                  v-model="state.location.stationLng"
+                  placeholder="经度坐标"
+                  clearable
+                  type="number"
+                  class="wd200">
+              </el-input>
+            </el-form-item>
+            <el-form-item label="纬度坐标:" prop="location">
+              <el-input
+                  v-model="state.location.stationLat"
+                  placeholder="纬度坐标"
+                  clearable
+                  type="number"
+                  class="wd200">
+              </el-input>
+            </el-form-item>
+            <el-form-item label="服务电话:" prop="serviceTel">
+              <el-input
+                  v-model="state.ruleForm.serviceTel"
+                  placeholder="服务电话"
+                  clearable
+                  class="wd350">
+              </el-input>
+            </el-form-item>
+            <el-form-item label="站点电话:" prop="stationTel">
+              <el-input
+                  v-model="state.ruleForm.stationTel"
+                  placeholder="站点电话"
+                  clearable
+                  class="wd350">
+              </el-input>
+            </el-form-item>
+
+
+            <el-form-item label="营业时间描述:" prop="businessHours" class="w100">
+              <el-input
+                  v-model="state.ruleForm.businessHours"
+                  placeholder="营业时间描述"
+                  clearable
+                  type="textarea"
+                  class="w100">
+              </el-input>
+            </el-form-item>
+
+            <el-form-item label="停车费描述:" prop="parkingFee" class="w100">
+              <el-input
+                  v-model="state.ruleForm.parkingFee"
+                  type="textarea"
+                  placeholder="停车费描述:eg:洗车费用满10元减免1小时停车费"
+                  clearable
+                  class="w100">
+              </el-input>
+            </el-form-item>
+
+            <el-form-item label="停车费减免二维码文本:" prop="parkingQrCode" class="w100">
+              <el-input
+                  v-model="state.ruleForm.parkingQrCode"
+                  type="textarea"
+                  placeholder="停车费减免二维码文本"
+                  clearable
+                  class="w100">
+              </el-input>
+            </el-form-item>
+
+
+            <el-form-item label="备注:" prop="remark" class="w100">
+              <el-input
+                  v-model="state.ruleForm.remark"
+                  placeholder="备注"
+                  clearable
+                  type="textarea"
+                  class="w100">
+              </el-input>
+            </el-form-item>
+          </el-form>
+        </el-tab-pane>
+
+        <el-tab-pane label="费率信息" name="fee">
+          <el-form
+              inline
+              :model="state.feeForm"
+              :rules="state.feeRules"
+              label-position="top"
+              ref="feeFormRef"
+              size="default"
+              label-width="100px"
+              class="mt5">
+            <el-form-item label="站点" prop="feeRate" class="wd250">
+              <ext-select
+                  v-model="state.feeForm.stationId"
+                  placeholder="站点"
+                  url="washStation/list"
+                  url-method="post"
+                  label-key="stationName"
+                  value-key="stationId"
+                  data-key="list"
+                  clearable
+                  disabled
+                  class="wd200 ml10"/>
+            </el-form-item>
+            <el-form-item label="绑定平台费率" prop="feeRate" class="wd250">
+              <ext-select
+                  v-model="state.feeForm.feeRateId"
+                  placeholder="平台费率配置"
+                  clearable
+                  :dataList="state.platformFeeRateList"
+                  @on-change="handlePlatformFeeRateChange"
+                  class="wd200 ml10"/>
+            </el-form-item>
+
+            <el-form-item label="平台费率(0.1代表10%)" prop="feeRate" class="wd250">
+              <el-input-number
+                  v-model="state.feeForm.feeRate"
+                  placeholder="平台费率(0.1代表10%)"
+                  clearable
+                  step="0.1"
+                  class="w100">
+              </el-input-number>
+            </el-form-item>
+            <el-form-item label="充值冻结金额比例(0.3代表30%)" prop="frozenRatio" class="wd250">
+              <el-input-number
+                  v-model="state.feeForm.frozenRatio"
+                  placeholder="充值冻结金额比例(0.3代表30%)"
+                  clearable
+                  step="0.1"
+                  class="w100">
+              </el-input-number>
+            </el-form-item>
+
+            <el-form-item label="提现手续费率(0.006代表6‰)" prop="withdrawalFeeRate" class="wd250">
+              <el-input-number
+                  v-model="state.feeForm.withdrawalFeeRate"
+                  placeholder="提现手续费率(0.006代表6‰)"
+                  clearable
+                  step="0.001"
+                  class="w100">
+              </el-input-number>
+            </el-form-item>
+          </el-form>
+
+        </el-tab-pane>
+      </el-tabs>
+
+
+      <!--        <el-form-item label="站点引导" prop="siteGuide">
+                <el-input
+                    v-model="state.ruleForm.siteGuide"
+                    placeholder="站点引导"
+                    clearable
+                    class="wd350">
+                </el-input>
+              </el-form-item>-->
+
 
       <template #footer>
 				<span class="dialog-footer">
@@ -181,6 +250,7 @@ import {$body, $get} from "/@/utils/request";
 import u from '/@/utils/u'
 import ExtUpload from "/@/components/form/ExtUpload.vue";
 import ExtDSelect from "/@/components/form/ExtDSelect.vue";
+import ExtSelect from "/@/components/form/ExtSelect.vue";
 
 // 引入组件
 
@@ -191,7 +261,10 @@ const formRef = ref();
 const initState = () => ({
   ruleForm: {
     id: 0,
-    location:{}
+    location: {}
+  },
+  feeForm: {
+    stationId: null as any
   },
   btnLoading: false,
   dialog: {
@@ -201,7 +274,14 @@ const initState = () => ({
     submitTxt: '',
   },
   rules: {},
-  location:{}
+  feeRules: {
+
+  },
+  location: {},
+  tab: 'basic',
+  platformFeeRateList: [],
+  stationFeeRate: [],
+  action:''
 })
 
 // 定义变量内容
@@ -210,14 +290,20 @@ 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.action = action;
+  state.dialog.title = u.dialog.actions[action].title + "『站点信息』"
+  state.dialog.submitTxt = u.dialog.actions[action].btn + "『站点信息』"
   state.dialog.isShowDialog = true;
   if (action !== 'add') {
     loadData(row.id);
+    state.feeForm.stationId = row.stationId
+    loadStationFeeRate();
   } else {
     state.ruleForm = Object.assign(state.ruleForm, row);
+
   }
+
+  loadPlatformFeeRateList()
 };
 // 关闭弹窗
 const onClose = () => {
@@ -230,23 +316,37 @@ const onCancel = () => {
 };
 // 提交
 const onSubmit = () => {
-  formRef.value.validate((v: boolean) => {
-    if (v) {
-      state.btnLoading = true;
-      state.ruleForm.location = JSON.stringify(state.location)
-      const url = state.ruleForm.id > 0 ? "washStation/modify" : "washStation/add"
-      $body(url, state.ruleForm).then(() => {
+  if(state.tab==='basic'){
+    formRef.value.validate((v: boolean) => {
+      if (v) {
+        state.btnLoading = true;
+        state.ruleForm.location = JSON.stringify(state.location)
+        const url = !!state.ruleForm.id ? "washStation/modify" : "washStation/add"
+        $body(url, state.ruleForm).then(() => {
+          state.btnLoading = false;
+          Msg.message('操作成功');
+          //console.log('submit!')
+          onClose();
+          emit('refresh');
+        })
+      } else {
         state.btnLoading = false;
-        Msg.message('操作成功');
-        //console.log('submit!')
-        onClose();
-        emit('refresh');
-      })
-    } else {
+        Msg.message('请先完整填写表单', 'error');
+      }
+    })
+  }else{
+    state.btnLoading = true;
+    $body(`station-fee-rate/bind`,state.feeForm).then(res=>{
+      Msg.message('站点费率绑定成功');
       state.btnLoading = false;
-      Msg.message('请先完整填写表单', 'error');
-    }
-  })
+      onClose();
+      emit('refresh');
+    }).catch(e=>{
+      Msg.message('操作失败','error');
+      state.btnLoading = false;
+    })
+  }
+
 };
 
 const handleFormChange = (formData: any) => {
@@ -261,6 +361,40 @@ const loadData = (id: number) => {
   })
 }
 
+const loadStationFeeRate = () => {
+  $get(`station-fee-rate/${state.feeForm.stationId}`).then(res => {
+    state.feeForm = res;
+  })
+}
+
+const loadPlatformFeeRateList = () => {
+
+  $body(`platform-fee-rate/list`, {pageSize: 1024}).then(res => {
+    state.platformFeeRateList = res.list;
+  })
+}
+
+const handlePlatformFeeRateChange = (platformFeeRateId: any) => {
+  console.log(platformFeeRateId)
+  let rate = state.platformFeeRateList.find(k => k.id == platformFeeRateId);
+  if (rate) {
+    let {feeRate, withdrawalFeeRate, frozenRatio} = rate;
+    state.feeForm = Object.assign({}, state.feeForm, {feeRate, withdrawalFeeRate, frozenRatio})
+  } else {
+    state.feeForm.withdrawalFeeRate = 0;
+    state.feeForm.feeRate = 0;
+    state.feeForm.frozenRatio = 0;
+  }
+}
+
+const handleTabChange = (tab:string) => {
+  if(tab==='basic'){
+    state.dialog.submitTxt = u.dialog.actions[state.action].btn + "『站点信息』"
+  }else if(tab==='fee'){
+    state.dialog.submitTxt = u.dialog.actions[state.action].btn + "『站点费率』"
+  }
+}
+
 // 暴露变量
 defineExpose({
   open

+ 5 - 3
admin-web/src/views/admin/station/list/index.vue

@@ -91,6 +91,9 @@
             <template v-else-if="field.prop==='updateTime'">
               {{ u.fmt.fmtDateTime(row[field.prop]) }}
             </template>
+            <template v-else-if="field.prop==='parkingQrCode'">
+              <Qrcode :link="row[field.prop]" text="停车二维码"></Qrcode>
+            </template>
             <template v-else-if="field.prop==='action'">
               <el-button  v-auth="'washStation.modify'" size="small" plain type="warning" @click="handleRowClick('edit',row)">编辑</el-button>
               <el-button  v-auth="'washStation.modify'" size="small" plain type="danger" @click="handleRowDelete(row)">删除</el-button>
@@ -121,13 +124,12 @@ const {proxy}: any = getCurrentInstance();
 
 import ExtPage from '/@/components/form/ExtPage.vue'
 import ExtQueryForm from "/@/components/form/ExtQueryForm.vue";
-import ExtTable from "/@/components/form/ExtTable.vue";
 
-import mittBus from '/@/utils/mitt';
 
 import {ElButton} from 'element-plus'
 import ExtDLabel from "/@/components/form/ExtDLabel.vue";
 import ExtImage from "/@/components/form/ExtImage.vue";
+import Qrcode from "/@/components/qrcode/index.vue";
 
 
 const WashStationDialog = defineAsyncComponent(() => import("/@/views/admin/station/list/dialog.vue"));
@@ -163,7 +165,7 @@ const state = reactive({
     {label: '营业时间', prop: 'businessHours', query: false, type: 'text', resizable: true},
     {label: '工位数量', prop: 'parkingNum', query: false, type: '', resizable: true},
     {label: '停车费用', prop: 'parkingFee', query: false, type: '', resizable: true},
-    {label: '停车减免二维码', prop: 'parkingQrCode', query: false, type: '', resizable: true},
+    {label: '停车减免二维码', width:200,prop: 'parkingQrCode', query: false, type: '', resizable: true},
     {label: '创建时间 ', prop: 'createTime', query: false, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
     {label: '更新时间', prop: 'updateTime', query: false, sortable: 'custom', type: 'datetime', resizable: true, conf: {format: (val: any) => u.fmt.fmtDate(val)}},
     {

+ 8 - 0
car-wash-admin/src/main/java/com/kym/admin/controller/DeviceConfigController.java

@@ -6,6 +6,7 @@ import com.kym.common.annotation.SysLog;
 import com.kym.common.controller.IController;
 import com.kym.entity.DeviceConfig;
 import com.kym.entity.queryParams.DeviceConfigParams;
+import com.kym.entity.queryParams.DeviceConfigQueryParams;
 import com.kym.service.DeviceConfigService;
 import jakarta.validation.Valid;
 import org.springframework.web.bind.annotation.*;
@@ -81,4 +82,11 @@ public class DeviceConfigController extends IController {
     }
 
 
+    @PostMapping("list")
+    @SysLog(value = "查询设备配置")
+    public R<?> list(@Valid @RequestBody DeviceConfigQueryParams params) {
+        return resp(() -> deviceConfigService.list(params));
+    }
+
+
 }

+ 2 - 2
car-wash-admin/src/main/java/com/kym/admin/controller/StationFeeRateController.java

@@ -43,10 +43,10 @@ public class StationFeeRateController extends IController {
      *
      * @return Res
      */
-    @GetMapping("/{stationId}")
+    @GetMapping("{stationId}")
     @SysLog(value = "获取站点平台费率配置")
     public R<?> get(@PathVariable String stationId) {
-        return resp((t) -> stationFeeRateService.getStationFeeRate(stationId));
+        return resp(() -> stationFeeRateService.getStationFeeRate(stationId));
     }
 
 

+ 4 - 0
car-wash-entity/src/main/java/com/kym/entity/DeviceConfig.java

@@ -220,4 +220,8 @@ public class DeviceConfig {
     @Expose
     private LocalDateTime updateTime;
 
+    private String name;
+    private String remark;
+
+
 }

+ 13 - 0
car-wash-entity/src/main/java/com/kym/entity/queryParams/DeviceConfigQueryParams.java

@@ -0,0 +1,13 @@
+package com.kym.entity.queryParams;
+
+import com.kym.entity.common.PageParams;
+import lombok.Data;
+
+
+@Data
+public class DeviceConfigQueryParams extends PageParams {
+
+    private static final long serialVersionUID = 1L;
+
+    private String stationId;
+}

+ 3 - 1
car-wash-mapper/src/main/resources/mappers/DeviceConfigMapper.xml

@@ -4,6 +4,8 @@
 
     <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.kym.entity.DeviceConfig">
+        <result column="name" property="name" />
+        <result column="remark" property="remark" />
         <result column="user_message_1" property="userMessage1" />
         <result column="user_message_2" property="userMessage2" />
         <result column="water_price" property="waterPrice" />
@@ -61,7 +63,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        user_message_1, user_message_2, water_price, foam_price, cleaner_price, tap_price, user_ext_price, prepay_money, work_light_delay, tap_on_delay, idle_timeout, notice_threshold_idle, operation_timeout, notice_threshold_operation, motor_on_delay, motor_off_delay, motor_on_interval, motor_flow_on, motor_flow_off, work_mode, work_time_period_1, work_time_period_2, light_mode, light_time_period_1, light_time_period_2, tts_open_by_button, tts_open_by_network, tts_close_by_button, tts_close_by_network, tts_close_by_idle_timeout, tts_close_by_operation_timeout, tts_close_by_no_balance, tts_close_by_sys_error, tts_water_on, tts_water_off, tts_foam_on, tts_foam_off, tts_cleaner_on, tts_cleaner_off, tts_tap_on, tts_tap_off, tts_user_ext_on, tts_user_ext_off, tts_alert_sys_boot, tts_alert_idle_mode, tts_alert_sleep_mode, tts_alert_config_mode, tts_alert_maintenance_mode, tts_alert_no_water, tts_alert_no_foam, tts_alert_motor_error, tts_alert_idle_remain_time, tts_alert_operation_remain_time
+        name,remark,user_message_1, user_message_2, water_price, foam_price, cleaner_price, tap_price, user_ext_price, prepay_money, work_light_delay, tap_on_delay, idle_timeout, notice_threshold_idle, operation_timeout, notice_threshold_operation, motor_on_delay, motor_off_delay, motor_on_interval, motor_flow_on, motor_flow_off, work_mode, work_time_period_1, work_time_period_2, light_mode, light_time_period_1, light_time_period_2, tts_open_by_button, tts_open_by_network, tts_close_by_button, tts_close_by_network, tts_close_by_idle_timeout, tts_close_by_operation_timeout, tts_close_by_no_balance, tts_close_by_sys_error, tts_water_on, tts_water_off, tts_foam_on, tts_foam_off, tts_cleaner_on, tts_cleaner_off, tts_tap_on, tts_tap_off, tts_user_ext_on, tts_user_ext_off, tts_alert_sys_boot, tts_alert_idle_mode, tts_alert_sleep_mode, tts_alert_config_mode, tts_alert_maintenance_mode, tts_alert_no_water, tts_alert_no_foam, tts_alert_motor_error, tts_alert_idle_remain_time, tts_alert_operation_remain_time
     </sql>
 
 </mapper>

+ 2 - 1
car-wash-miniapp/src/main/resources/application-dev.yml

@@ -14,7 +14,8 @@ spring:
       maximum-pool-size: 10
       idle-timeout: 60000
       connection-timeout: 10000
-    url: jdbc:mysql://121.40.98.15:3307/car_wash?serverTimezone=Asia/Shanghai
+      validation-timeout: 3000
+    url: jdbc:mysql://121.40.98.15:3307/car_wash?serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&allowMultiQueries=true
     username: root
     password: KuaiyuMan/*-
     driver-class-name: com.mysql.cj.jdbc.Driver

+ 4 - 0
car-wash-service/src/main/java/com/kym/service/DeviceConfigService.java

@@ -1,7 +1,9 @@
 package com.kym.service;
 
 import com.kym.entity.DeviceConfig;
+import com.kym.entity.common.PageBean;
 import com.kym.entity.queryParams.DeviceConfigParams;
+import com.kym.entity.queryParams.DeviceConfigQueryParams;
 import com.kym.service.mybatisplus.MyBaseService;
 
 /**
@@ -21,4 +23,6 @@ public interface DeviceConfigService extends MyBaseService<DeviceConfig> {
     void batchModify(DeviceConfigParams params);
 
     Object readDeviceConfig(String shortId);
+
+    PageBean<DeviceConfig> list(DeviceConfigQueryParams params);
 }

+ 15 - 0
car-wash-service/src/main/java/com/kym/service/impl/DeviceConfigServiceImpl.java

@@ -1,9 +1,14 @@
 package com.kym.service.impl;
 
+import com.github.pagehelper.PageHelper;
 import com.kym.common.utils.CommUtil;
 import com.kym.entity.DeviceConfig;
+import com.kym.entity.PlatformFeeRate;
 import com.kym.entity.WashDevice;
+import com.kym.entity.common.PageBean;
 import com.kym.entity.queryParams.DeviceConfigParams;
+import com.kym.entity.queryParams.DeviceConfigQueryParams;
+import com.kym.entity.queryParams.PlatformFeeRateQueryParam;
 import com.kym.mapper.DeviceConfigMapper;
 import com.kym.service.DeviceConfigService;
 import com.kym.service.WashDeviceService;
@@ -11,6 +16,7 @@ import com.kym.service.awoara.AwoaraService;
 import com.kym.service.cache.KymCache;
 import com.kym.service.mybatisplus.MyBaseServiceImpl;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * <p>
@@ -58,5 +64,14 @@ public class DeviceConfigServiceImpl extends MyBaseServiceImpl<DeviceConfigMappe
         return awoaraService.readConfig(KymCache.INSTANCE.getProductKeyAndDeviceNameByWashShortId(shortId)[0], KymCache.INSTANCE.getProductKeyAndDeviceNameByWashShortId(shortId)[1]);
     }
 
+    @Override
+    public PageBean<DeviceConfig> list(DeviceConfigQueryParams params) {
+        PageHelper.startPage(params.getPageNum(), params.getPageSize());
+        var res = lambdaQuery()
+                .orderByDesc(DeviceConfig::getId)
+                .list();
+        return new PageBean<>(res);
+    }
+
 
 }

+ 10 - 10
car-wash-service/src/main/java/com/kym/service/wechat/impl/WxPayServiceImpl.java

@@ -144,16 +144,16 @@ public class WxPayServiceImpl implements WxPayService {
             ClassPathResource resource = new ClassPathResource(conf.getKeyPath());
             privateKey = IoUtil.read(resource.getInputStream(), StandardCharsets.UTF_8);
         }
-        config = new RSAAutoCertificateConfig.Builder()
-                .merchantId(conf.getMchid()) // 商户号
-                .privateKey(privateKey)//商户API私钥路径
-                .merchantSerialNumber(conf.getMchsn()) // 商户证书序列号
-                .apiV3Key(conf.getV3key()) // 商户APIV3密钥
-                .build();
-        jsapiService = new JsapiService.Builder().config(config).build();
-        refundService = new RefundService.Builder().config(config).build();
-
-        wxHttpClient = (OkHttpClientAdapter) new DefaultHttpClientBuilder().newInstance().config(config).build();
+//        config = new RSAAutoCertificateConfig.Builder()
+//                .merchantId(conf.getMchid()) // 商户号
+//                .privateKey(privateKey)//商户API私钥路径
+//                .merchantSerialNumber(conf.getMchsn()) // 商户证书序列号
+//                .apiV3Key(conf.getV3key()) // 商户APIV3密钥
+//                .build();
+//        jsapiService = new JsapiService.Builder().config(config).build();
+//        refundService = new RefundService.Builder().config(config).build();
+//
+//        wxHttpClient = (OkHttpClientAdapter) new DefaultHttpClientBuilder().newInstance().config(config).build();
 
 //        // 初始化微信电子发票开发配置,主要是回调地址 todo 区块链电子发票开通后放开
 //         devConfig();