浏览代码

feat:"优化订单分析,添加MQTT主题管理"

soobin 9 月之前
父节点
当前提交
cb72c92da8

+ 3 - 0
src/assets/language/en.json

@@ -17,6 +17,7 @@
     "nextStep": "Next",
     "dayType": "Daily statistics",
     "monthType": "Monthly statistics",
+    "yearType": "Yearly statistics",
     "monthTip1": "The time span cannot exceed six months",
     "monthTip2": "The end time cannot be later than the start time"
   },
@@ -1387,6 +1388,8 @@
     "emailCodeLabel": "Verification code",
     "emailCodePlaceholder": "Please enter the email verification code",
     "emailCodeRequired": "Please enter the email verification code",
+    "invitationCode": "Invitation Code (optional)",
+    "invitationCodePlaceholder": "Please enter the invitation code",
     "registerButton": "Register",
     "replaysInSeconds": "Replays in seconds",
     "emailRegistration": "Email",

+ 3 - 0
src/assets/language/ja.json

@@ -17,6 +17,7 @@
         "nextStep": "次のステップです",
         "dayType": "日毎に集計する",
         "monthType": "月ごとに集計します",
+        "yearType": "年ごとに集計します",
         "monthTip1": "期間は半年以内です",
         "monthTip2": "終了時間が開始時間より小さくなってはいけません"
     },
@@ -1403,6 +1404,8 @@
         "emailCodeLabel": "メール認証コード",
         "emailCodePlaceholder": "メール認証コードを入力してください",
         "emailCodeRequired": "メール認証コードを入力してください",
+        "invitationCode": "招待コードです(任意)",
+        "invitationCodePlaceholder": "招待コードの入力をお願いします",
         "registerButton": "登録する",
         "replaysInSeconds": "数秒後に再送信できます",
         "emailRegistration": "メールでの登録",

+ 17 - 5
src/assets/language/zh.json

@@ -17,6 +17,7 @@
     "nextStep": "下一步",
     "dayType": "按日统计",
     "monthType": "按月统计",
+    "yearType": "按年统计",
     "monthTip1": "时间跨度不能超过半年",
     "monthTip2": "结束时间不能小于开始时间"
   },
@@ -1102,7 +1103,7 @@
     "taskMessage": "任务消息",
     "discountCode": "优惠码",
     "accountPermission": "账号权限",
-    "orderExport": "订单导出",
+    "orderExport": "订单分析",
     "shandeSubLedger": "杉德分账",
     "salesRanking": "销售排行",
     "settlementAccount": "结算账号",
@@ -1137,9 +1138,10 @@
   "mqtt": {
     "title": "MQTT管理",
     "message": "消息管理",
+    "topicManagement": "主题管理",
     "sendMessage": "发送消息",
-    "topic": "主题",
-    "topicPlaceholder": "请输入主题",
+    "topic": "主题名称",
+    "topicPlaceholder": "请输入主题名称",
     "qos": "QoS等级",
     "send": "发送",
     "messageContent": "消息内容",
@@ -1147,7 +1149,15 @@
     "sendSuccess": "发送成功",
     "sendFailed": "发送失败",
     "messageList": "消息列表",
-    "receiveTime": "接收时间"
+    "receiveTime": "接收时间",
+    "addTopic": "添加主题",
+    "topicDescription": "主题描述",
+    "topicDescriptionPlaceholder": "请输入主题描述",
+    "sumbit": "提交",
+    "addSuccessfully": "添加成功",
+    "addFailed": "添加失败",
+    "topicList": "已订阅主题列表",
+    "createTime": "创建时间"
   },
   "huifuMch": {
     "standbyWithdrawalAccountNo": "备用提现账号",
@@ -1448,6 +1458,8 @@
     "emailCodeLabel": "验证码",
     "emailCodePlaceholder": "请输入邮箱验证码",
     "emailCodeRequired": "请输入邮箱验证码",
+    "invitationCode": "邀请码(选填)",
+    "invitationCodePlaceholder": "请输入邀请码",
     "registerButton": "注册",
     "replaysInSeconds": "秒后可重发",
     "emailRegistration": "邮箱注册",
@@ -1700,6 +1712,6 @@
   "优惠码": "优惠码",
   "报警历史": "报警历史",
   "广告管理": "广告管理",
-  "订单导出": "订单导出",
+  "订单分析": "订单分析",
   "apk管理": "apk管理"
 }

二进制
src/assets/mqtt/topic.png


+ 65 - 0
src/components/dateOrderList/index.less

@@ -0,0 +1,65 @@
+.dateSelectListBox {
+  .Tabs1 {
+    background-color: rgba(255, 255, 255, 1);
+    z-index: 29;
+    border: 0.5px solid rgba(185, 186, 208, 1);
+    width: calc(100% - 30px);
+    margin: 0 auto;
+    position: relative;
+    box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.1);
+
+
+    .timeTabBox {
+      display: flex;
+
+      .timeTab {
+        width: 20%;
+        text-align: center;
+        overflow-wrap: break-word;
+        color: rgba(64, 77, 116, 1);
+        font-size: 0.35rem;
+        text-align: center;
+        white-space: nowrap;
+        line-height: 1rem;
+        border-right: 1px solid #B9BAD0;
+        &:last-child{
+          border-right: none;
+        }
+        .block3 {
+          width: 0;
+          height: 0;
+          border-left: 3px solid transparent;
+          border-right: 3px solid transparent;
+          border-bottom: 3px solid rgba(64, 77, 116, 1);
+          transform: rotate(180deg);
+          display: inline-block;
+          margin-bottom: 3px;
+          margin-left: 3px;
+        }
+      }
+
+      .active {
+        background: #4d6add;
+        color: #fff;
+        position: relative;
+
+        .block3 {
+          border-bottom-color: #fff;
+        }
+
+        &::after {
+          position: absolute;
+          top: 34px;
+          left: 65%;
+          width: 0;
+          height: 0;
+          border-left: 5px solid transparent;
+          border-right: 5px solid transparent;
+          border-bottom: 5px solid #4d6add;
+          transform: rotate(180deg);
+          content: "";
+        }
+      }
+    }
+  }
+}

