فهرست منبع

feat:“添加用户反馈功能,优化标签为分组功能”

soobin 1 سال پیش
والد
کامیت
d6f644d37c

+ 5 - 1
src/assets/language/en.json

@@ -1341,6 +1341,7 @@
     "announcement": "Editorial notice",
     "standbyWithdrawalAccountNo": "Standby withdrawal account No",
     "changePassword": "Change Password",
+    "feedback": "Feedback",
     "selfRecharging": "Self recharging",
     "logOut": "Sign out",
     "logOutTips": "tips",
@@ -1380,11 +1381,14 @@
       "merchant": "Merchant List",
       "merchantPlace": "Please select the merchant list",
       "machine": "Machine list",
-      "machinePlace": "Please select the machine list"
+      "machinePlace": "Please select the machine list",
+      "addSuccess": "Add successfully",
+      "editSuccess": "Modified successfully"
     }
   },
   
   "kSelectPop": {
+    "searchKey": "Please enter your search term",
     "cancel": "cancel",
     "confirm": "confirm",
     "allCheck": "Select All",

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

@@ -389,7 +389,7 @@
     "affiliatedArea": "设属地区",
     "machineType": "机器类型",
     "expirationTime": "到期时间",
-    "equipmentLabel": "设备标签",
+    "equipmentLabel": "设备分组标签",
     "edit": "编辑",
     "nameOfOperatorLabel": "运营者名称",
     "nameOfOperatorPlaceholder": "请输入运营者名称",
@@ -472,10 +472,10 @@
     "orderNumber": "订单号",
     "price": "价格",
     "tagSet": {
-      "title": "编辑标签",
-      "addTag": "添加标签",
-      "tag": "标签",
-      "allTags": "所有标签",
+      "title": "编辑分组标签",
+      "addTag": "添加分组标签",
+      "tag": "分组标签",
+      "allTags": "所有分组标签",
       "sumbmit": "提交",
       "cancel": "取消",
       "confirm": "确定"
@@ -675,7 +675,7 @@
     "orderAnalysis": "订单分析",
     "shandeSubLedger": "杉德分账",
     "taskMessage": "任务消息",
-    "labelMan": "标签管理",
+    "labelMan": "分组管理",
     "apkMan": "apk管理",
     "customLogo": "定制logo",
     "logMan": "日志管理",
@@ -995,7 +995,7 @@
     "orderData": "订单数据",
     "orderRefund": "订单退款",
     "systemOffline": "系统脱机",
-    "labelMan": "标签管理",
+    "labelMan": "分组管理",
     "relationAdmin": "关联上级",
     "apkMan": "apk管理",
     "merchantMan": "商户管理",
@@ -1360,6 +1360,7 @@
     "announcement": "公告编辑",
     "standbyWithdrawalAccountNo": "备用提现账号",
     "changePassword": "修改密码",
+    "feedback": "意见反馈",
     "selfRecharging": "自充值",
     "logOut": "退出登录",
     "logOutTips": "提示",
@@ -1387,22 +1388,25 @@
     "bindSuccess": "绑定成功"
   },
   "labelMan": {
-    "title": "标签管理",
+    "title": "分组管理",
     "merchant": "商户",
     "machine": "机器",
-    "labelName": "标签名",
+    "labelName": "分组标签名",
     "add": {
-      "name": "标签名称",
-      "namePlace": "请输入标签名称",
+      "name": "分组名称",
+      "namePlace": "请输入分组名称",
       "type": "类型",
       "typePlace": "请选择类型",
       "merchant": "商家列表",
       "merchantPlace": "请选择商家列表",
       "machine": "机器列表",
-      "machinePlace": "请选择机器列表"
+      "machinePlace": "请选择机器列表",
+      "addSuccess": "添加成功",
+      "editSuccess": "修改成功"
     }
   },
   "kSelectPop": {
+    "searchKey": "请输入搜索关键词",
     "cancel": "取消",
     "confirm": "确定",
     "allCheck": "全选",
@@ -1470,7 +1474,7 @@
     "withdrawalAccountNo": "提现账号",
     "standbyWithdrawalAccountNo": "备用提现账号",
     "merchantManagement": "商户管理",
-    "labelMan": "标签管理"
+    "labelMan": "分组管理"
   },
   "wechat": {
     "headerName": "绑定微信"

BIN
src/assets/user/feedback.png


+ 32 - 3
src/components/commom/kSelectPop/index.vue

@@ -7,6 +7,8 @@
           <div class="c-text-16 c-text-b">{{ $t('kSelectPop.selectTitle') }}</div>
           <div @click="confirmClk" class="c-text-16 c-text-color">{{ $t('kSelectPop.confirm') }}</div>
         </div>
+        <van-search v-model="searchValue" placeholder="请输入搜索关键词" shape="round" background="#4d6add"
+          @update:model-value="valueChange" />
         <div style="height: 50vh;overflow:hidden;overflow-y:auto;">
           <!-- 全选 -->
           <van-checkbox-group v-model="allChecked" ref="allCheckRef">
@@ -24,7 +26,7 @@
                 <div v-for="(item, index) in props.selOptions" :key="index" class="kBordBott">
                   <van-cell :border="false" :title="item[selTitleAndName[0]]" @click="toggle(index)">
                     <template #right-icon>
-                      <van-checkbox @click.stop="toggle(index)" :name="item[selTitleAndName[1]]"
+                      <van-checkbox @click.stop="toggle(index)" :name="item[selTitleAndName[0]]"
                         :ref="(el) => (checkboxRefs[index] = el)" />
                     </template>
                   </van-cell>
@@ -63,8 +65,33 @@ export default {
   setup(props, { emit }) {
     // 点击确定按钮
     const confirmClk = () => {
-      emit("selconfirm", checked.value);
+      // console.log("选中", checked.value);
+      let checkedOptions = [];
+      props.selOptions.forEach((item) => {
+        if (checked.value.includes(item.name) || (item.username != null && checked.value.includes(item.username)) || (item.clientId != null && checked.value.includes(item.clientId))) {
+          checkedOptions.push(item.id);
+        }
+      })
+      // console.log("checkedOptions", checkedOptions);
+      emit("selconfirm", checked.value, checkedOptions);
     };
+    // 搜索关键词
+    const searchValue = ref('');
+    const tempOptions = ref([]);
+    const valueChange = () => {
+      if (tempOptions.value.length == 0) {
+        tempOptions.value = props.selOptions;
+      }
+      // console.log("关键词", searchValue.value);
+      // console.log("原始数据", tempOptions.value);
+      let newOptions = [];
+      tempOptions.value.forEach((item) => {
+        if (item.name.includes(searchValue.value) || (item.username != null && item.username.includes(searchValue.value)) || (item.clientId != null && item.clientId.includes(searchValue.value))) {
+          newOptions.push(item);
+        }
+      })
+      emit("searchData", newOptions);
+    }
     // 全选
     const allCheckRef = ref(null);
     const allChecked = ref([]);
@@ -86,7 +113,7 @@ export default {
       if (selectedOpt) {
         // 找到选中的值在所有值里面的下标,做切换状态
         if (props.selOptions.length > 0) {
-          console.log('selectedOpt',selectedOpt)
+          console.log('selectedOpt', selectedOpt)
           checked.value = selectedOpt;
         }
       } else {
@@ -131,6 +158,8 @@ export default {
       allCheckRef,
       confirmClk,
       selPopClose,
+      searchValue,
+      valueChange
     };
   },
 };

+ 2 - 1
src/main.js

@@ -4,7 +4,7 @@ import {
   AddressList, Field, CellGroup, Cell, SwipeCell, Icon, Stepper, Card, Checkbox, CheckboxGroup, Button, Swipe, SwipeItem,
   PullRefresh, List, Tab, Tabs, SubmitBar, Toast, Skeleton, RadioGroup, Radio, NoticeBar, ActionSheet, Cascader, Col, Row,
   Slider, DatePicker, Switch, Calendar, Picker, Uploader, Tag, DropdownMenu, DropdownItem, Notify, ConfigProvider, NavBar,
-  Area, Popover, Collapse, CollapseItem, PickerGroup, TimePicker, BackTop, Progress, Tabbar, TabbarItem
+  Area, Popover, Collapse, CollapseItem, PickerGroup, TimePicker, BackTop, Progress, Tabbar, TabbarItem, Search
 } from 'vant';
 import { Image as VanImage } from 'vant';
 import App from './App.vue'
@@ -90,6 +90,7 @@ app.use(ActionBarButton)
   .use(Progress)
   .use(Tabbar)
   .use(TabbarItem)
+  .use(Search)
 
 app.use(router)
 app.use(store)

+ 7 - 0
src/router/index.js

@@ -77,6 +77,13 @@ const router = createRouter({
       component: () => import("@/views/changePassword"),
       meta: { index: 1, noLogin: true },
     },
+    // 意见反馈
+    {
+      path: "/feedback",
+      name: "feedback",
+      component: () => import("@/views/feedback"),
+      meta: { index: 1 },
+    },
     // 设备页面
     {
       path: "/device",

+ 10 - 3
src/styles/home/index.less

@@ -67,12 +67,12 @@
 
     .titleBox {
       width: 100%;
-      height: 16px;
-      margin: 23px 0;
+      height: 30px;
+      margin: 12px 0;
 
       .layer2 {
         width: 100%;
-        height: 16px;
+        height: 25px;
 
         .section5 {
           width: 14px;
@@ -100,6 +100,13 @@
             display: block;
           }
         }
+
+        .surveybyantdv {
+          // width: au;
+          height: 27px;
+          margin-left: auto;
+          margin-right: 10px;
+        }
       }
     }
 

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

@@ -265,6 +265,17 @@
                         top: 0.4rem;
                     }
 
+                    &.feedbackIcon::after {
+                        content: "";
+                        position: absolute;
+                        background: #fff url("../../assets/user/feedback.png") top center no-repeat;
+                        background-size: 100%;
+                        width: 0.6rem;
+                        height: 0.6rem;
+                        right: 0.2rem;
+                        top: 0.4rem;
+                    }
+
                     &.loginOutIcon::after {
                         content: "";
                         position: absolute;

+ 4 - 2
src/views/device/deviceSet.vue

@@ -119,14 +119,14 @@
           </template>
         </van-field>
         <div class="cust_vantBorder">
-          <van-field required clearable v-model="deviceDetal.operationalName" name="operationalName"
+          <van-field required clearable v-model="deviceDetal.contactName" name="contactName"
             :label="`${$t('device.nameOfOperatorLabel')}`" :placeholder="$t('device.nameOfOperatorPlaceholder')" :rules="[
               {
                 required: true,
                 message: $t('device.nameOfOperatorPlaceholder'),
               },
             ]" />
-          <van-field required clearable v-model="deviceDetal.operationalPhone" name="operationalPhone"
+          <van-field required clearable v-model="deviceDetal.contactPhone" name="contactPhone"
             :label="`${$t('device.operatorTelephoneLabel')}`" :placeholder="$t('device.operatorTelephonePlaceholder')"
             :rules="[
               {
@@ -296,6 +296,8 @@ export default {
     const deviceDetal = ref({
       name: "",
       selfName: "",
+      contactName: "",
+      contactPhone: "",
       operationalName: "",
       operationalPhone: "",
       timeRuleId: "",

+ 39 - 0
src/views/feedback.vue

@@ -0,0 +1,39 @@
+<template>
+  <div class="feedback">
+    <s-header :name="$t('user.feedback')" :noback="false"></s-header>
+    <iframe height="100%" width="100%" :src="surveyUrl" frameborder="0" allowfullscreen></iframe>
+  </div>
+</template>
+
+<script>
+// 导入接口
+import sHeader from "@/components/SimpleHeader";
+import { onMounted, ref } from '@vue/runtime-core';
+import { getLoginUser } from '@/common/js/utils';
+export default {
+  components: {
+    sHeader
+  },
+  setup() {
+    const user = getLoginUser();
+    const surveyUrl = ref('');
+    // 刚进页面
+    onMounted(() => {
+      // 加载样式
+      // styleUrl('feedback');
+      if(user.ifForeign == '1') {
+        surveyUrl.value = 'https://form.antdv.com/r/655ab04d801e005c';
+      } else {
+        surveyUrl.value = 'https://form.antdv.com/r/6581b0e5773a00a0';
+      }
+    });
+  
+    return {
+      surveyUrl
+    };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+</style>

+ 15 - 10
src/views/home/index.vue

@@ -21,7 +21,7 @@
       <!-- 没有数据概览M14权限的人看不到数据概览和ECharts -->
       <!-- 数据概览 -->
       <div v-if="showDataDiv" class="titleBox flex-col">
-        <div class="layer2 flex-row">
+        <div class="layer2 l-flex-RC">
           <div class="section5 flex-col"></div>
           <div class="TextGroup2 flex-col">
             <span class="txt4">{{ $t("home.dataOverview") }}</span>
@@ -120,13 +120,13 @@
         <van-image src="https://fastly.jsdelivr.net/npm/@vant/assets/apple-3.jpeg" />
       </div>
     </van-dialog> -->
-    <!-- <van-dialog v-model:show="showAlarm" title="以下机器出现报警,请及时处理!">
+    <van-dialog v-model:show="showAlarm" title="以下机器出现报警,请及时处理!" @confirm="confirmAlarm">
       <div style="max-height: 30vh; overflow-y: auto;">
         <div v-for="(item, index) in alarmList" :key="index" style="padding: 2px 5px;">
-            <van-cell :title="'设备:'+ (item.name ? item.name : item.clientId.slice(-6)) + ' , 报警内容:' + item.alarmContent" />
+          <van-cell :title="'设备:' + (item.name ? item.name : item.clientId.slice(-6)) + ' , 报警内容:' + item.alarmContent" />
         </div>
       </div>
-    </van-dialog> -->
+    </van-dialog>
   </div>
 </template>
 
@@ -198,18 +198,16 @@ export default {
         }, 500);
       });
     };
+    const firstLogin = ref(true);
     const user = getLoginUser();
     const router = useRouter();
     const userName = ref(user.name);
     const sys = ref(null);
-
-    const firstLogin = ref(true);
     const pic1 = ref([
       { url: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-3.jpeg', isImage: true },
       // Uploader 根据文件后缀来判断是否为图片文件
       // 如果图片 URL 中不包含类型信息,可以添加 isImage 标记来声明
     ]);
-
     // 自定义货币符号
     const currencySymbol = ref("¥");
     if (user.currencySymbol) {
@@ -395,6 +393,7 @@ export default {
         const loginSysString = localStorage.getItem("loginSys");
         sys.value = JSON.parse(loginSysString);
       }
+      firstLogin.value = localStorage.getItem('firstLogin');
       // 设置菜单权限
       menuSet();
       // 设置菜单权限, 只执行一次
@@ -426,14 +425,19 @@ export default {
           todayDate: dateUtil.formateDate(new Date(), "yyyy-MM-dd"), // 当天时间
         };
         const { data } = await getIsAlarm(queryParams);
-        if (data.data.length > 0) {
-          showAlarm.value = true;
+        if (data.data != null) {
+          if (firstLogin.value == 'true') {
+            showAlarm.value = true;
+          }
           alarmList.value = data.data;
-          console.log("alarmList", alarmList.value)
         }
       }
     });
 
+    const confirmAlarm = () => {
+      localStorage.setItem('firstLogin', false);
+    }
+
     const menuList = [];
 
     const showDataDiv = ref(false);
@@ -629,6 +633,7 @@ export default {
       pic1,
       showAlarm,
       alarmList,
+      confirmAlarm
     };
   },
 

+ 39 - 12
src/views/labelMan/add.vue

@@ -71,11 +71,11 @@
       </div>
     </van-form>
     <!-- 商家弹窗 -->
-    <kSelectPop @selconfirm="selAdminConfirm" ref="kSelectPopAdminRef" :selOptions="selAdminOptions"
-                :selTitleAndName="['username', 'username']"></kSelectPop>
+    <kSelectPop @selconfirm="selAdminConfirm" ref="kSelectPopAdminRef" :selOptions="selAdminOptions" @searchData="searchAdminData"
+                :selTitleAndName="['username', 'id']"></kSelectPop>
     <!-- 机器弹窗 -->
-    <kSelectPop @selconfirm="selEquipConfirm" ref="kSelectPopEquipRef" :selOptions="selEquipOptions"
-                :selTitleAndName="['name', 'name']"></kSelectPop>
+    <kSelectPop @selconfirm="selEquipConfirm" ref="kSelectPopEquipRef" :selOptions="selEquipOptions" @searchData="searchEquData"
+                :selTitleAndName="['name', 'id']"></kSelectPop>
   </div>
 </template>
 
@@ -131,15 +131,27 @@ export default {
     const selAdminOptions = ref([]);
     const kSelectPopAdminRef = ref(null);
     // 商家弹窗的确定按钮
-    const selAdminConfirm = (e) => {
-      if (e.length > 0) {
+    const selAdminConfirm = (e, e1) => {
+      if (e.length > 0 && e1.length > 0) {
         cofficentForm.value.adminNames = e.join(",");
+        cofficentForm.value.adminIds = e1.join(",");
       } else {
         cofficentForm.value.adminNames = "";
       }
       kSelectPopAdminRef.value.selPopClose();
     };
 
+    const searchAdminData = (e) => {
+      // console.log("用户列表", e);
+      selAdminOptions.value = e;
+    }
+
+    const searchEquData = (e) => {
+      // console.log("设备列表", e);
+      selEquipOptions.value = e;
+    }
+    
+
     // 点击机器列表
     const busiEquipInpClk = () => {
       if (cofficentForm.value.equipmentNames) {
@@ -154,11 +166,13 @@ export default {
     const selEquipOptions = ref([]);
     const kSelectPopEquipRef = ref(null);
     // 机器弹窗的确定按钮
-    const selEquipConfirm = (e) => {
-      if (e.length > 0) {
+    const selEquipConfirm = (e, e1) => {
+      if (e.length > 0 && e1.length > 0) {
         cofficentForm.value.equipmentNames = e.join(",");
+        cofficentForm.value.equipmentIds = e1.join(",");
       } else {
         cofficentForm.value.equipmentNames = "";
+        cofficentForm.value.equipmentIds = "";
       }
       kSelectPopEquipRef.value.selPopClose();
     };
@@ -194,10 +208,12 @@ export default {
     // 表单参数
     let cofficentForm = ref({
       adminId: user.id,
+      adminIds: "",
       name: "",
       type: "0",
       adminNames: "",
       equipmentNames: "",
+      equipmentIds: "",
     });
     // 路由
     const router = useRouter();
@@ -207,10 +223,14 @@ export default {
       // 判断type清空商家或机器参数
       if (param.type === "0") {
         param.equipmentNames = "";
+        param.equipmentIds = "";
         param.adminNames = JSON.stringify(param.adminNames.split(","));
+        param.adminIds = JSON.stringify(param.adminIds.split(","));
       } else {
         param.adminNames = "";
+        param.adminIds = "";
         param.equipmentNames = JSON.stringify(param.equipmentNames.split(","));
+        param.equipmentIds = JSON.stringify(param.equipmentIds.split(","));
       }
       // 如果是编辑
       let res;
@@ -220,11 +240,16 @@ export default {
         res = await Api_postAddLabel(param);
       }
       const { data } = res;
-      if (data.code === "00000") {
-        showToast(data.message);
+      if (data.code === "00000" && cofficentForm.value.id) {
+        showToast(t('labelMan.add.editSuccess'));
+        setTimeout(() => {
+          router.go(-1);
+        }, 1000);
+      } else if (data.code === "00000" && !cofficentForm.value.id) {
+        showToast(t('labelMan.add.addSuccess'));
         setTimeout(() => {
-          router.back();
-        }, 500);
+          router.go(-1);
+        }, 1000);
       }
     };
     return {
@@ -239,6 +264,8 @@ export default {
       selEquipConfirm,
       busiAdminInpClk,
       busiEquipInpClk,
+      searchAdminData,
+      searchEquData,
     };
   },
 };

+ 1 - 0
src/views/login.vue

@@ -150,6 +150,7 @@ export default {
           }
         }
         showSuccessToast(t('login.loginSucess'));
+        localStorage.setItem('firstLogin', true);
 
         // 需要刷新页面,否则 axios.js 文件里的 token 不会被重置
         window.location.href = '/shenze/';

+ 1 - 0
src/views/merchantManage/index.vue

@@ -137,6 +137,7 @@ export default {
             setLocal("loginUser", JSON.stringify(res.data.data));
             // console.log('loginUser JSON:', JSON.stringify(data.data));
             showSuccessToast(t('login.loginSucess'));
+            localStorage.setItem('firstLogin', true);
             // 需要刷新页面,否则 axios.js 文件里的 token 不会被重置
             window.location.href = '/shenze/';
             setTimeout(() => {

+ 11 - 2
src/views/user.vue

@@ -24,7 +24,7 @@
           </div>
         </div>
         <div class="userInfoBox">
-          
+
           <!-- 地区 -->
           <div v-if="isInland && user.type != '0'">
             <div v-if="!areaShow" class="userInfo l-flex-between">
@@ -100,7 +100,7 @@
               <van-icon name="edit" class="editIcon" @click="editClk(3)" />
             </div>
           </div>
-          
+
           <!-- 关联上级 -->
           <!-- <div v-if="isInland && user.type == '2'">
             <div v-if="!relationType" class="userInfo l-flex-between">
@@ -264,6 +264,15 @@
             </div>
           </div>
 
+          <!-- 意见反馈 -->
+          <!-- <div v-if="user.type > 1" class="taskListRow flex-col" @click="pushPageList('/feedback')"> -->
+          <div v-if="user.id == 31 || user.id == 34" class="taskListRow flex-col" @click="pushPageList('/feedback')">
+            <div class="taskIcon feedbackIcon"></div>
+            <div class="taskRight">
+              <div class="taskTitle">{{ $t("user.feedback") }}</div>
+            </div>
+          </div>
+
           <!-- 自动充值 -->
           <!-- <div v-if="isInland" class="taskListRow flex-col" @click="operUnipay()">
             <div class="taskIcon selfPayIcon"></div>