+ 270 - 0
src/components/dateOrderList/index.vue

@@ -0,0 +1,270 @@
+<template>
+  <div class="dateSelectListBox flex-col">
+    <div class="Tabs1 flex-col">
+      <div class="timeTabBox">
+        <div class="timeTab" :class="{ active: timeType === '0' }" @click="timeChange('0')">
+          {{ $t('dateSelectList.today') }}</div>
+        <div class="timeTab" :class="{ active: timeType === '1' }" @click="timeChange('1')">
+          {{ $t('dateSelectList.yesterday') }}</div>
+        <div class="timeTab" :class="{ active: timeType === '2' }" @click="timeChange('2')">
+          {{ $t('dateSelectList.thisWeek') }}</div>
+        <div class="timeTab" :class="{ active: timeType === '3' }" @click="timeChange('3')">
+          {{ $t('dateSelectList.thisMonth') }}</div>
+        <div class="timeTab" :class="{ active: timeType === '4' }" @click="timeChange('4')">
+          {{ $t('dateSelectList.other') }}
+          <div class="block3 flex-col"></div>
+        </div>
+        <!-- 日期选择 -->
+        <van-calendar color="#4d6add" v-model:show="calendarShow" type="range" :show-confirm="false"
+          :allow-same-day="true" :min-date="minDate" @confirm="calendarConfirm" />
+        <!-- 选择统计类型 -->
+        <van-popup v-model:show="timeTypeShow" position="bottom">
+          <van-picker :title="$t('dateSelectList.timeType')" :columns="timeTypeList" @confirm="timeTypeConfirm"
+            @cancel="timeTypeCancel" />
+        </van-popup>
+        <!-- 按月统计 -->
+        <van-popup v-model:show="monthDateShow" position="bottom">
+          <van-date-picker v-model="monthDate" :title="$t('dateSelectList.monthDate')" :min-date="minDate" :max-date="maxDate" @confirm="monthDateConfirm" @cancel="monthDateCancel"
+            :columns-type="monthDateType" />
+        </van-popup>
+        <!-- 按年统计 -->
+        <van-popup v-model:show="yearDateShow" position="bottom">
+          <van-date-picker v-model="yearDate" :title="$t('dateSelectList.monthDate')" :min-date="minDate" :max-date="maxDate" @confirm="yearDateConfirm" @cancel="yearDateCancel"
+            :columns-type="yearDateType" />
+        </van-popup>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { ref } from "vue";
+import dateUtil from "@/utils/dateUtil";
+import { getLoginUser } from "@/common/js/utils";
+// import { showToast } from "vant";
+import { useI18n } from 'vue-i18n';
+
+
+
+
+export default {
+  name: "dateSelectList",
+  components: {},
+  setup(props, { emit }) {
+    const minDate = new Date(2022, 0, 1);
+    const maxDate = new Date();
+    const { t } = useI18n();
+    const user = getLoginUser();
+    // 时间类型
+    const timeType = ref("0");
+    // 统计类型
+    const chartType = ref("0");
+    // 时间类型切换
+    const timeChange = (data) => {
+      timeType.value = data;
+      // 当时时间类型切换为其他时间弹出日期控件
+      // (data === "4") ? calendarShow.value = true : outputDate();
+      (data === "4") ? timeTypeShow.value = true : outputDate();
+    };
+    let calendarDate = [];
+    const calendarShow = ref(false);
+    // 其他时间日期组件确认返回
+    const calendarConfirm = (data) => {
+      calendarShow.value = false;
+      console.log(data);
+      calendarDate = data;
+      outputDate();
+    };
+    const outputDate = async () => {
+      const params = {};
+      // 今日
+      if (timeType.value === "0") {
+        params.chartType = "day";
+        params.startDate = dateUtil.formateDate(
+          new Date(new Date(new Date().getTime()).setHours(0, 0, 0, 0)),
+          "yyyy-MM-dd hh:mm:ss"
+        );
+        params.endDate = dateUtil.formateDate(
+          new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 59)),
+          "yyyy-MM-dd hh:mm:ss"
+        );
+        params.dateType = "2";
+      }
+      // 昨日
+      if (timeType.value === "1") {
+        let yesterday = new Date();
+        yesterday.setDate(yesterday.getDate() - 1);
+        params.chartType = "day";
+        params.startDate = dateUtil.formateDate(
+          new Date(new Date(yesterday.getTime()).setHours(0, 0, 0, 0)),
+          "yyyy-MM-dd hh:mm:ss"
+        );
+        params.endDate = dateUtil.formateDate(
+          new Date(new Date(yesterday.getTime()).setHours(23, 59, 59, 59)),
+          "yyyy-MM-dd hh:mm:ss"
+        );
+        params.dateType = "2";
+      }
+      // 本周
+      if (timeType.value === "2") {
+        const curr = new Date();
+        const dayOfWeek = curr.getDay();
+        // 如果是周日,将当前日期设置到本周的周一
+        if (dayOfWeek === 0 && user.ifForeign != '1') {
+          curr.setDate(curr.getDate() - 6); // 将当前日期设置为本周的周一
+        }
+        const firstday = new Date(
+          curr.setDate(curr.getDate() - curr.getDay() + 1)
+        );
+        const lastday = new Date(
+          curr.setDate(curr.getDate() - curr.getDay() + 7)
+        );
+        params.chartType = "week";
+        params.startDate =
+          dateUtil.formateDate(firstday, "yyyy-MM-dd") + " 00:00:00";
+        params.endDate =
+          dateUtil.formateDate(lastday, "yyyy-MM-dd") + " 23:59:59";
+        params.dateType = "2";
+      }
+      // 本月
+      if (timeType.value === "3") {
+        const now = new Date(); //当前日期
+        const nowMonth = now.getMonth(); //当前月
+        const nowYear = now.getFullYear(); //当前年
+        params.chartType = "month";
+        params.startDate =
+          dateUtil.formateDate(new Date(nowYear, nowMonth, 1), "yyyy-MM-dd") +
+          " 00:00:00";
+        params.endDate =
+          dateUtil.formateDate(
+            new Date(nowYear, nowMonth + 1, 0),
+            "yyyy-MM-dd"
+          ) + " 23:59:59";
+          params.dateType = "0";
+      }
+      // 其他时间
+      if (timeType.value === "4") {
+        let startTime = null;
+        let endTime = null;
+        if (chartType.value == '1') {
+          // 按日统计
+          const firstday = Date.parse(calendarDate[0]);
+          const lastday = Date.parse(calendarDate[1]);
+          const diffDate = Math.abs(firstday - lastday);
+          const totalDays = Math.floor(diffDate / (1000 * 3600 * 24));
+          if (totalDays === 0) {
+            params.chartType = "day";
+          } else {
+            params.chartType = "month";
+          }
+          timeTypeShow.value = false;
+          startTime = calendarDate[0];
+          endTime = calendarDate[1];
+          params.startDate =
+            dateUtil.formateDate(startTime, "yyyy-MM-dd") + " 00:00:00";
+          params.endDate =
+            dateUtil.formateDate(endTime, "yyyy-MM-dd") + " 23:59:59";
+          params.dateType = "2";
+        } else if (chartType.value == '2') {
+          // 按月统计
+          params.chartType = "month";
+          startTime = new Date(monthDate.value[0] + '-' + monthDate.value[1] + '-01');
+          endTime = new Date(monthDate.value[0], monthDate.value[1], 0);
+
+          params.startDate =
+            dateUtil.formateDate(startTime, "yyyy-MM-dd") + " 00:00:00";
+          params.endDate =
+            dateUtil.formateDate(endTime, "yyyy-MM-dd") + " 23:59:59";
+          timeTypeShow.value = false;
+          params.dateType = "0";
+        } else if (chartType.value == '3') {
+          // 按年统计
+          params.chartType = "year";
+          startTime = new Date(monthDate.value[0] + '-01-01');
+          endTime = new Date(monthDate.value[0] + '-12-31');
+          params.startDate =
+            dateUtil.formateDate(startTime, "yyyy-MM-dd") + " 00:00:00";
+          params.endDate =
+            dateUtil.formateDate(endTime, "yyyy-MM-dd") + " 23:59:59";
+          timeTypeShow.value = false;
+          params.dateType = "1";
+        }
+
+      }
+      emit('update', params);
+    };
+    // 统计类型
+    const timeTypeShow = ref(false);
+    const timeTypeList = [
+      { text: t('dateSelectList.dayType'), value: "1" },
+      { text: t('dateSelectList.monthType'), value: "2" },
+      { text: t('dateSelectList.yearType'), value: "3" },
+    ];
+    const timeTypeConfirm = ({ selectedOptions }) => {
+      console.log('selectedOptions[0].text', selectedOptions[0].value);
+      // 按日统计
+      chartType.value = selectedOptions[0].value;
+      if (selectedOptions[0].value == '1') {
+        calendarShow.value = true;
+      } else if (selectedOptions[0].value == '2') {
+        monthDateShow.value = true;
+      } else if (selectedOptions[0].value == '3') {
+        yearDateShow.value = true;
+      }
+
+    }
+    const timeTypeCancel = () => { timeTypeShow.value = false; }
+    // 按月统计
+    // 创建一个Date对象,表示当前时间
+    const currentDate = new Date();
+    // 获取当前时间的年份
+    const currentYear = currentDate.getFullYear();
+    // 获取当前时间的月份(注意,月份是从0开始的,所以要加1)
+    const currentMonth = currentDate.getMonth() + 1;
+    const monthDateShow = ref(false);
+    const monthDate = ref([currentYear, currentMonth]);
+    const monthDateType = ['year', 'month'];
+    const monthDateConfirm = () => {
+      monthDateShow.value = false;
+      outputDate();
+    }
+    const monthDateCancel = () => { monthDateShow.value = false; }
+    // 按年统计
+    const yearDateShow = ref(false);
+    const yearDateType = ['year'];
+    const yearDate = ref([currentYear]);
+    const yearDateConfirm = () => {
+      yearDateShow.value = false;
+      outputDate();
+    }
+    const yearDateCancel = () => { yearDateShow.value = false; }
+    return {
+      minDate,
+      maxDate,
+      timeType,
+      timeChange,
+      calendarShow,
+      calendarConfirm,
+      timeTypeShow,
+      timeTypeList,
+      timeTypeConfirm,
+      timeTypeCancel,
+      monthDateShow,
+      monthDateType,
+      monthDateConfirm,
+      monthDateCancel,
+      monthDate,
+      yearDateShow,
+      yearDateType,
+      yearDateConfirm,
+      yearDateCancel,
+      yearDate
+    };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+@import "@/common/style/common.less";
+@import "./index.less";
+</style>

+ 0 - 13
src/components/dateSelectList/index.vue

@@ -24,15 +24,6 @@
         </van-popup>
         <!-- 按月统计 -->
         <van-popup v-model:show="monthDateShow" position="bottom">
-          <!-- 
-          <van-picker-group :title="$t('dateSelectList.monthDate')"
-            :tabs="[$t('dateSelectList.monthStartDate'), $t('dateSelectList.monthEndDate')]"
-            :next-step-text="$t('dateSelectList.nextStep')" @confirm="monthDateConfirm" @cancel="monthDateCancel"
-            position="bottom">
-            <van-date-picker v-model="monthStartDate" :min-date="minDate" :columns-type="monthDateType" />
-            <van-date-picker v-model="monthEndDate" :min-date="minDate" :columns-type="monthDateType" />
-          </van-picker-group>
-          -->
           <van-date-picker v-model="monthDate" :title="$t('dateSelectList.monthDate')" :min-date="minDate" :max-date="maxDate" @confirm="monthDateConfirm" @cancel="monthDateCancel"
             :columns-type="monthDateType" />
         </van-popup>
@@ -216,8 +207,6 @@ export default {
     // 获取当前时间的月份(注意,月份是从0开始的,所以要加1)
     const currentMonth = currentDate.getMonth() + 1;
     const monthDateShow = ref(false);
-    const monthStartDate = ref([currentYear, currentMonth]);
-    const monthEndDate = ref([currentYear, currentMonth]);
     const monthDate = ref([currentYear, currentMonth]);
     const monthDateType = ['year', 'month'];
     const monthDateConfirm = () => {
@@ -239,9 +228,7 @@ export default {
       timeTypeConfirm,
       timeTypeCancel,
       monthDateShow,
-      monthStartDate,
       monthDateType,
-      monthEndDate,
       monthDateConfirm,
       monthDateCancel,
       monthDate,

+ 28 - 5
src/components/typeDownMenu/index.vue

@@ -192,7 +192,7 @@ export default {
     // 商户筛选
     const adminType = ref(null);
     // 商户筛选标题
-    const adminTypeTitle = ref(t('typeSelectList.thisMerchant'));
+    const adminTypeTitle = ref('选择商户');
     // 商户列表
     const adminList = ref([]);
     // 获取商户列表
@@ -200,10 +200,11 @@ export default {
       const { data } = await getAdminList({ adminId: user.id });
       if (data.code === '00000') {
         adminList.value = data.data.map(item => {
-          return { text: item.username, value: item.id }
+          return { text: item.username, value: item.id, ifForeign: item.ifForeign }
         });
-        adminList.value.unshift({ text: t('typeSelectList.allSuboUsers'), value: 'all' });
-        adminList.value.unshift({ text: t('typeSelectList.thisMerchant'), value: 'this' });
+        console.log("adminList",adminList.value)
+        // adminList.value.unshift({ text: t('typeSelectList.allSuboUsers'), value: 'all' });
+        // adminList.value.unshift({ text: t('typeSelectList.thisMerchant'), value: 'this' });
       }
     }
     // 选择商户
@@ -213,6 +214,27 @@ export default {
       searchParams.adminType = (adminTypeTitle.value === t('typeSelectList.allSuboUsers')) ? 'all' : '';
       searchParams.userName = (adminTypeTitle.value === t('typeSelectList.allSuboUsers')) ? 'all' : (adminTypeTitle.value === t('typeSelectList.thisMerchant')) ? user.username : adminTypeTitle.value;
       searchParams.adminId = (value ===  ('this' || 'all') ? null : value);
+      searchParams.ifForeign = adminList.value.find(adminList => adminList.value === value).ifForeign;
+      if (searchParams.ifForeign == "0") {
+        payList.value = [
+          { text: t('typeSelectList.whole'), value: '' },
+          { text: t('typeSelectList.mainSweepOfAlipay'), value: 'ALIPAY_NATIVE' },
+          { text: t('typeSelectList.weChatScanning'), value: 'WEIXIN_NATIVE' },
+          { text: t('typeSelectList.antiScanningOfAlipay'), value: 'ALIPAY_CARD' },
+          { text: t('typeSelectList.weChatBackScanning'), value: 'WEIXIN_CARD' },
+          { text: t('typeSelectList.eCNYBackeScanning'), value: 'ECNY_CARD' },
+        ];
+      } else {
+        payList.value = [
+          { text: t('typeSelectList.whole'), value: '' },
+          { text: t('typeSelectList.noPaymentRequired'), value: 0 },
+          { text: t('typeSelectList.coin'), value: 1 },
+          { text: t('typeSelectList.notes'), value: 2 },
+          { text: t('typeSelectList.coinsNotes'), value: 3 },
+          { text: t('typeSelectList.creditCard'), value: 4 },
+          { text: t('typeSelectList.electronicPayment'), value: 5 },
+        ];
+      }
       outputDate();
     }
 
@@ -228,11 +250,12 @@ export default {
       companyType: '',
       machineType: '',
       ifForeign: '',
+      adminId: user.id,
     });
 
 
     // 是否商户
-    const isShowAdmin = () => { return (user && user.type === 1); }
+    const isShowAdmin = () => { return (user && user.type === 4); }
     // 是否管理员
     const isShowCompany = () => { return (user && user.type === 0); }
     // 是否管理员和公司人员

+ 7 - 0
src/router/index.js

@@ -597,6 +597,13 @@ const router = createRouter({
       component: () => import("@/views/mqtt/message/index"),
       meta: { index: 1 },
     },
+    // MQTT主题管理
+    {
+      path: "/topic",
+      name: "topic",
+      component: () => import("@/views/mqtt/topic/index"),
+      meta: { index: 1 },
+    },
   ],
 });
 // 路由守卫处理

+ 15 - 0
src/service/mqtt/index.js

@@ -9,3 +9,18 @@ export function sendMqttMessage(params) {
 export function getMsgList(params) {
   return axios.post(`/SZWL-SERVER/mqtt/getMsgList`, params);
 }
+
+// 添加主题
+export function addMqttTopic(params) {
+  return axios.post(`/SZWL-SERVER/mqtt/addTopic`, params);
+}
+
+// 获取订阅列表
+export function getSubscribeList() {
+  return axios.get(`/SZWL-SERVER/mqtt/getTopicList`);
+}
+
+// 删除订阅
+export function delSubscribe(params) {
+  return axios.post(`/SZWL-SERVER/mqtt/delTopic`, params);
+}

+ 10 - 0
src/service/orderExport/index.js

@@ -7,6 +7,16 @@ export function getOrderList(params) {
   return axios.get(`/ORDER-SERVER/tOrder/orderSelect?${stringToUrl(params)}`);
 }
 
+// 订单分析
+export function getOrderAnalysis(params) {
+  return axios.post(`/ORDER-SERVER/tOrder/orderAnalysis`, params);
+}
+
+// 订单分析导出
+export function Api_getOrderAnalysisExport(params) {
+  return axios.post(`/ORDER-SERVER/tOrder/orderAnalysisExport`, params, {responseType:'blob'});
+}
+
 // 导出Excle
 export function Api_getOrderExport(params) {
   return axios.get(`/ORDER-SERVER/tOrder/onlineExport?${stringToUrl(params)}`,{responseType:'blob'});

+ 11 - 0
src/styles/mqtt/index.less

@@ -25,6 +25,17 @@
             right: 0.18rem;
             top: 0.45rem;
           }
+
+          &.topicIcon::after {
+            content: '';
+            position: absolute;
+            background: #fff url('../../assets/mqtt/topic.png') top center no-repeat;
+            background-size: 100%;
+            width: 0.55rem;
+            height: 0.55rem;
+            right: 0.20rem;
+            top: 0.45rem;
+          }
   
         }
   

+ 15 - 6
src/views/accountPer/add.vue

@@ -20,11 +20,19 @@
           :placeholder="$t('accountPer.phonePlaceholder')" :rules="[
             { required: true, message: $t('accountPer.phonePlaceholder') },
           ]" />
+        <van-field name="radio" label="类型" v-if="user.type === 0">
+          <template #input>
+            <van-radio-group v-model="type" direction="horizontal">
+              <van-radio name="1">公司人员</van-radio>
+              <van-radio name="4">营销经理</van-radio>
+            </van-radio-group>
+          </template>
+        </van-field>
         <van-field v-model="roleText" is-link readonly :label="$t('accountPer.roleLabel')"
-          :placeholder="$t('accountPer.rolePlaceholder')" @click="roleShow = true" />
+          :placeholder="$t('accountPer.rolePlaceholder')" @click="roleShow = true" :rules="[{ required: true, message: $t('accountPer.rolePlaceholder') }]"/>
         <div class="van-cell van-field">
           <div class="van-cell__title van-field__label">
-            <label id="van-field-4-label" for="van-field-4-input">{{ $t("accountPer.setUp") }}:</label>
+            <label id="van-field-4-label" for="van-field-4-input">{{ $t("accountPer.setUp") }}</label>
           </div>
           <div class="van-cell__value">
             <div class="van-field__body">
@@ -34,11 +42,10 @@
             </div>
           </div>
         </div>
-        <van-field colon :border="false" @click-input="busiEquipInpClk" readonly clearable
+        <van-field :border="false" @click-input="busiEquipInpClk" readonly clearable
           v-model="cofficentForm.equipmentNames" :label="$t('accountPer.manageMachinesLabel')"
-          :placeholder="$t('accountPer.manageMachinesPlaceholder')" :rules="[
-            { required: true, message: $t('accountPer.manageMachinesPlaceholder') },
-          ]">
+          :placeholder="$t('accountPer.manageMachinesPlaceholder')" >
+          <!-- :rules="[{ required: true, message: $t('accountPer.manageMachinesPlaceholder') }]" -->
           <template #right-icon>
             <div class="l-flex-RC">
               <van-icon v-if="cofficentForm.equipmentNames" @click="cofficentForm.equipmentNames = ''; equipmentIds = ''"
@@ -114,6 +121,7 @@ export default {
       password: "",
       name: "",
       phone: "",
+      type: user.type === 0 ? "1" : "",
       isEnabled: true,
       roleList: "",
       equipmentIds: "",
@@ -285,6 +293,7 @@ export default {
       kSelectPopEquipRef.value.selPopClose();
     };
     return {
+      user,
       pageTitle,
       roleShow,
       roleText,

+ 1 - 1
src/views/home/index.vue

@@ -84,7 +84,7 @@
       <kNoData v-else></kNoData>
       <!-- </div> -->
       <!-- 常用工具 -->
-      <div v-if="user.type === 0">
+      <div v-if="user.type === 0 || user.type === 4">
         <div class="outer9 flex-col justify-center">
           <div class="main24 flex-col ">
             <div class="ImageText10 flex-col">

+ 7 - 0
src/views/mqtt/index.vue

@@ -10,6 +10,13 @@
                     <div class="taskTitle">{{ $t('mqtt.message') }}</div>
                 </div>
             </div>
+            <!-- 消息管理 -->
+            <div class="taskListRow flex-col" @click="pushPageList('/topic')">
+                <div class="taskIcon topicIcon"></div>
+                <div class="taskRight">
+                    <div class="taskTitle">{{ $t('mqtt.topicManagement') }}</div>
+                </div>
+            </div>
         </div>
     </div>
 </template>

+ 277 - 0
src/views/mqtt/topic/index.vue

@@ -0,0 +1,277 @@
+<template>
+    <!-- MQTT管理 -->
+    <div class="flex-col">
+        <s-header :name="$t('mqtt.topicManagement')" :noback="false"></s-header>
+        <div class="outer flex-col justify-center ">
+            <div class="l-flex-RC">
+                <div class="outer1 flex-col"></div>
+                <div class="TextGroup flex-col">
+                    <span class="txt">{{ $t("mqtt.addTopic") }}</span>
+                </div>
+            </div>
+        </div>
+        <van-form @submit="addTopic">
+            <van-cell-group inset>
+                <van-field v-model="params.topic" :label="$t('mqtt.topic')" :placeholder="$t('mqtt.topicPlaceholder')"
+                    :rules="[{ required: true, message: $t('mqtt.topicPlaceholder') }]" />
+                <van-field name="radio" :label="$t('mqtt.qos')">
+                    <template #input>
+                        <van-radio-group v-model="params.qos" direction="horizontal">
+                            <van-radio :name="0">0</van-radio>
+                            <van-radio :name="1">1</van-radio>
+                            <van-radio :name="2">2</van-radio>
+                        </van-radio-group>
+                    </template>
+                </van-field>
+                <van-field v-model="params.topicDesc" rows="5" autosize :label="$t('mqtt.topicDescription')" type="textarea" :placeholder="$t('mqtt.topicDescriptionPlaceholder')"
+                    :rules="[{ required: true, message: $t('mqtt.topicDescriptionPlaceholder') }]" />
+                <br>
+            </van-cell-group>
+            <div style="margin: 10px 100px;">
+                <van-button round block type="primary" native-type="submit">
+                    {{ $t('mqtt.sumbit') }}
+                </van-button>
+            </div>
+        </van-form>
+        <div class="taskMessageBox flex-col">
+            <div class="searchRow flex-row justify-between">
+                <div class="flex-col">
+                     <div class="flex-row justify-between bd3">
+                        <div class="flex-col outer4"></div>
+                        <span class="flex-col txt2">{{ $t("mqtt.topicList") }}(左滑选择删除)</span>
+                    </div>
+                </div>
+            </div>
+            <div class="listBox">
+                <div v-for="(item, index) in alarmHistoryList" :key="index" class="listItem">
+                    <van-swipe-cell right-width="70">
+                        <div class="itemBox">
+                        <div class="itemRow">
+                            <span class="itemTitle">{{ $t('mqtt.topic') }}:&nbsp;</span>
+                            {{ item.topic }}
+                        </div>
+                        <div class="itemRow">
+                            <span class="itemTitle">{{ $t('mqtt.createTime') }}:&nbsp;</span>
+                            {{ showDateTime(item.createDate) }}
+                        </div>
+                        <div class="itemRow">
+                            <span class="itemTitle">{{ $t('mqtt.qos') }}:&nbsp;</span>
+                            {{ item.qos }}
+                        </div>
+                        <div class="itemRow">
+                            <span class="itemTitle">{{ $t('mqtt.topicDescription') }}:&nbsp;</span>
+                            {{ item.topicDesc }}
+                        </div>
+                    </div>
+                        <template #right>
+                            <van-button square text="删除" color="#df5e4c" class="delete-button" @click="deleteTopic(item.id)"/>
+                        </template>
+                    </van-swipe-cell>
+                    
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import { onMounted, reactive, ref } from 'vue';
+import sHeader from "../../../components/SimpleHeader";
+import { addMqttTopic, getSubscribeList, delSubscribe } from '@/service/mqtt';
+import { showConfirmDialog, showFailToast, showSuccessToast } from "vant";
+import dateUtil from "@/utils/dateUtil";
+import { useI18n } from "vue-i18n";
+
+
+export default {
+    components: { sHeader },
+    setup() {
+        const { t } = useI18n();
+        // 添加主题参数
+        const params = reactive({
+            topic: '',
+            qos: 0,
+            topicDesc: '',
+        });
+        // 添加主题
+        const addTopic = async () => {
+            const { data } = await addMqttTopic(params);
+            if (data.code === "00000") {
+                showSuccessToast(t("mqtt.addSuccessfully"));
+                getList();
+            } else {
+                showFailToast(t("mqtt.addFailed"));
+            }
+        };
+        // 查询消息参数
+        const alarmHistoryList = ref([]); // 列表集合
+        const alarmHistoryTotal = ref(0); // 列表总数
+        // 获取消息列表
+        const getList = async () => {
+            const { data } = await getSubscribeList();
+            if (data.code === "00000") {
+                alarmHistoryList.value = data.data;
+                alarmHistoryTotal.value = alarmHistoryList.value.length;
+            } else {
+                showFailToast(data.message);
+            }
+        };
+        // 格式化时间
+        const showDateTime = (date) => {
+            return date
+                ? dateUtil.formateDate(new Date(date), "yyyy/MM/dd hh:mm:ss")
+                : "";
+        };
+        // 删除主题
+        const deleteTopic =  (id) => {
+            const params = {
+                id: id
+            };
+            showConfirmDialog({
+                title: "提示",
+                message: "确定删除吗?",
+            }).then(async() => {
+                const { data } = await delSubscribe(params)
+                if (data.code === "00000") {
+                    showSuccessToast("删除成功");
+                    getList();
+                } else {
+                    showFailToast(data.message);
+                }
+            }).catch(() => {
+            })
+        };
+
+        // 初始化页面获取列表
+        onMounted(async () => {
+            getList();
+        });
+        return {
+            params,
+            addTopic,
+            alarmHistoryList,
+            alarmHistoryTotal,
+            showDateTime,
+            deleteTopic,
+        };
+    }
+};
+</script>
+
+<style lang="less" scoped>
+@import "../../../common/style/common.less";
+
+.outer {
+    background: rgba(255, 255, 255, 1) url("../../../assets/home/line.png") bottom center no-repeat;
+    background-size: 100% auto;
+    padding: 20px 20px;
+
+    .outer1 {
+        position: relative;
+        width: 15px;
+        height: 15px;
+        margin-right: 5px;
+        background: url("../../../assets/home/machineSales.png") center no-repeat;
+        background-size: 100%;
+    }
+
+    .TextGroup {
+
+        .txt {
+            overflow-wrap: break-word;
+            color: rgba(64, 77, 116, 1);
+            font-size: 0.4rem;
+            font-family: PingFangSC-Semibold;
+            text-align: left;
+            white-space: nowrap;
+            line-height: 0.4rem;
+            display: block;
+        }
+
+    }
+}
+
+.searchRow {
+    padding: 0.5rem 0.5rem;
+    background: rgba(255, 255, 255, 1) url("../../../assets/home/line.png") bottom center no-repeat;
+    background-size: 100%;
+    align-items: center;
+
+    .bd3 {
+
+
+        .outer4 {
+            width: 19px;
+            height: 19px;
+            margin-right: 5px;
+            background: url("../../../assets/taskMessage/retailIcon.png") top center no-repeat;
+            background-size: 100%;
+        }
+
+        .txt2 {
+            overflow-wrap: break-word;
+            color: rgba(64, 77, 116, 1);
+            font-size: 14px;
+            font-family: PingFangSC-Semibold;
+            text-align: left;
+            white-space: nowrap;
+            // line-height: 0.4rem;
+            display: block;
+
+            .discountNumber {
+                font-size: 18px;
+                color: #df5e4c;
+            }
+        }
+    }
+
+    .label3 {
+        color: #4d6add;
+    }
+
+    .main5 {
+        width: auto;
+
+        .label2 {
+            width: 0.5rem;
+            height: 0.5rem;
+            margin-right: 5px;
+            cursor: pointer;
+        }
+    }
+}
+
+.listBox {
+    .listItem {
+        width: 100%;
+        background: url("../../../assets/home/line.png") bottom center no-repeat;
+        background-size: 100%;
+        position: relative;
+        display: inline-block;
+
+        .itemBox {
+            margin: 10px 20px;
+            line-height: 20px;
+
+            .itemRow {
+                width: 100%;
+            }
+
+            .itemTitle {
+                color: #8787a6;
+                font-size: 13px;
+            }
+
+            .discount {
+                padding-left: 2em;
+            }
+        }
+    }
+}
+
+
+.delete-button {
+    height: 100%;
+    padding: 10px 10px;
+}
+</style>

+ 6 - 1
src/views/orderCenter/index.vue

@@ -593,6 +593,11 @@ export default {
     let chartType = "day";
     // 获取订单列表数据
     const getList = async () => {
+      if (user.type === 4 && user.username === searchParams.userName) {
+        loading.value = false;
+        finished.value = true;
+        return;
+      }
       finished.value = false;
       getStatisticsFun();
       // 因为订单的时间格式不一样
@@ -873,7 +878,7 @@ export default {
     // 查询图表
     const getStatisticsFun = async () => {
       const params = {
-        adminId: user.id,
+        adminId: searchParams.adminId === null ? user.id : searchParams.adminId,
         chartType: chartType,
         endDate: searchParams.endDate,
         ifForeign: searchParams.ifForeign == '' ? user.ifForeign : searchParams.ifForeign,

+ 42 - 24
src/views/orderExport/index.vue

@@ -71,9 +71,9 @@
               </div>
               <span class="info9">{{ $t("orderExport.address") }}:{{ item.address }}</span>
               <div class="mod11 flex-col">
-                <span v-if="type == 2" class="txt5">{{ item.equipmentTotal }}{{ $t("orderExport.machines") }},{{
-                  $t("orderExport.superior")
-                }}:{{ item.lastUsername }}</span>
+                <span v-if="packetType == '0'" class="txt5">{{ item.equipmentTotal }}{{ $t("orderExport.machines") }}
+                <!-- ,{{$t("orderExport.superior")}}:{{ item.lastUsername }} -->
+                </span>
                 <div v-else class="txt5 l-flex-RC">
                   <div>{{ item.equipmentType }}</div>
                   <div class="lineCon o-mlr-6"></div>
@@ -92,9 +92,9 @@
       :isCloseForCancel="false" @cancelclk="cancelClk">
       <template #content>
         <div class="cust_vantBorder">
-          <van-field clearable v-model="searchForm.orderNo" :placeholder="$t('orderExport.searchPop.orderNoPlace')"
+          <van-field v-if="packetType == '1'" clearable v-model="searchForm.orderNo" :placeholder="$t('orderExport.searchPop.orderNoPlace')"
             :label="$t('orderExport.searchPop.orderNo')" />
-          <van-field clearable v-model="searchForm.busiName" :placeholder="$t('orderExport.searchPop.busiNamePlace')"
+          <van-field v-if="packetType == '0'" clearable v-model="searchForm.busiName" :placeholder="$t('orderExport.searchPop.busiNamePlace')"
             :label="$t('orderExport.searchPop.busiName')" />
           <van-field @click-input="changeTypeInpClk" readonly clearable v-model="searchForm.ifForeignName"
             :placeholder="$t('orderExport.searchPop.mainOverPlace')" :label="$t('orderExport.searchPop.mainOver')">
@@ -136,11 +136,11 @@
   </div>
 </template>
 <script>
-import { getOrderList, Api_getOrderExport } from "@/service/orderExport";
+import { getOrderAnalysis, Api_getOrderAnalysisExport } from "@/service/orderExport";
 import { onMounted, reactive, toRefs, ref } from "vue";
 import sHeader from "@/components/SimpleHeader";
 import { getLoginUser, $M_ExportFile } from "@/common/js/utils";
-import dateSelectList from "@/components/dateSelectList";
+import dateSelectList from "@/components/dateOrderList";
 import dateUtil from "@/utils/dateUtil";
 import kTabs from "@/components/commom/kTabs/index.vue";
 import { useI18n } from "vue-i18n";
@@ -224,7 +224,7 @@ export default {
         current: pageNo.value,
         size: pageSize.value,
       };
-      const { data } = await getOrderList(Object.assign(param, searchParams));
+      const { data } = await getOrderAnalysis(Object.assign(param, searchParams));
       if (data.code === "00000") {
         if (data.data.total === 0) {
           finished.value = true;
@@ -306,10 +306,10 @@ export default {
     // 点击导出按钮
     const exportOrder = async () => {
       const param = {
-        current: pageNo.value,
-        size: pageSize.value,
+        current: 1,
+        size: 50,
       };
-      const { headers, data } = await Api_getOrderExport(Object.assign(param, searchParams));
+      const { headers, data } = await Api_getOrderAnalysisExport(Object.assign(param, searchParams));
       $M_ExportFile(data, headers);
     };
     // 筛选弹窗的元素
@@ -379,23 +379,25 @@ export default {
       pageSize.value = 10;
       // ruleData.tableData = [];
       tableData.value = [];
-      if (e == 1) {
-        searchParams.type = 1;
+      if (e == 0) {
+        searchParams.packetType = "0";
       } else {
-        searchParams.type = 2;
+        searchParams.packetType = "1";
       }
       getList();
     };
     let searchParams = reactive({
-      type: "2", // 分组类型
+      type: user.type, // 商户类型
       clientId: "", // 设备编号
       username: "",
       userName: "",
       startDate: "",
       endDate: "",
       ifForeign: '0',
-      adminId: user.id,
+      adminId: user.type == 4 ? user.id : "",
       companyType: "",
+      dateType: "2",
+      packetType: "0", // 分组类型
     });
     // 搜索点击
     const searchClick = () => {
@@ -408,15 +410,31 @@ export default {
       // ruleData.tableData = [];
       tableData.value = [];
       searchParams = Object.assign(searchParams, uDate);
-      searchParams.startDate = dateUtil.formateDate(
-        new Date(searchParams.startDate),
-        "yyyy-MM-dd hh:mm:ss"
-      );
-      searchParams.endDate = dateUtil.formateDate(
-        new Date(searchParams.endDate),
-        "yyyy-MM-dd hh:mm:ss"
-      );
+      if (searchParams.dateType === "2") {
+        // 按日、周统计
+        searchParams.startDate = dateUtil.formateDate(
+          new Date(searchParams.startDate),
+          "yyyy-MM-dd hh:mm:ss"
+        );
+        searchParams.endDate = dateUtil.formateDate(
+          new Date(searchParams.endDate),
+          "yyyy-MM-dd hh:mm:ss"
+        );
+      } else if (searchParams.dateType === "0") {
+        // 按月统计
+        searchParams.startDate = dateUtil.formateDate(
+          new Date(searchParams.startDate),
+          "yyyy-MM"
+        );
+      } else if (searchParams.dateType === "1") {
+        // 按年统计
+        searchParams.startDate = dateUtil.formateDate(
+          new Date(searchParams.startDate),
+          "yyyy"
+        );
+      }
       getList();
+      console.log("searchParams的值是 >>>", searchParams);
       // onRefresh(1);
     };
 

+ 7 - 1
src/views/register.vue

@@ -111,6 +111,9 @@
             </template>
           </van-field>
         </div>
+        <van-field v-model="inviteCode" name="inviteCode" :label="$t('register.invitationCode')"
+          :placeholder="$t('register.invitationCodePlaceholder')" />
+        <br>
         <!-- 提交验证信息 -->
         <van-button round type="primary" class="register" native-type="submit">{{
           $t('register.registerButton')
@@ -148,6 +151,7 @@ export default {
     const phone = ref('');
     const email = ref('');
     const code = ref('');
+    const inviteCode = ref('');
     const verifyRef = ref(null);
     const verCodeTime = reactive({
       time: 0
@@ -174,7 +178,8 @@ export default {
         // email: email.value,
         phoneOrEmail: phone.value || email.value,
         code: code.value,
-        companyType: '0'
+        companyType: '0',
+        inviteCode: inviteCode.value
       });
 
       if (data.code === '00000') {
@@ -297,6 +302,7 @@ export default {
       phone,
       email,
       code,
+      inviteCode,
       verifyRef,
       verCodeTime,
       verCodeTimeInterval,

+ 3 - 1
src/views/user.vue

@@ -74,12 +74,14 @@
             </div>
           </div>
           <!-- 上级账号 -->
+          <!--
           <div v-if="isInland && user.type == '2'">
             <div v-if="haveRelation" class="userInfo l-flex-between">
               <span class="userInfoLeft">{{ $t("user.superiorAccount") }}: </span>
               <span>{{ relationAdminName }}</span>
             </div>
           </div>
+          -->
           <!-- 手机号码 -->
           <div v-if="user.type != '0'">
             <div v-if="!phoneNumberShow" class="userInfo l-flex-between">
@@ -201,7 +203,7 @@
             </div>
           </div>
           <!-- 设备上下线提醒 -->
-          <div v-if="isInWeChat && user.type == '2'" class="userInfo l-flex-RC">
+          <div v-if="(isInWeChat || user.ifForeign === '1') && user.type == '2'" class="userInfo l-flex-RC">
             <span class="userInfoLeft l-flex-RC">{{ $t("user.onOffNotice") }}: </span>
             <div class="cust_vantBorder">
               <van-switch :model-value="onOffNotice" active-color="#0090fa" style="font-size: 18px;"