瀏覽代碼

feat:"增加税收功能"

soobin 3 月之前
父節點
當前提交
664ea091b5

二進制
src/assets/device/operIcon/tax.png


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

@@ -1732,6 +1732,15 @@
     "M21": "MQTT",
     "M22": "Terminals"
   },
+  "tax": {
+    "taxFee": "Tax Levy",
+    "taxFeeDesc": "Taxes will be automatically collected per transaction when enabled",
+    "taxRate": "Tax Rate",
+    "ratePlaceholder": "Enter tax rate percentage (0.00-100.00)",
+    "rateRequired": "Tax rate is required",
+    "rateInvalid": "Enter valid value with up to 2 decimal places",
+    "update": "Apply Now"
+  },
   "remote": {
     "C1": "Restart Machine",
     "C2": "Sleep Toggle",
@@ -1760,6 +1769,7 @@
     "C25": "Mixing Mode (Ice Cream)",
     "C26": "Freshness Mode (Ice Cream)",
     "C27": "Defrost Mode (Ice Cream)",
-    "C28": "Edit Payment Method"
+    "C28": "Edit Payment Method",
+    "C29": "Tax Administration"
   }
 }

+ 11 - 1
src/assets/language/es.json

@@ -1731,6 +1731,15 @@
         "M21": "MQTT",
         "M22": "Terminales"
     },
+    "tax": {
+        "taxFee": "Recaudación Tributaria",
+        "taxFeeDesc": "Recaudación automática por transacción",
+        "taxRate": "Tipo Impositivo",
+        "ratePlaceholder": "Introduzca porcentaje (0,00-100,00)",
+        "rateRequired": "Campo obligatorio",
+        "rateInvalid": "Valor entre 0-100 con 2 decimales",
+        "update": "Actualizar ahora"
+    },
     "remote": {
         "C1": "Reiniciar máquina",
         "C2": "Alternar suspensión",
@@ -1759,6 +1768,7 @@
         "C25": "Mezcla (Helado)",
         "C26": "Conservación (Helado)",
         "C27": "Descongelación (Helado)",
-        "C28": "Cambiar pago"
+        "C28": "Cambiar pago",
+        "C29": "Gestión Tributaria"
     }
 }

+ 11 - 1
src/assets/language/fr.json

@@ -1762,6 +1762,15 @@
         "M21": "MQTT",
         "M22": "Terminaux"
     },
+    "tax": {
+        "taxFee": "Prélèvement Fiscal",
+        "taxFeeDesc": "Collecte automatique des taxes par transaction",
+        "taxRate": "Taux d'Imposition",
+        "ratePlaceholder": "Saisir le taux en % (00,00 à 100,00)",
+        "rateRequired": "Le taux est obligatoire",
+        "rateInvalid": "Valeur entre 0 et 100 (2 décimales max)",
+        "update": "Mettre à jour"
+    },
     "remote": {
         "C1": "Redémarrer machine",
         "C2": "Mode veille",
@@ -1790,6 +1799,7 @@
         "C25": "Mélange (Glace)",
         "C26": "Conservation (Glace)",
         "C27": "Décongélation (Glace)",
-        "C28": "Modifier paiement"
+        "C28": "Modifier paiement",
+        "C29": "Gestion Fiscale"
     }
 }

+ 11 - 1
src/assets/language/ja.json

@@ -1723,6 +1723,15 @@
         "M21": "MQTT",
         "M22": "端末"
     },
+    "tax": {
+        "taxFee": "課税徴収",
+        "taxFeeDesc": "有効時、取引ごとに自動課税されます",
+        "taxRate": "税率",
+        "ratePlaceholder": "税率パーセンテージを入力(0.00~100.00)",
+        "rateRequired": "税率の入力必須",
+        "rateInvalid": "0-100の数値を入力(小数点第2位まで)",
+        "update": "即時適用"
+    },
     "remote": {
         "C1": "マシン再起動",
         "C2": "スリープ切替",
@@ -1751,6 +1760,7 @@
         "C25": "攪拌モード(アイスクリーム)",
         "C26": "鮮度保持(アイスクリーム)",
         "C27": "解凍モード(アイスクリーム)",
-        "C28": "支払い方法変更"
+        "C28": "支払い方法変更",
+        "C29": "税務管理"
     }
 }

+ 11 - 1
src/assets/language/pt.json

@@ -1731,6 +1731,15 @@
         "M21": "MQTT",
         "M22": "Terminais"
     },
+    "tax": {
+        "taxFee": "Cobrança Fiscal",
+        "taxFeeDesc": "Coleta automática de impostos por transação",
+        "taxRate": "Alíquota",
+        "ratePlaceholder": "Insira a porcentagem (0,00-100,00)",
+        "rateRequired": "Campo obrigatório",
+        "rateInvalid": "Valor entre 0-100 com 2 decimais",
+        "update": "Atualizar agora"
+    },
     "remote": {
         "C1": "Reiniciar máquina",
         "C2": "Alternar sono",
@@ -1759,6 +1768,7 @@
         "C25": "Mistura (Sorvete)",
         "C26": "Conservação (Sorvete)",
         "C27": "Descongelamento (Sorvete)",
-        "C28": "Alterar pagamento"
+        "C28": "Alterar pagamento",
+        "C29": "Gestão Tributária"
     }
 }

+ 11 - 1
src/assets/language/ru.json

@@ -1762,6 +1762,15 @@
         "M21": "MQTT",
         "M22": "Терминалы"
     },
+    "tax": {
+        "taxFee": "Налоговое взимание",
+        "taxFeeDesc": "Автоматическое удержание налогов при активации",
+        "taxRate": "Налоговая ставка",
+        "ratePlaceholder": "Введите процентную ставку (0,00-100,00)",
+        "rateRequired": "Укажите налоговую ставку",
+        "rateInvalid": "Допустимое значение: 0-100 с 2 знаками после запятой",
+        "update": "Применить сейчас"
+    },
     "remote": {
         "C1": "Перезагрузка машины",
         "C2": "Переключение сна",
@@ -1790,6 +1799,7 @@
         "C25": "Перемешивание (Мороженое)",
         "C26": "Сохранение свежести (Мороженое)",
         "C27": "Разморозка (Мороженое)",
-        "C28": "Изменить оплату"
+        "C28": "Изменить оплату",
+        "C29": "Управление налогообложением"
     }
 }

+ 11 - 1
src/assets/language/uk.json

@@ -1732,6 +1732,15 @@
         "M21": "MQTT",
         "M22": "Керування терміналами"
     },
+    "tax": {
+        "taxFee": "Податкове стягнення",
+        "taxFeeDesc": "Автоматичне утримання податків",
+        "taxRate": "Податкова ставка",
+        "ratePlaceholder": "Введіть відсоток (0,00-100,00)",
+        "rateRequired": "Обов'язкове поле",
+        "rateInvalid": "Допустиме значення: 0-100 з 2 десятковими",
+        "update": "Застосувати"
+    },
     "remote": {
         "C1": "Перезавантажити пристрій",
         "C2": "Режим сну",
@@ -1760,6 +1769,7 @@
         "C25": "Змішування (морозиво)",
         "C26": "Збереження свіжості (морозиво)",
         "C27": "Розморозка (морозиво)",
-        "C28": "Змінити оплату"
+        "C28": "Змінити оплату",
+        "C29": "Управління оподаткуванням"
     }
 }

+ 11 - 1
src/assets/language/zh.json

@@ -1732,6 +1732,15 @@
     "M21": "MQTT",
     "M22": "终端管理"
   },
+  "tax": {
+    "taxFee": "税费征收",
+    "taxFeeDesc": "启用后将对每笔交易收取税费",
+    "taxRate": "税率",
+    "ratePlaceholder": "请输入税率百分比",
+    "rateRequired": "税率不能为空",
+    "rateInvalid": "请输入0-100之间的有效数值(最多两位小数)",
+    "update": "立即更新"
+  },
   "remote": {
     "C1": "重启设备",
     "C2": "睡眠开关",
@@ -1760,6 +1769,7 @@
     "C25": "搅拌模式(冰淇淋)",
     "C26": "保鲜模式(冰淇淋)",
     "C27": "解冻模式(冰淇淋)",
-    "C28": "修改支付方式"
+    "C28": "修改支付方式",
+    "C29": "税收管理"
   }
 }

+ 1 - 0
src/common/js/utils.js

@@ -58,6 +58,7 @@ export const remoteMenus = {
   "C26": t("remote.C26"),
   "C27": t("remote.C27"),
   "C28": t("remote.C28"),
+  "C29": t("remote.C29")
 }
 // 判断值是否是数字 true:数值型的,false:非数值型
 export const $M_IsNaN = (num) => {

+ 2 - 14
src/components/dateSelectList/index.vue

@@ -236,8 +236,7 @@ export default {
   .time-tabs {
     display: flex;
     background: #fff;
-    border-radius: 4px;
-    border: 1px solid var(--border-color);
+    border-radius: 8px;
     box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
     overflow: hidden;
 
@@ -253,8 +252,7 @@ export default {
       white-space: nowrap;
       cursor: pointer;
       transition: all 0.2s ease;
-      border-right: 1px solid var(--border-color);
-
+    
       &:last-child {
         border-right: none;
       }
@@ -268,16 +266,6 @@ export default {
         color: #fff;
         font-weight: 500;
 
-        &::after {
-          content: "";
-          position: absolute;
-          bottom: -5px;
-          left: 50%;
-          transform: translateX(-50%);
-          border-left: 6px solid transparent;
-          border-right: 6px solid transparent;
-          border-top: 6px solid var(--bg-active);
-        }
 
         .dropdown-indicator {
           border-top-color: #fff;

+ 7 - 1
src/router/index.js

@@ -477,7 +477,6 @@ const router = createRouter({
       component: () => import("@/views/device/modifyPrice/index"),
       meta: { index: 1 },
     },
-
     // 修改支付方式
     {
       path: "/payment",
@@ -485,6 +484,13 @@ const router = createRouter({
       component: () => import("@/views/device/payment/index"),
       meta: { index: 1 },
     },
+    // 税收管理
+    {
+      path: "/tax",
+      name: "tax",
+      component: () => import("@/views/device/tax/index"),
+      meta: { index: 1 },
+    },
     // 屏蔽/展示商品
     {
       path: "/showGoods",

+ 16 - 1
src/service/device/index.js

@@ -72,6 +72,11 @@ export function doSugar(params) {
     return axios.post(`/SZWL-SERVER/tSugarDo/doSugar`, params);
 }
 
+// 远程制作
+export function remoteProduction(params) {
+    return axios.post(`/SZWL-SERVER/tSugarDo/remoteProduction`, params);
+}
+
 // 查询做糖状态
 export function selectSugarStatus(params) {
     return axios.get(`/SZWL-SERVER/tSugarDo/selectSugarStatus?${stringToUrl(params)}`);
@@ -244,7 +249,7 @@ export function updateAlarmCleanStatus(params) {
 
 // 日志上传
 export function uploadLog(params) {
-    return axios.get(`/SZWL-SERVER/tEquipment/uploadLog?${stringToUrl(params)}`);
+    return axios.get(`/SZWL-SERVER/tEquipment/newUploadLog?${stringToUrl(params)}`);
 }
 
 // 查询日志
@@ -285,4 +290,14 @@ export function getReturnCoinList(params) {
 // 提交远程退币申请
 export function applyReturnCoin(params) {
     return axios.post(`/SZWL-SERVER/returnCoinRecord/apply`, params)
+}
+
+// 修改税费开关状态
+export function updateTaxStatus(params) {
+    return axios.post(`/SZWL-SERVER/tEquipment/updateTaxSwitch`, params)
+}
+
+// 修改税率
+export function updateTaxRate(params) {
+    return axios.post(`/SZWL-SERVER/tEquipment/updateTaxRate`, params)
 }

+ 25 - 0
src/service/purse/index.js

@@ -35,3 +35,28 @@ export function withdrawStatus(params) {
 export function idCardRecognition(params) {
   return axios.post(`/SZWL-SERVER/purse/ocrImage`, params);
 }
+
+// 获取商户信息
+export function getJoinPayMch(params) {
+  return axios.post(`/SZWL-SERVER/tJoinpayMch/getOne?${stringToUrl(params)}`, params);
+}
+
+// 汇聚入网
+export function createMch(params) {
+  return axios.post(`/SZWL-SERVER/tJoinpayMch/createMch`, params);
+}
+
+// 上传图片审核
+export function uploadPhotos(params) {
+  return axios.post(`/SZWL-SERVER/tJoinpayMch/uploadPhotos`, params);
+}
+
+// 更新图片审核情况
+export function updateApproveStatus(params) {
+  return axios.post(`/SZWL-SERVER/tJoinpayMch/updateApproveStatus`, params);
+}
+
+// 签约
+export function altMchSign(params) {
+  return axios.get(`/SZWL-SERVER/tJoinpayMch/altMchSign?${stringToUrl(params)}`);
+}

+ 0 - 43
src/styles/orderCenter/index.less

@@ -61,11 +61,6 @@
     .salesData {
       background: url("../../assets/home/outer.png") top center no-repeat;
       background-size: 100% 100%;
-      // width: 100%;
-      // margin-top: 20px;
-
-      // .block5 {
-      //   width: 100%;
       padding: 10px 20px;
 
       .salesDataBox {
@@ -73,16 +68,10 @@
 
         .dataGroup {
           width: 33.3%;
-          // height: 58px;
 
           .dataGroupBox {
-            // width: 100%;
-            // height: 30px;
 
             .currencySymbol {
-              // width: auto;
-              // height: 20px;
-              // padding-right: 0.5rem;
               overflow-wrap: break-word;
               color: rgba(223, 94, 76, 1);
               font-size: 16px;
@@ -95,8 +84,6 @@
             }
 
             .dataNum {
-              // width: 49px;
-              // height: 30px;
               overflow-wrap: break-word;
               color: rgba(223, 94, 76, 1);
               font-size: 22px;
@@ -104,15 +91,11 @@
               text-align: center;
               white-space: nowrap;
               line-height: 22px;
-              // margin-left: -20px;
-              // margin-top: 5px;
               display: block;
             }
           }
 
           .dataText {
-            // width: 100%;
-            // height: 17px;
             overflow-wrap: break-word;
             color: rgba(135, 135, 166, 1);
             font-size: 13px;
@@ -130,34 +113,23 @@
       height: 50px;
       margin-top: 10px;
       margin-bottom: 15px;
-      // width: 100%;
       background: rgba(255, 255, 255, 1) url("../../assets/home/line.png") bottom center no-repeat;
       background-color: #fff;
       box-shadow: 0 2px 2px 2px rgba(0, 0, 0, 0.1);
 
 
       .main9 {
-        // width: 100%;
         padding: 2.5px 20px;
-        // height: 30px;
         display: flex;
         align-items: center;
 
 
         .wrap1 {
           width: 100%;
-          // height: 30px;
-          // padding: 0 12px 5px;
-          // align-items: center;
 
           .ImageText7 {
-            // height: 18px;
-            // margin-top: 1px;
-            // width: 79px;
 
             .mod5 {
-              // width: 30px;
-              // height: 18px;
 
               .block3 {
                 width: 15px;
@@ -168,13 +140,7 @@
               }
 
               .TextGroup7 {
-                // height: 14px;
-                // margin-top: 3px;
-                // width: 56px;
-
                 .info3 {
-                  // width: 56px;
-                  // height: 14px;
                   overflow-wrap: break-word;
                   color: rgba(64, 77, 116, 1);
                   font-size: 14px;
@@ -212,18 +178,10 @@
 
     .orderList {      
       background-color: rgba(255, 255, 255, 1);
-      // margin-top: 1px;
       width: 100%;
       position: relative;
 
-      // .group8 {
-
-      //   .section4 {
-      //     width: 100%;
-      // margin-top: 13px;
-
       .section5 {
-        // width: 100%;
         height: 160px;
         position: relative;
         padding: 5px 10px;
@@ -231,7 +189,6 @@
 
         .van-card {
           height: 150px;
-          // background-color: #ffff;
           display: flex;
           align-items: center;
           border-radius: 20px;

文件差異過大導致無法顯示
+ 1338 - 165
src/views/bindBankCard/index copy.vue


文件差異過大導致無法顯示
+ 936 - 250
src/views/bindBankCard/index.vue


+ 22 - 3
src/views/device/deviceOper.vue

@@ -198,7 +198,7 @@
         </div>
       </div>
       <!-- 支付方式 -->
-      <div v-if="controlList.includes('C28') && device.machineType == '0' && user.ifForeign == '1'" class="operation-item" @click="paymentClk()">
+      <div v-if="controlList.includes('C28') && device.machineType == '0' && (user.ifForeign == '1' || user.type == 0)" class="operation-item" @click="paymentClk()">
         <div class="icon-wrapper">
           <img class="operation-icon" src="../../assets/device/operIcon/payment.png" alt="payment" />
         </div>
@@ -220,7 +220,7 @@
         </div>
       </div>
       <!-- 屏蔽/展示商品 -->
-      <div v-if="device.equimentType != 'SI320' && controlList.includes('C13')" class="operation-item"
+      <div v-if="controlList.includes('C13')" class="operation-item"
         @click="showGoodsClk()">
         <div class="icon-wrapper">
           <img class="operation-icon" src="../../assets/device/operIcon/showGoods.png" alt="showGoods" />
@@ -306,7 +306,7 @@
       </div>
       <!-- 远程退币 -->
       <div class="operation-item"
-        v-if="(device.machineType === '0' || device.machineType === null) && (user.ifForeign == 1 || user.type == 0) && controlList.includes('C18')"
+        v-if="(device.machineType === '0' || device.machineType === null) && (user.ifForeign == '1' || user.type == 0) && controlList.includes('C18')"
         @click="returnCoinClk()">
         <div class="icon-wrapper">
           <img class="operation-icon" src="../../assets/device/operIcon/coin.png" alt="coin" />
@@ -315,6 +315,15 @@
           <span class="operation-text">{{ $t("device.returnCoin") }}</span>
         </div>
       </div>
+      <!-- 税费管理 -->
+      <div v-if="user.type == 0" class="operation-item" @click="taxClk()">
+        <div class="icon-wrapper">
+          <img class="operation-icon" src="../../assets/device/operIcon/tax.png" alt="tax" />
+        </div>
+        <div class="text-wrapper">
+          <span class="operation-text">{{ $t("remote.C29") }}</span>
+        </div>
+      </div>
     </div>
   </van-dialog>
   <van-dialog close-on-click-overlay v-model:show="operCheckShow" :title="$t('device.operationConfirmation')"
@@ -400,6 +409,14 @@ export default {
         query: { deviceId: device.value.id, name: device.value.name, clientId: device.value.clientId },
       });
     };
+
+    // 点击税收管理
+    const taxClk = () => {
+      router.push({
+        path: "tax",
+        query: { deviceId: device.value.id, name: device.value.name, clientId: device.value.clientId},
+      });
+    };
     const { t } = useI18n();
     const user = getLoginUser();
     const router = useRouter();
@@ -764,6 +781,8 @@ export default {
       restartAndroid,
       alramCleanClk,
       returnCoinClk,
+      taxClk,
+
       changePasswordClk,
       sleepIcon,
       materialIcon,

+ 14 - 6
src/views/device/doSugar.vue

@@ -16,7 +16,7 @@
             <template #option="{ option }">
               <div class="cascader-item">
                 <van-image :src="option.imgUrl" width="55px" height="55px"></van-image>
-                <div class="cascader-label">{{ option.value }}</div>
+                <div class="cascader-label">{{ option.text }}</div>
               </div>
             </template>
           </van-cascader>
@@ -38,7 +38,7 @@
             <template #option="{ option }">
               <div class="cascader-item">
                 <van-image :src="option.imgUrl" width="55px" height="55px"></van-image>
-                <div class="cascader-label">{{ option.value }}</div>
+                <div class="cascader-label">{{ option.text }}</div>
               </div>
             </template>
           </van-cascader>
@@ -94,7 +94,7 @@ import sHeader from "@/components/SimpleHeader";
 import { useRoute, useRouter } from 'vue-router';
 import {
   getDeviceDetal, selectProducts,
-  doSugar,
+  remoteProduction,
   selectSugarStatus
 } from '@/service/device'
 import { showConfirmDialog, showFailToast, showSuccessToast, showToast } from 'vant';
@@ -114,6 +114,7 @@ export default {
     const cascaderValue = ref('');
     const options = ref([]);
     const activeNames = ref(['1', '2']);
+    const productNo = ref('');
 
     // 果酱数据
     const iceName = ref();
@@ -131,7 +132,8 @@ export default {
     const onFinish = ({ selectedOptions }) => {
       console.log('onFinish', selectedOptions);
       show.value = false;
-      fieldValue.value = selectedOptions[0].value
+      fieldValue.value = selectedOptions[0].text;
+      productNo.value = selectedOptions[0].value;
     }
     const doSugartData = ref(null);
     const doSugartType = ref(true);
@@ -162,10 +164,11 @@ export default {
           options.value = data.data.map(item => {
             return {
               text: item.productName,
-              value: item.productName,
+              value: item.no,
               imgUrl: showSugarPhoto(item.no),
             };
           })
+          console.log(options.value);
         } else {
           data.data.forEach(item => {
             if (item.no.includes('J01')) {
@@ -258,7 +261,12 @@ export default {
         title: t('user.tips'),
         message: t('device.confirmMake')+ fieldValue.value +'?',
       }).then(async() => {
-        const { data } = await doSugar({ equipmentId: deviceId, productName: fieldValue.value, makeCodes: makeCodes.value.join(',') });
+        const { data } = await remoteProduction({ 
+          equipmentId: deviceId, 
+          productName: fieldValue.value, 
+          makeCodes: makeCodes.value.join(','),
+          productNo: productNo.value
+         });
         if (data.code == "00000") {
           doSugartData.value = data.data;
           setTimeout(() => {

+ 6 - 2
src/views/device/index.vue

@@ -820,7 +820,7 @@ export default {
 
   .data-overview {
     background: white;
-    margin: 16px;
+    margin: 10px;
     border-radius: 8px;
     box-shadow: var(--card-shadow);
 
@@ -881,11 +881,15 @@ export default {
   }
 
   .device-list-section {
-    margin: 16px;
+    margin: 10px;
     background: white;
     border-radius: 8px;
     box-shadow: var(--card-shadow);
 
+    :deep(.van-tabs__wrap) {
+      border-radius: 8px;
+    }
+
     .device-tabs {
       :deep(.van-tab) {
         font-size: 14px;

+ 252 - 91
src/views/device/showGoods/index.vue

@@ -1,70 +1,84 @@
 <template>
-  <div class="showGoodsIdx">
-    <s-header :name="$t('device.showGoodsPage.title')" :noback="false"></s-header>
-    <div class="headerCon kBordBott o-plr-10 o-ptb-16 l-flex-RC">
-      <div class="line o-mr-6"></div>
-      <div>
-        <span class="c-color c-text-14">{{ $t('device.showGoodsPage.equipmentName') }}:</span>
-        <span class="c-text-color c-text-14">{{ equipmentName }}</span>
+  <div class="goods-management">
+    <!-- 头部优化 -->
+    <s-header
+      :name="$t('device.showGoodsPage.title')"
+      :noback="false"
+      class="management-header"
+    />
+    <div class="content-container">
+      <!-- 设备信息卡片 -->
+      <div class="device-card">
+        <div class="device-info">
+          <h3 class="device-name">
+            <div class="vertical-indicator"></div>
+            {{ $t("device.showGoodsPage.equipmentName") }}:
+            <span class="highlight">{{ equipmentName }}</span>
+          </h3>
+          <div class="goods-count">
+            {{ $t("device.modifyPricePage.total") }}
+            <van-tag type="primary">{{ tableData.length }}</van-tag>
+            {{ $t("device.modifyPricePage.goods") }}
+          </div>
+        </div>
       </div>
-    </div>
-  </div>
-  <div class="l-flex-between o-m-10">
-    <div>{{ $t('device.modifyPricePage.total') }}{{ tableData.length }}{{ $t('device.modifyPricePage.goods') }}
-    </div>
-  </div>
-  <van-row class="goods o-mlr-8" wrap="true">
-    <van-col v-for="(item, index) in tableData" :key="index" class="goodsCon o-mr-14" span="11">
-      <div class="l-flex-RC">
-        <van-image width="60" height="60" fit="contain" :src="showSugerPhoto(item)" />
-        <span class="o-ml-10" style="word-wrap: break-word; width: 50px;">{{ item.productName }}</span>
-        <van-switch class="o-ml-1" v-model="switchStates[index]" size="20px" active-color="#4dc294"
-          @change="handleChange(index)" />
+
+      <!-- 商品网格布局 -->
+      <div class="goods-grid">
+        <div v-for="(item, index) in tableData" :key="index" class="goods-card">
+          <div class="card-header" v-if="item.no != 'I01'">
+            <van-image
+              class="goods-thumb"
+              fit="cover"
+              :src="showSugerPhoto(item)"
+            />
+            <div class="goods-info">
+              <h4 class="goods-title van-ellipsis">{{ item.productName }}</h4>
+              <van-switch
+                v-model="switchStates[index]"
+                size="24px"
+                active-color="var(--primary-color)"
+                inactive-color="#dcdee0"
+                @change="handleChange(index)"
+              />
+            </div>
+          </div>
+          <van-divider class="card-divider" />
+        </div>
       </div>
-      <van-divider :style="{ color: '#1baeae', borderColor: '#1baeae', fontSize: '20px', fontWeight: 500 }" />
-    </van-col>
-  </van-row>
-  <div class="l-flex-center" style="margin: 16px">
-    <van-button round block class="Btn" type="primary" native-type="submit" @click="updateShowGoods()">
-      {{ $t('device.submitAndPushDeviceUpdates') }}
-    </van-button>
+      <!-- 粘性底栏提交按钮 -->
+      <div class="action-footer">
+        <van-button
+          round
+          block
+          type="primary"
+          class="submit-btn"
+          @click="updateShowGoods"
+        >
+          <van-icon name="passed" class="btn-icon" />
+          {{ $t("device.submitAndPushDeviceUpdates") }}
+        </van-button>
+      </div>
+    </div>
   </div>
 </template>
 
 <script>
 // 导入接口
-import {
-  selectProducts,
-  updateProductsShow
-} from '@/service/device/index';
+import { selectProducts, updateProductsShow } from "@/service/device/index";
 import sHeader from "@/components/SimpleHeader";
-import {
-  ref,
-  reactive
-} from "@vue/reactivity";
-import {
-  onMounted
-} from '@vue/runtime-core';
-import {
-  useRoute,
-  useRouter
-} from 'vue-router';
-import {
-  showFailToast, showDialog, showConfirmDialog
-} from 'vant';
-import {
-  useI18n
-} from "vue-i18n";
-import { styleUrl } from '../../../common/js/utils';
+import { ref, reactive } from "@vue/reactivity";
+import { onMounted } from "@vue/runtime-core";
+import { useRoute, useRouter } from "vue-router";
+import { showFailToast, showDialog, showConfirmDialog } from "vant";
+import { useI18n } from "vue-i18n";
 export default {
   components: {
-    sHeader
+    sHeader,
   },
   setup() {
     // 引入语言
-    const {
-      t
-    } = useI18n();
+    const { t } = useI18n();
     // 商品图片路径处理
     const showSugerPhoto = (row) => {
       let imgId = row.no;
@@ -80,12 +94,10 @@ export default {
     const tableData = ref([]);
     const switchStates = ref([]);
     // 设备名称
-    const equipmentName = ref('');
+    const equipmentName = ref("");
 
     // 刚进页面
     onMounted(() => {
-      // 加载样式
-      styleUrl('showGoods');
       const id = route.query.deviceId || "";
       const name = route.query.name || "";
       if (id) {
@@ -93,77 +105,72 @@ export default {
         equipmentName.value = name;
         getList();
       }
-
     });
     // 获取商品列表
     const getList = () => {
       selectProducts({
-        equipmentId: cofficentForm.equipmentId
-      }).then(res => {
-        console.log('res', res)
-        const {
-          data
-        } = res.data;
+        equipmentId: cofficentForm.equipmentId,
+      }).then((res) => {
+        console.log("res", res);
+        const { data } = res.data;
         if (data) {
           if (data.length > 0) {
             // 是否修改状态
-            data.forEach(item => {
+            data.forEach((item) => {
               if (item.showType == null || item.showType == 0) {
                 switchStates.value.push(true);
               } else {
                 switchStates.value.push(false);
               }
-            })
+            });
           }
           tableData.value = data;
         }
-      })
-    }
+      });
+    };
     // 修改的价格
     const cofficentForm = reactive({
-      equipmentId: '',
+      equipmentId: "",
     });
 
     // 更改状态
     const handleChange = (index) => {
-      // console.log(index);
-      // console.log(switchStates.value[index]);
       if (switchStates.value[index]) {
         tableData.value[index].showType = 0;
       } else {
         tableData.value[index].showType = 1;
       }
-      // console.log(tableData.value[index]);
     };
 
-
     // 点击提交
     const updateShowGoods = () => {
       showConfirmDialog({
-        title: t('device.openRemind'),
-        message: t('device.editCheck'),
+        title: t("device.openRemind"),
+        message: t("device.editCheck"),
       }).then(() => {
         console.log(tableData.value);
         var products = tableData.value;
         var list = JSON.stringify(products);
         updateProductsShow({
-          productList: list
-        }).then((res) => {
-          console.log(res.data.message);
-          // Toast();
-          showDialog({
-            message: t('device.modificationSucceeded'),
-          }).then(() => {
-            //返回上一页
-            router.go(-1);
+          productList: list,
+        })
+          .then((res) => {
+            console.log(res.data.message);
+            // Toast();
+            showDialog({
+              message: t("device.modificationSucceeded"),
+            }).then(() => {
+              //返回上一页
+              router.go(-1);
+            });
+            setTimeout(() => {
+              getList();
+            }, 1000);
+          })
+          .catch((error) => {
+            console.log(error);
+            showFailToast(t("device.unknownError"));
           });
-          setTimeout(() => {
-            getList();
-          }, 1000);
-        }).catch((error) => {
-          console.log(error);
-          showFailToast(t('device.unknownError'));
-        });
       });
     };
     return {
@@ -179,4 +186,158 @@ export default {
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.goods-management {
+  --primary-color: #4dc294;
+  --card-radius: 12px;
+  --card-padding: 16px;
+  --grid-gap: 12px;
+
+  background: #f7f8fa;
+
+  .content-container {
+    background: #f7f8fa;
+  }
+}
+
+/* 设备信息卡片 */
+.device-card {
+  display: flex;
+  align-items: center;
+  padding: 12px 15px;
+  background: #fff;
+  margin: 15px;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.device-info {
+  flex: 1;
+}
+
+.device-name {
+  display: flex;
+  align-items: center;
+  font-size: 16px;
+  margin: 0 0 8px;
+  color: #404d74;
+
+  .vertical-indicator {
+    width: 4px;
+    height: 15px;
+    background: var(--active-color, #4d6add);
+    border-radius: 2px;
+    margin-right: 10px;
+  }
+}
+
+.highlight {
+  font-size: 15px;
+  color: #404d74;
+  font-weight: 500;
+  // 长名称处理
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  max-width: 70vw;
+}
+
+.goods-count {
+  font-size: 14px;
+  color: #888;
+}
+
+/* 商品网格布局 */
+.goods-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+  gap: var(--grid-gap);
+  padding: 0 16px;
+  margin-bottom: 20px;
+}
+
+.goods-card {
+  background: #fff;
+  border-radius: var(--card-radius);
+  overflow: hidden;
+  transition: transform 0.2s;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.goods-card:hover {
+  transform: translateY(-2px);
+}
+
+.card-header {
+  display: flex;
+  padding: var(--card-padding);
+}
+
+.goods-thumb {
+  width: 80px;
+  height: 80px;
+  border-radius: 8px;
+  margin-right: 12px;
+}
+
+.goods-info {
+  flex: 1;
+  min-width: 0;
+}
+
+.goods-title {
+  font-size: 15px;
+  margin: 0 0 12px;
+  line-height: 1.4;
+}
+
+.card-divider {
+  margin: 0;
+  border-color: rgba(0, 0, 0, 0.05);
+}
+
+/* 提交按钮 */
+.action-footer {
+  position: sticky;
+  bottom: 0;
+  padding: 16px;
+  background: linear-gradient(to top, #fff 70%, rgba(255, 255, 255, 0.8));
+}
+
+.submit-btn {
+  height: 48px;
+  font-size: 16px;
+  font-weight: 500;
+}
+
+.btn-icon {
+  margin-right: 3px;
+  font-size: 12px;
+}
+
+/* 移动端适配 */
+@media (max-width: 480px) {
+  .goods-grid {
+    grid-template-columns: 1fr;
+  }
+
+  .device-card {
+    margin: 15px;
+    padding: 12px;
+  }
+
+  .goods-thumb {
+    width: 60px;
+    height: 60px;
+  }
+
+  .submit-btn {
+    height: 44px;
+    font-size: 15px;
+  }
+}
+
+:deep(.van-image__img) {
+  object-fit: contain !important;
+}
+</style>

+ 266 - 0
src/views/device/tax/index.vue

@@ -0,0 +1,266 @@
+<template>
+  <div class="payment-settings">
+    <s-header :name="$t('remote.C29')" :noback="false" />
+
+    <!-- 设备名称标题 -->
+    <div class="device-header">
+      <div class="vertical-indicator"></div>
+      <h3 class="device-name">
+        {{ $t("device.equipmentName") }}:{{ deviceName }}
+      </h3>
+    </div>
+
+    <!-- 税收设置卡片 -->
+    <div class="settings-card">
+      <!-- 税费开关 -->
+      <div class="payment-item">
+        <div class="payment-info">
+          <van-icon name="balance-pay" class="payment-icon" />
+          <div class="payment-detail">
+            <h3 class="payment-title">{{ $t("tax.taxFee") }}</h3>
+            <p class="payment-desc">{{ $t("tax.taxFeeDesc") }}</p>
+          </div>
+        </div>
+        <van-switch
+          :model-value="taxStatus"
+          size="22px"
+          @click="handleSubmit"
+        />
+      </div>
+
+      <!-- 税率输入(条件渲染) -->
+      <div v-if="taxStatus" class="tax-rate-input">
+        <van-form @submit="changeTaxRate">
+          <van-field
+            v-model="taxRate"
+            type="number"
+            :label="$t('tax.taxRate')"
+            :placeholder="$t('tax.ratePlaceholder')"
+            :rules="[
+              { required: true, message: $t('tax.rateRequired') },
+              {
+                pattern: /^([0-9]\d?|100)(\.\d{1,2})?$/,
+                message: $t('tax.rateInvalid'),
+              },
+            ]"
+          >
+            <template #right-icon>
+              <span class="percentage-symbol">%</span>
+            </template>
+          </van-field>
+          <van-button
+            type="primary"
+            size="small"
+            class="update-btn"
+            native-type="submit"
+          >
+            {{ $t("tax.update") }}
+          </van-button>
+        </van-form>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import sHeader from "@/components/SimpleHeader";
+import {
+  getDeviceDetal,
+  updateTaxStatus,
+  updateTaxRate,
+} from "@/service/device";
+import { showConfirmDialog, showSuccessToast } from "vant";
+import { useRoute } from "vue-router";
+import { ref } from "vue";
+import { useI18n } from "vue-i18n";
+import { onMounted } from "vue";
+
+export default {
+  components: { sHeader },
+  setup() {
+    const { t } = useI18n();
+    const route = useRoute();
+    const deviceId = ref(route.query.deviceId);
+    const deviceName = ref(route.query.name);
+    const deviceDetal = ref(null);
+
+    const taxStatus = ref(false);
+    const taxRate = ref("");
+
+    onMounted(async () => {
+      await getDeviceInfo();
+    });
+
+    // 获取设备数据
+    const getDeviceInfo = async () => {
+      const { data } = await getDeviceDetal({ id: deviceId.value });
+      if (data.code === "00000") {
+        deviceDetal.value = data.data;
+        if (deviceDetal.value.taxStatus) {
+          taxStatus.value = deviceDetal.value.taxStatus;
+          taxRate.value = deviceDetal.value.taxRate;
+        }
+      }
+    };
+
+    const handleSubmit = () => {
+      showConfirmDialog({
+        title: t("device.operationConfirmation"),
+        message: t("device.pleaseConfirmAgainWhetherToOperate"),
+      })
+        .then(async () => {
+          const { data } = await updateTaxStatus({
+            equipmentId: deviceId.value,
+            taxStatus: !taxStatus.value,
+          });
+          if (data.code === "00000") {
+            taxStatus.value = !taxStatus.value;
+          }
+        })
+        .catch(() => {
+          // on cancel
+        });
+    };
+
+    const changeTaxRate = async() => {
+      showConfirmDialog({
+        title: t("device.operationConfirmation"),
+        message: t("device.pleaseConfirmAgainWhetherToOperate"),
+      })
+        .then(async () => {
+          console.log("taxRate", taxRate.value);
+          const { data } = await updateTaxRate({
+            equipmentId: deviceId.value,
+            taxRate: taxRate.value,
+          });
+          if (data.code === "00000") {
+            showSuccessToast(t("device.modificationSucceeded"));
+          }
+        })
+        .catch(() => {
+          // on cancel
+        });
+    };
+
+    return {
+      deviceName,
+      taxStatus,
+      taxRate,
+      handleSubmit,
+      changeTaxRate,
+    };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.payment-settings {
+  --payment-bg: #ffffff;
+  --border-color: #ebedf0;
+  --active-color: #4d6add;
+  --text-primary: #323233;
+  --text-secondary: #969799;
+
+  background: #f7f8fa;
+  min-height: 100vh;
+
+  .device-header {
+    display: flex;
+    align-items: center;
+    padding: 12px 15px;
+    background: #fff;
+    margin: 12px 16px;
+    border-radius: 8px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+
+    .vertical-indicator {
+      width: 4px;
+      height: 15px;
+      background: var(--active-color, #4d6add);
+      border-radius: 2px;
+      margin-right: 10px;
+    }
+
+    .device-name {
+      margin: 0;
+      font-size: 15px;
+      color: #404d74;
+      font-weight: 550;
+
+      // 长名称处理
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      max-width: 70vw;
+    }
+  }
+
+  .settings-card {
+    background: #fff;
+    border-radius: 12px;
+    margin: 12px 16px;
+    padding: 16px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+  }
+
+  .payment-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+
+  .payment-info {
+    display: flex;
+    align-items: center;
+  }
+
+  .payment-icon {
+    font-size: 24px;
+    color: #4dc294;
+    margin-right: 12px;
+  }
+
+  .payment-title {
+    font-size: 15px;
+    color: #333;
+    margin-bottom: 4px;
+  }
+
+  .payment-desc {
+    font-size: 12px;
+    color: #666;
+  }
+
+  .tax-rate-input {
+    margin-top: 16px;
+    padding-top: 16px;
+    border-top: 1px solid #eee;
+  }
+
+  .percentage-symbol {
+    color: #999;
+    padding-left: 4px;
+  }
+
+  .update-btn {
+    margin-top: 12px;
+    width: 120px;
+    border-radius: 18px;
+  }
+
+  /* 移动端适配 */
+  @media (max-width: 480px) {
+    .device-name {
+      font-size: 15px;
+    }
+
+    .payment-title {
+      font-size: 14px;
+    }
+
+    .tax-rate-input {
+      margin-top: 12px;
+    }
+  }
+}
+</style>

+ 766 - 0
src/views/home/index copy.vue

@@ -0,0 +1,766 @@
+<template>
+  <!-- 主页 -->
+  <div class="homePage flex-col">
+    <div class="homeBox">
+      <s-header :name="sys ? sys.title : (sysTitle == 'AETI GLOBAL' ? sysTitle : $t('public.sysName'))" :noback="true"
+        :isFixed="false"></s-header>
+      <!-- 留言滚动条 -->
+      <template v-if="noticeContent.title">
+        <van-notice-bar @click="noticeClk" mode="link" :scrollable="true" color="rgba(64,77,116,1)" background="#fff"
+          style="font-size: 0.375rem;" left-icon="volume-o" :text="noticeContent.title" />
+      </template>
+      <div class="intervalRow"></div>
+      <div class="nameDeviceRow flex-col">
+        <span class="txt3">{{ userName }}</span>
+        <div class="l-flex-RC">
+          <span class="info3" style="color: #4d6add;">{{ $t("home.totalEquipment") }} : {{ equipStatus.machineTotalNum
+            }}</span>
+          <div class="lineCon o-mlr-6"></div>
+          <span class="word2" style="color: #07c160;">{{ $t("home.running") }} : {{ equipStatus.machineUseNum }}</span>
+        </div>
+      </div>
+      <!-- 没有数据概览M14权限的人看不到数据概览和ECharts -->
+      <!-- 数据概览 -->
+      <!-- 时间选择 -->
+      <dateSelectList v-if="showDataDiv" @update="update($event)"></dateSelectList>
+      <typeDownMenu v-if="showDataDiv" :isHome="true" @upselectdata="upselectdata($event)"></typeDownMenu>
+      <!-- 订单数据 -->
+      <div v-if="showDataDiv" class="o-plr-8 o-pt-10">
+        <div class="salesData flex-col" @click="pushOrderCenter">
+          <div class="topTitle flex-row justify-end" v-if="isOrderData">
+            <span class="txt10">{{ $t("home.orderData") }}</span>
+            <div class="layer4 flex-col"></div>
+          </div>
+          <div class="salesDataBox flex-row">
+            <div class="dataGroup flex-col">
+              <div class="dataGroupBox l-flex-RC justify-center">
+                <!-- 首页 - 订单数据 - 收入总额¥ -->
+                <span class="currencySymbol o-pr-2">{{ currencySymbol }}</span>
+                <span class="dataNum">{{ salesVolume.toFixed(2) }}</span>
+              </div>
+              <span class="dataText">{{ $t("home.totalIncome") }}</span>
+            </div>
+            <div class="dataGroup flex-col">
+              <div class="dataGroupBox flex-col justify-between">
+                <span class="dataNum">{{ salesNumber }}</span>
+                <span class="dataText">{{ $t("home.productNum") }}</span>
+              </div>
+            </div>
+            <div class="dataGroup flex-col">
+              <div class="dataGroupBox flex-col justify-between">
+                <span class="dataNum">{{ orderNumber }}</span>
+                <span class="dataText">{{ $t("home.numberOfOrders") }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 时间 -->
+      <div v-if="showDataDiv" class="c-text-c" style="font-size: 18px; margin-top: 5px;">
+        {{ Format_time(dateSelect.startDate, 'YYYY/MM/DD') }}-{{ Format_time(dateSelect.endDate, 'YYYY/MM/DD') }}
+      </div>
+      <div v-if="showDataDiv && !noData(salesVolume, salesNumber)">
+        <div ref="chartBox" class="Chart1 flex-col"></div>
+      </div>
+      <kNoData v-else></kNoData>
+      <!-- 常用工具 -->
+      <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">
+              <div class="wrap2 flex-row l-flex-RC">
+                <div class="outer10 flex-col"></div>
+                <div class="TextGroup13 flex-col">
+                  <span class="txt13">{{ $t("home.commonTools") }}</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="outer11 flex-row">
+          <div class="main25 flex-col" v-for="(item, index) in pushToolList" :key="index"
+            @click="pushToolPage(item)">
+            <img class="mod7 flex-col" :src="showLogo(item)" />
+            <div class="TextGroup14 flex-col">
+              <span class="info15">{{ $t("permission." + item) }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div v-if="showDataDiv && user.ifForeign === '1'">
+        <!-- 机器销售额 -->
+        <div class="outer9 flex-col justify-center">
+          <div class="main24 flex-col">
+            <div class="ImageText10 flex-col">
+              <div class="wrap2 flex-row l-flex-RC">
+                <div class="outer10 flex-col"></div>
+                <div class="TextGroup13 flex-col">
+                  <span class="txt13">{{ $t("home.machineSales") }}</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- 各支付方式总额 -->
+        <van-list offset="100" :immediate-check="false">
+          <div v-for="item in combinedList" :key="item" class="o-ptb-10">
+            <div>
+              <van-cell-group inset class="machineSaleBox">
+                <!-- 设备名称 -->
+                <div class="contentWord kBordBott">{{ item.machineName }}
+                </div>
+                <van-row class="layer5" justify="space-between">
+                  <van-col span="12">{{ $t("home.coinsBills") }}: {{ item.coinsBills !== undefined ? item.coinsBills :
+        '0'
+                    }}</van-col>
+                  <!-- 信用卡 -->
+                  <van-col span="12">{{ $t("home.creditCard") }}: {{ item.creditCard !== undefined ? item.creditCard :
+        '0'
+                    }}</van-col>
+                  <van-col span="12">{{ $t("home.allPayTypeTotal") }}: {{ calculateTotal(item) }}</van-col>
+                </van-row>
+              </van-cell-group>
+            </div>
+          </div>
+        </van-list>
+      </div>
+
+    </div>
+    
+    <!-- 通知弹窗 -->
+    <kDialog :dialogTitle="$t('home.notificationPop.notification')" :cancelBtnTxt="$t('home.notificationPop.nextTime')"
+      :confirmBtnTxt="$t('home.notificationPop.roger')" ref="kDialogRef" @confirmclk="confirmClk">
+      <template #content>
+        <div class="o-w" style="max-height: 50vh; overflow-y: auto" v-html="noticeContent.note"></div>
+      </template>
+    </kDialog>
+    <van-dialog v-model:show="showAlarm" :title="$t('home.alarmTitle')" @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="$t('home.alarmDevice') + (item.name ? item.name : item.clientId.slice(-6)) + $t('home.alarmContent') + item.alarmContent" />
+        </div>
+      </div>
+    </van-dialog>
+  </div>
+</template>
+
+<script>
+// 导入无数据组件
+import kNoData from "../../components/commom/kNoData/index.vue";
+import kDialog from "../../components/commom/kDialog/index.vue";
+import { onMounted, ref, nextTick } from "vue";
+import sHeader from "../../components/SimpleHeader";
+import dateSelectList from "../../components/dateSelectList";
+import typeDownMenu from "../../components/typeDownMenu";
+import { getLoginUser, Format_time, styleUrl } from "../../common/js/utils";
+import { useRouter } from "vue-router";
+import {
+  getStatistics,
+  Api_getNotice,
+  Api_postMachineNum,
+  Api_getUpdateNotice,
+  getIsAlarm,
+  Api_getEquipmentPageStatistics,
+} from "../../service/home";
+import { getAdminRole } from "@/service/user";
+import dateUtil from "../../utils/dateUtil";
+import { useI18n } from "vue-i18n";
+import { showFailToast, showToast } from "vant";
+import RobotIcon from '@/assets/home/robot.png';
+import { getLocal, setLocal, navigatorLanguage } from "@/common/js/utils";
+
+export default {
+  name: "home",
+  components: {
+    sHeader,
+    dateSelectList,
+    typeDownMenu,
+    kDialog,
+    kNoData,
+  },
+  setup() {
+    // 是否显示机器人
+    const isShowRobot = ref(false);
+    // 设备状况
+    const equipStatus = ref({});
+    // 获取设备情况
+    const getMachineNum = () => {
+      Api_postMachineNum({ adminId: user.id }).then((res) => {
+        equipStatus.value = res.data.data || {};
+      });
+    };
+    const noticeContent = ref({});
+    // 获取公告
+    const getNotice = () => {
+      Api_getNotice({ adminId: user.id }).then((res) => {
+        noticeContent.value = res.data.data || {};
+      });
+    };
+    const { t } = useI18n();
+    // 通知弹窗
+    const kDialogRef = ref(null);
+    // 点击通知栏
+    const noticeClk = () => {
+      kDialogRef.value.openDialog();
+    };
+    // 点击右侧按钮
+    const confirmClk = () => {
+      Api_getUpdateNotice({
+        adminId: user.id,
+      }).then((res) => {
+        showToast(res.data.message);
+        setTimeout(() => {
+          getNotice();
+        }, 500);
+      });
+    };
+    const firstLogin = ref(true);
+    const user = getLoginUser();
+    const router = useRouter();
+    const userName = ref(user.name);
+    const sys = ref(null);
+    const sysTitle = ref(''); // 页头标题
+    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) {
+      currencySymbol.value = user.currencySymbol;
+    } else {
+      currencySymbol.value = "¥";
+    }
+
+    // 图表对象
+    const chartBox = ref();
+    let chartObj = null;
+    // 跳转订单中心
+    const pushOrderCenter = () => {
+      router.push({ path: "/orderCenter" });
+    };
+    const dateSelect = ref({});
+    const update = (uDate) => {
+      dateSelect.value = uDate;
+      getStatisticsFun();
+      getMachineNameListFunc()
+    };
+    let typeSelectData = {};
+    const upselectdata = (uSData) => {
+      typeSelectData = uSData;
+      getStatisticsFun();
+      getMachineNameListFunc()
+    };
+    const salesVolume = ref(0);
+    const salesNumber = ref(0);
+    const orderNumber = ref(0);
+    // 查询图表
+    const getStatisticsFun = async () => {
+      const params = {
+        ...dateSelect.value,
+        adminId: typeSelectData.adminId === null ? user.id : typeSelectData.adminId,
+        ifForeign: typeSelectData.ifForeign === '' ? user.ifForeign : typeSelectData.ifForeign,
+        payType: typeSelectData.payType,
+        clientId: typeSelectData.clientId,
+        username: typeSelectData.userName, // 商家
+        companyType: typeSelectData.companyType, // 公司平台
+        machineType: typeSelectData.machineType, // 设备类型
+        equipmentId:
+          typeSelectData.equipmentId === "" ? null : typeSelectData.equipmentId,
+      };
+      const { data } = await getStatistics(params);
+      if (data.code && data.data) {
+        salesVolume.value = 0;
+        salesNumber.value = 0;
+        orderNumber.value = 0;
+        data.data.series[0].data.forEach((item) => {
+          salesNumber.value = parseInt(salesNumber.value + item);
+        });
+        data.data.series[1].data.forEach((item) => {
+          salesVolume.value = parseFloat(salesVolume.value) + parseFloat(item);
+        });
+        data.data.series[2].data.forEach((item) => {
+          orderNumber.value = parseInt(orderNumber.value + item);
+        });
+        data.data.categories.forEach((item, index) => {
+          if (item == "周1") {
+            data.data.categories[index] = t("home.week.mon");
+          }
+          if (item == "周2") {
+            data.data.categories[index] = t("home.week.tue");
+          }
+          if (item == "周3") {
+            data.data.categories[index] = t("home.week.wed");
+          }
+          if (item == "周4") {
+            data.data.categories[index] = t("home.week.thu");
+          }
+          if (item == "周5") {
+            data.data.categories[index] = t("home.week.fri");
+          }
+          if (item == "周6") {
+            data.data.categories[index] = t("home.week.sat");
+          }
+          if (item == "周日") {
+            data.data.categories[index] = t("home.week.sun");
+          }
+        })
+        // 解决eacharts与v-if的渲染问题
+        await nextTick();
+        if (chartBox.value) {
+          chartObj = window.echarts.init(chartBox.value, null, {
+            renderer: "canvas",
+            useDirtyRect: false,
+          });
+          const option = {
+            tooltip: {
+              trigger: "axis",
+              axisPointer: {
+                type: "shadow",
+              },
+            },
+            grid: {
+              top: '10%',
+              left: "3%",
+              right: "4%",
+              bottom: "10%",
+              containLabel: true,
+              height: 'auto',
+            },
+            legend: {
+              bottom: 0,
+              right: 10,
+              itemWidth: 10,
+              itemHeight: 10,
+              icon: "rect",
+            },
+            // 固定屏幕显示多少个,其余的滑动
+            dataZoom: [
+              // {
+              // 	type: 'slider',
+              // 	xAxisIndex: 0,
+              // 	filterMode: 'none',
+              // 	// 开始的值
+              // 	startValue: null,
+              // 	// 结束的值
+              // 	endValue: null,
+              // 	// 锁定滑动的区域
+              // 	// zoomLock:true,
+              // },
+              {
+                type: "inside",
+                xAxisIndex: 0,
+                filterMode: "none",
+                startValue: 0,
+                endValue: 50,
+                zoomLock: true,
+              },
+            ],
+            xAxis: {
+              type: "category",
+              axisLabel: {
+                rotate: 35,
+              },
+              data: data.data.categories,
+            },
+            yAxis: {
+              type: "value",
+            },
+            series: [
+              {
+                ...data.data.series[0],
+                type: "bar",
+                itemStyle: { color: "#e59a6d" },
+                name: t("home.productNum"),
+                label: {
+                  show: true,
+                  position: "top",
+                },
+              },
+              {
+                ...data.data.series[1],
+                type: "bar",
+                itemStyle: { color: "#4d6add" },
+                name: t("home.salesAmount"),
+                label: {
+                  show: true,
+                  position: "top",
+                },
+              },
+            ],
+          };
+
+          option.dataZoom[0]["startValue"] =
+            data.data.categories[data.data.categories.length - 4];
+          option.dataZoom[0]["endValue"] =
+            data.data.categories[data.data.categories.length - 1];
+          // option.dataZoom[0].start =
+          //   (data.data.categories.length - 5) * 10;
+          // option.dataZoom[0].end =
+          //   (data.data.categories.length - 1) * 10;
+          chartObj && chartObj.setOption(option);
+          //图形宽度随屏幕宽度改变而改变
+          window.onresize = chartObj.resize;
+        }
+      }
+    };
+    const pushToolList = ref([]);
+    // 是否有报警
+    const showAlarm = ref(false);
+    const alarmList = ref([]);
+    // 页面初始化
+    onMounted(async () => {
+      // 加载样式
+      styleUrl('home');
+      // 检测语言是否有缓存
+      if (!getLocal("curLang")) {
+        //  根据浏览器语言重新缓存到localstorage
+        setLocal("curLang", navigatorLanguage());
+      }
+      if (localStorage.getItem("loginSys")) {
+        const loginSysString = localStorage.getItem("loginSys");
+        sys.value = JSON.parse(loginSysString);
+      }
+      firstLogin.value = localStorage.getItem('firstLogin');
+      // 设置菜单权限
+      // menuSet();
+      // 设置菜单权限, 只执行一次
+      // once(menuSet);
+      dateSelect.value = {
+        chartType: "day",
+        startDate: dateUtil.formateDate(
+          new Date(new Date(new Date().getTime()).setHours(0, 0, 0, 0)),
+          "yyyy-MM-dd hh:mm:ss"
+        ),
+        endDate: dateUtil.formateDate(
+          new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 59)),
+          "yyyy-MM-dd hh:mm:ss"
+        ),
+      };
+      // 获取账号权限
+      getAccountPer();
+      typeSelectData = {
+        userName: user.username,
+        clientId: null,
+        adminId: null,
+      };
+      getStatisticsFun();
+      // 获取首页公告
+      getNotice();
+      // 获取设备情况
+      getMachineNum();
+      if (user.type > 1) {
+        // 查询设备是否有报警
+        const queryParams = {
+          adminId: user.id,
+          todayDate: dateUtil.formateDate(new Date(), "yyyy-MM-dd"), // 当天时间
+        };
+        const { data } = await getIsAlarm(queryParams);
+        if (data.data != null) {
+          if (firstLogin.value == 'true') {
+            showAlarm.value = true;
+          }
+          alarmList.value = data.data;
+        }
+      }
+
+      if (localStorage.getItem('curLang') == 'zh') { // 机器人仅在中文环境下使用
+        isShowRobot.value = true;
+      }
+
+      getTitleFunc();
+
+      if (user) {
+        getMachineNameListFunc();
+      }
+
+    });
+
+    const finished = ref(false);
+    const loading = ref(true);
+
+
+    // 设备销售数据 
+    const combinedList = ref([]); // 设备销额集合
+    const calculateTotal = (item) => {
+      let total = 0;
+      if (typeof item.coinsBills === 'number' && !isNaN(item.coinsBills)) {
+        total += item.coinsBills;
+      }
+
+      if (typeof item.creditCard === 'number' && !isNaN(item.creditCard)) {
+        total += item.creditCard;
+      }
+      return total;
+    }
+
+    const getMachineNameListFunc = async () => {
+
+      combinedList.value = [];
+      const searchParams = {
+        adminId: user.id,
+        ...dateSelect.value,
+      }
+      try {
+        const { data } = await Api_getEquipmentPageStatistics(Object.assign({}, searchParams));
+        if (data.code === "00000" && data.data) {
+
+          for (let i = 0; i < data.data.categories.length; i++) {
+            const machineNameVal = data.data.categories[i];  // 设备名称
+            const coinsVal = data.data.series[0].data[i];  // 硬币销额
+            const billsVal = data.data.series[1].data[i];  // 纸币销额
+            const coinsBillsVal = data.data.series[2].data[i];  // 硬币+纸币销额
+            const creditCardVal = data.data.series[3].data[i];  // 信用卡销额
+            const electronicPaymentVal = data.data.series[4].data[i];  // 电子支付销额
+
+
+            const machineSalesData = {
+              machineName: machineNameVal,
+              coins: coinsVal,
+              bills: billsVal,
+              coinsBills: coinsBillsVal,
+              creditCard: creditCardVal,
+              electronicPayment: electronicPaymentVal
+            }
+
+            combinedList.value.push(machineSalesData);
+          }
+
+        } else {
+          showFailToast(data.message);
+        }
+      } catch (error) {
+        console.error("Error", error)
+      }
+    }
+
+    const getTitleFunc = async () => {
+      const currentDomain = window.location.href;
+      switch (true) {
+        case currentDomain.includes('/aeti/'):
+          sysTitle.value = 'AETI GLOBAL';
+          break;
+        default:
+          sysTitle.value = t('public.sysName');
+      }
+    }
+
+    const confirmAlarm = () => {
+      localStorage.setItem('firstLogin', false);
+    }
+
+    const menuList = [];
+
+    const showDataDiv = ref(true);
+
+    const isOrderData = ref(true);
+
+    // 获取账号权限
+    const getAccountPer = async () => {
+      const { data } = await getAdminRole({ adminId: user.id });
+      if (data.code === '00000') {
+        if (data.data.menuCodesJson !== null) {
+          menuList.value = sortedArray(data.data.menuCodesJson);
+        }
+        // 子商家以上级别,默认可以看到订单详情
+        if (user.type === 3 && !menuList.value.includes("M4")) {
+          // 如果有数据概览权限 和 订单数据权限
+          if (!menuList.value.includes("M4")) {
+            showDataDiv.value = false;
+          }
+          if (!menuList.value.includes("M14")) {
+            isOrderData.value = false;
+          }
+        }
+        // 组合菜单权限
+        menuList.value.forEach((item) => {
+          // 设备管理M1,设备查看M2,订单数据M4,任务消息M6,销售排行M11,数据概览M14,订单退款M16,系统脱机M17 这些图标不用加载到底部菜单上
+          if (
+            item !== "M1" &&
+            item !== "M2" && // M2本身就无logo
+            item !== "M4" &&
+            item !== "M6" &&
+            item !== "M8" &&
+            item !== "M11" &&
+            item !== "M12" &&
+            item !== "M13" &&
+            item !== "M18" &&
+            item !== "M14" &&
+            item !== "M16" &&
+            item !== "M17" &&
+            item !== "M20"
+          ) {
+            pushToolList.value.push(item);
+          }
+        });
+      }
+    }
+
+    // 解析 + 排序计算属性
+    const sortedArray = (value) => {
+      try {
+        // 1. 安全解析JSON
+        const parsed = JSON.parse(value)
+        if (!Array.isArray(parsed)) return []
+
+        // 2. 自定义排序函数
+        const getNumber = str => parseInt(str.match(/\d+/)?.[0] || 0)
+
+        // 3. 执行排序(数字从小到大)
+        return [...parsed].sort((a, b) => {
+          return getNumber(a) - getNumber(b)
+        })
+
+      } catch (error) {
+        console.error('解析失败:', error)
+        return []
+      }
+    };
+
+    // 常用操作跳转页面
+    const pushToolPage = (index) => {
+      switch (index) {
+        case "M1":
+          router.push({ path: "/device" }); // 设备管理
+          break;
+        case "M3":
+          router.push({ path: "/accountOperation" }); // 账户操作
+          break;
+        case "M4":
+          router.push({ path: "/orderCenter" }); // 订单数据
+          break;
+        case "M5":
+          router.push({ path: "/advertManage" }); // 广告管理
+          break;
+        case "M6":
+          router.push({ path: "/taskMessage" });
+          break;
+        case "M7":
+          router.push({ path: "/discountCode" });
+          break;
+        case "M8":
+          router.push({ path: "/accountPer" });
+          break;
+        case "M9":
+          router.push({ path: "/orderExport" });
+          break;
+        case "M10":
+          router.push({ path: "/subLedgerManage" });
+          break;
+        case "M11":
+          router.push({ path: "/robotranking" });
+          break;
+        case "M12":
+          router.push({ path: "/joinpayMch" });
+          break;
+        case "M13":
+          router.push({ path: "/shandeMch" });
+          break;
+        case "M15":
+          router.push({ path: "/alarmHistory" }); // 报警历史
+          break;
+        case "M18":
+          router.push({ path: "/labelMan" });
+          break;
+        case "M19":
+          router.push({ path: "/apkManage" });
+          break;
+        case "M20":
+          router.push({ path: "/merchantManage" });
+          break;
+        case "M21":
+          router.push({ path: "/mqtt" });
+          break;
+        case "M22":
+          router.push({ path: "/terminal" });
+          break;
+      }
+    };
+
+
+    // 如果是空数据
+    const noData = (volumes, nums) => {
+      if (!volumes && !nums) {
+        return true;
+      }
+      return false;
+    };
+    // 显示logo
+    const showLogo = (url) => {
+      return require(`../../assets/home/${url}.png`);
+    };
+
+    // const aiDialog = showDialog();
+    const offset = ref({ x: Math.floor(window.innerWidth * 0.8), y: Math.floor(window.innerHeight * 0.75) });
+
+    const popupVisible = ref(false);
+    const aiUrl = "https://chatbot.weixin.qq.com/webapp/c3thmydLGYWDugrgtfAj5I0Ng3sniv?robotName=Cotton%20Candy%20Robot"; // 阿里云智能对话机器人-ccbot
+
+    // AI小助手
+    const onClickBot = () => {
+      popupVisible.value = true;
+    }
+
+    const onOffsetChange = (offset) => {
+      offset.value = offset;
+    }
+
+    const onClose = () => {
+      popupVisible.value = false;
+    }
+
+    return {
+      user,
+      userName,
+      update,
+      upselectdata,
+      chartBox,
+      pushOrderCenter,
+      pushToolList,
+      pushToolPage,
+      salesVolume,
+      salesNumber,
+      orderNumber,
+      sys,
+      noticeClk,
+      confirmClk,
+      kDialogRef,
+      noData,
+      equipStatus,
+      noticeContent,
+      showLogo,
+      dateSelect,
+      Format_time,
+      showDataDiv,
+      currencySymbol,
+      firstLogin,
+      pic1,
+      showAlarm,
+      alarmList,
+      confirmAlarm,
+      onOffsetChange,
+      robotIcon: RobotIcon,
+      aiUrl,
+      popupVisible,
+      onClose,
+      onClickBot,
+      offset,
+      isShowRobot,
+      sysTitle,
+      finished,
+      loading,
+      combinedList,
+      isOrderData,
+      calculateTotal
+    };
+  },
+
+};
+</script>
+
+<style lang="less" scoped>
+@import "../../common/style/common";
+</style>

+ 385 - 113
src/views/home/index.vue

@@ -1,133 +1,115 @@
 <template>
-  <!-- 主页 -->
-  <div class="homePage flex-col">
-    <div class="homeBox">
-      <s-header :name="sys ? sys.title : (sysTitle == 'AETI GLOBAL' ? sysTitle : $t('public.sysName'))" :noback="true"
-        :isFixed="false"></s-header>
-      <!-- 留言滚动条 -->
-      <template v-if="noticeContent.title">
-        <van-notice-bar @click="noticeClk" mode="link" :scrollable="true" color="rgba(64,77,116,1)" background="#fff"
-          style="font-size: 0.375rem;" left-icon="volume-o" :text="noticeContent.title" />
-      </template>
-      <div class="intervalRow"></div>
-      <div class="nameDeviceRow flex-col">
-        <span class="txt3">{{ userName }}</span>
-        <div class="l-flex-RC">
-          <span class="info3" style="color: #4d6add;">{{ $t("home.totalEquipment") }} : {{ equipStatus.machineTotalNum
-            }}</span>
-          <div class="lineCon o-mlr-6"></div>
-          <span class="word2" style="color: #07c160;">{{ $t("home.running") }} : {{ equipStatus.machineUseNum }}</span>
-        </div>
-      </div>
-      <!-- 没有数据概览M14权限的人看不到数据概览和ECharts -->
-      <!-- 数据概览 -->
-      <!-- 时间选择 -->
-      <dateSelectList v-if="showDataDiv" @update="update($event)"></dateSelectList>
-      <typeDownMenu v-if="showDataDiv" :isHome="true" @upselectdata="upselectdata($event)"></typeDownMenu>
-      <!-- 订单数据 -->
-      <div v-if="showDataDiv" class="o-plr-8 o-pt-10">
-        <div class="salesData flex-col" @click="pushOrderCenter">
-          <div class="topTitle flex-row justify-end" v-if="isOrderData">
-            <span class="txt10">{{ $t("home.orderData") }}</span>
-            <div class="layer4 flex-col"></div>
+  <div class="home-container">
+    <!-- 头部 -->
+    <s-header :name="sys ? sys.title : (sysTitle == 'AETI GLOBAL' ? sysTitle : $t('public.sysName'))" :noback="true"
+      class="app-header" />
+    <!-- 主体内容 -->
+    <main class="main-content">
+      <!-- 通知栏 -->
+      <van-notice-bar v-if="noticeContent.title" class="smart-notice" left-icon="volume-o" :text="noticeContent.title"
+        @click="noticeClk" :scrollable="true" color="#404B74" background="#ffffff" />
+
+      <!-- 用户信息卡 -->
+      <div class="user-card">
+        <div class="user-info">
+          <div class="avatar">
+            <van-icon name="user-circle-o" size="48px" color="#4D6ADD" />
           </div>
-          <div class="salesDataBox flex-row">
-            <div class="dataGroup flex-col">
-              <div class="dataGroupBox l-flex-RC justify-center">
-                <!-- 首页 - 订单数据 - 收入总额¥ -->
-                <span class="currencySymbol o-pr-2">{{ currencySymbol }}</span>
-                <span class="dataNum">{{ salesVolume.toFixed(2) }}</span>
-              </div>
-              <span class="dataText">{{ $t("home.totalIncome") }}</span>
-            </div>
-            <div class="dataGroup flex-col">
-              <div class="dataGroupBox flex-col justify-between">
-                <span class="dataNum">{{ salesNumber }}</span>
-                <span class="dataText">{{ $t("home.productNum") }}</span>
-              </div>
-            </div>
-            <div class="dataGroup flex-col">
-              <div class="dataGroupBox flex-col justify-between">
-                <span class="dataNum">{{ orderNumber }}</span>
-                <span class="dataText">{{ $t("home.numberOfOrders") }}</span>
-              </div>
+          <div class="user-meta">
+            <h3 class="user-name">{{ userName }}</h3>
+            <div class="device-status">
+              <span class="status-item total">
+                <van-icon name="desktop-o" size="14px" />
+                {{ $t("home.totalEquipment") }}: {{ equipStatus.machineTotalNum }}
+              </span>
+              <span class="status-item running">
+                <van-icon name="desktop-o" size="14px" />
+                {{ $t("home.running") }}: {{ equipStatus.machineUseNum }}
+              </span>
             </div>
           </div>
         </div>
       </div>
-      <!-- 时间 -->
-      <div v-if="showDataDiv" class="c-text-c" style="font-size: 18px; margin-top: 5px;">
-        {{ Format_time(dateSelect.startDate, 'YYYY/MM/DD') }}-{{ Format_time(dateSelect.endDate, 'YYYY/MM/DD') }}
-      </div>
-      <div v-if="showDataDiv && !noData(salesVolume, salesNumber)">
-        <div ref="chartBox" class="Chart1 flex-col"></div>
-      </div>
-      <kNoData v-else></kNoData>
-      <!-- 常用工具 -->
-      <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">
-              <div class="wrap2 flex-row l-flex-RC">
-                <div class="outer10 flex-col"></div>
-                <div class="TextGroup13 flex-col">
-                  <span class="txt13">{{ $t("home.commonTools") }}</span>
-                </div>
-              </div>
+
+      <dateSelectList v-if="showDataDiv" @update="update($event)"></dateSelectList>
+      <typeDownMenu v-if="showDataDiv" :isHome="true" @upselectdata="upselectdata($event)"></typeDownMenu>
+      <!-- 数据概览 -->
+      <section v-if="showDataDiv" class="data-section">
+        <div class="section-header">
+          <div class="header-main">
+            <h3 class="section-title">
+              {{ $t("home.dataOverview") }} </h3>
+            <div class="data-entry" @click="pushOrderCenter">
+              <span>{{ $t("home.orderData") }}</span>
+              <van-icon name="arrow" class="entry-arrow" />
             </div>
           </div>
         </div>
-        <div class="outer11 flex-row">
-          <div class="main25 flex-col" v-for="(item, index) in pushToolList" :key="index"
-            @click="pushToolPage(item)">
-            <img class="mod7 flex-col" :src="showLogo(item)" />
-            <div class="TextGroup14 flex-col">
-              <span class="info15">{{ $t("permission." + item) }}</span>
+
+        <!-- 数据卡片 -->
+        <div class="data-cards">
+          <div class="data-card income">
+            <div class="card-content">
+              <div class="card-text">
+                <div class="value">{{ currencySymbol }}{{ salesVolume.toFixed(2) }}</div>
+                <div class="label">{{ $t("home.totalIncome") }}</div>
+              </div>
             </div>
           </div>
-        </div>
-      </div>
 
-      <div v-if="showDataDiv && user.ifForeign === '1'">
-        <!-- 机器销售额 -->
-        <div class="outer9 flex-col justify-center">
-          <div class="main24 flex-col">
-            <div class="ImageText10 flex-col">
-              <div class="wrap2 flex-row l-flex-RC">
-                <div class="outer10 flex-col"></div>
-                <div class="TextGroup13 flex-col">
-                  <span class="txt13">{{ $t("home.machineSales") }}</span>
-                </div>
+          <div class="data-card sales">
+            <div class="card-content">
+              <div class="card-text">
+                <div class="value">{{ salesNumber }}</div>
+                <div class="label">{{ $t("home.productNum") }}</div>
               </div>
             </div>
           </div>
         </div>
-        <!-- 各支付方式总额 -->
-        <van-list offset="100" :immediate-check="false">
-          <div v-for="item in combinedList" :key="item" class="o-ptb-10">
-            <div>
-              <van-cell-group inset class="machineSaleBox">
-                <!-- 设备名称 -->
-                <div class="contentWord kBordBott">{{ item.machineName }}
-                </div>
-                <van-row class="layer5" justify="space-between">
-                  <van-col span="12">{{ $t("home.coinsBills") }}: {{ item.coinsBills !== undefined ? item.coinsBills :
-        '0'
-                    }}</van-col>
-                  <!-- 信用卡 -->
-                  <van-col span="12">{{ $t("home.creditCard") }}: {{ item.creditCard !== undefined ? item.creditCard :
-        '0'
-                    }}</van-col>
-                  <van-col span="12">{{ $t("home.allPayTypeTotal") }}: {{ calculateTotal(item) }}</van-col>
-                </van-row>
-              </van-cell-group>
+
+        <div v-if="showDataDiv" class="c-text-c" style="font-size: 18px; margin-top: 5px;">
+          {{ Format_time(dateSelect.startDate, 'YYYY/MM/DD') }}-{{ Format_time(dateSelect.endDate, 'YYYY/MM/DD') }}
+        </div>
+
+        <!-- 图表 -->
+        <div class="chart-container">
+          <div v-if="!noData(salesVolume, salesNumber)" ref="chartBox" class="sales-chart"></div>
+          <k-no-data v-else />
+        </div>
+      </section>
+
+      <!-- 常用工具 -->
+      <section v-if="user.type === 0 || user.type === 4" class="tools-section">
+        <h3 class="section-title">{{ $t("home.commonTools") }}</h3>
+        <van-row :gutter="12">
+          <van-col v-for="(item, index) in pushToolList" :key="index" span="8">
+            <div class="tool-item" @click="pushToolPage(item)">
+              <img class="tool-icon" :src="showLogo(item)" />
+              <span class="tool-name">{{ $t("permission." + item) }}</span>
             </div>
+          </van-col>
+        </van-row>
+      </section>
+
+      <!-- 机器销售额 -->
+      <section v-if="showDataDiv && user.ifForeign === '1'" class="machine-sales">
+        <h3 class="section-title">{{ $t("home.machineSales") }}</h3>
+        <div class="sales-list">
+          <div v-for="(item, index) in combinedList" :key="index" class="sales-item">
+            <van-cell-group inset>
+              <div class="machine-header">
+                <van-icon name="setting" class="machine-icon" />
+                {{ item.machineName }}
+              </div>
+              <van-cell :title="$t('home.coinsBills')" :value="item.coinsBills || '0'" class="sale-cell" />
+              <van-cell :title="$t('home.creditCard')" :value="item.creditCard || '0'" class="sale-cell" />
+              <van-cell :title="$t('home.allPayTypeTotal')" :value="calculateTotal(item)" class="total-cell" />
+            </van-cell-group>
           </div>
-        </van-list>
-      </div>
+        </div>
+      </section>
+    </main>
 
-    </div>
-    
     <!-- 通知弹窗 -->
     <kDialog :dialogTitle="$t('home.notificationPop.notification')" :cancelBtnTxt="$t('home.notificationPop.nextTime')"
       :confirmBtnTxt="$t('home.notificationPop.roger')" ref="kDialogRef" @confirmclk="confirmClk">
@@ -154,7 +136,7 @@ import { onMounted, ref, nextTick } from "vue";
 import sHeader from "../../components/SimpleHeader";
 import dateSelectList from "../../components/dateSelectList";
 import typeDownMenu from "../../components/typeDownMenu";
-import { getLoginUser, Format_time, styleUrl } from "../../common/js/utils";
+import { getLoginUser, Format_time } from "../../common/js/utils";
 import { useRouter } from "vue-router";
 import {
   getStatistics,
@@ -414,7 +396,6 @@ export default {
     // 页面初始化
     onMounted(async () => {
       // 加载样式
-      styleUrl('home');
       // 检测语言是否有缓存
       if (!getLocal("curLang")) {
         //  根据浏览器语言重新缓存到localstorage
@@ -763,4 +744,295 @@ export default {
 
 <style lang="less" scoped>
 @import "../../common/style/common";
+
+.home-container {
+  --primary-color: #4D6ADD;
+  --secondary-color: #07C160;
+  --text-primary: #2D354D;
+  --text-secondary: #666F8C;
+  --background: #F5F7FA;
+  --card-radius: 12px;
+  --shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+  --tabbar-height: 40px;
+  --tabbar-padding: calc(var(--tabbar-height) + 20px);
+
+  background: var(--background);
+  min-height: 100vh;
+
+  .app-header {
+    box-shadow: var(--shadow);
+  }
+
+  .main-content {
+    background-color: #F5F7FA;
+    min-height: calc(100vh - var(--tabbar-height));
+    padding-bottom: var(--tabbar-padding);
+  }
+
+  .smart-notice {
+    border-radius: 8px;
+    font-weight: 600;
+  }
+
+  .user-card {
+    background: white;
+    border-radius: var(--card-radius);
+    padding: 16px;
+    margin-bottom: 20px;
+    margin: 10px;
+    box-shadow: var(--shadow);
+
+    .user-info {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+
+      .user-meta {
+        flex: 1;
+
+        .user-name {
+          margin: 0 0 4px;
+          font-size: 18px;
+          color: var(--text-primary);
+        }
+
+        .device-status {
+          display: flex;
+          gap: 16px;
+          font-size: 13px;
+
+          .status-item {
+            display: flex;
+            align-items: center;
+            gap: 6px;
+            padding: 4px 8px;
+            border-radius: 16px;
+            background: #F5F7FA;
+
+            &.total {
+              color: var(--primary-color);
+              background: rgba(77, 106, 221, 0.1);
+            }
+
+            &.running {
+              color: var(--secondary-color);
+              background: rgba(7, 193, 96, 0.1);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .data-section {
+    background: white;
+    border-radius: var(--card-radius);
+    padding: 16px;
+    margin: 10px;
+    box-shadow: var(--shadow);
+
+    .section-header {
+      .header-main {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 12px;
+
+        .section-title {
+          position: relative;
+          margin: 0;
+          font-size: 16px;
+          color: var(--text-primary);
+          display: flex;
+          align-items: center;
+
+          .title-arrow {
+            margin-left: 6px;
+            font-size: 14px;
+            transition: transform 0.2s;
+          }
+
+          &:hover .title-arrow {
+            transform: translateX(2px);
+          }
+        }
+
+        .data-entry {
+          display: flex;
+          align-items: center;
+          color: var(--primary-color);
+          font-size: 13px;
+          cursor: pointer;
+          transition: all 0.2s;
+          padding: 4px 8px;
+          border-radius: 16px;
+          background: rgba(77, 106, 221, 0.05);
+
+          .entry-arrow {
+            margin-left: 4px;
+            font-size: 12px;
+            transition: transform 0.2s;
+          }
+        }
+      }
+    }
+
+    .data-cards {
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 12px;
+      margin-bottom: 20px;
+
+      .data-card {
+        padding: 15px;
+        border-radius: 8px;
+
+        &.income {
+          background: linear-gradient(135deg, var(--primary-color) 0%, #6B8CFF 100%);
+          color: white;
+        }
+
+        &.sales {
+          background: linear-gradient(135deg, #FFA26B 0%, #FF7B42 100%);
+          color: white;
+        }
+
+        .card-content {
+          display: flex;
+          align-items: center;
+
+          .card-text {
+            .value {
+              font-size: 18px;
+              font-weight: 600;
+              line-height: 1.2;
+            }
+
+            .label {
+              font-size: 13px;
+              opacity: 0.9;
+            }
+          }
+        }
+      }
+    }
+
+    .chart-container {
+      height: 300px;
+      background: white;
+      border-radius: 8px;
+
+      .sales-chart {
+        height: 270px;
+        width: 100%;
+      }
+    }
+  }
+
+  .tools-section {
+    padding: 10px 16px;
+    background: #fff;
+    border-radius: var(--card-radius);
+    margin: 10px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+
+    .section-title {
+      font-size: 16px;
+      color: #333;
+      // margin-bottom: 16px;
+    }
+
+    .tool-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      padding: 12px;
+      margin-bottom: 12px;
+      border-radius: 8px;
+      transition: all 0.2s;
+
+      &:active {
+        background: #f1f3f5;
+        transform: scale(0.98);
+      }
+
+      .tool-icon {
+        width: 48px;
+        height: 48px;
+        margin-bottom: 8px;
+        border-radius: 8px;
+      }
+
+      .tool-name {
+        font-size: 12px;
+        color: #666;
+        text-align: center;
+        line-height: 1.4;
+        display: -webkit-box;
+        -webkit-line-clamp: 2;
+        -webkit-box-orient: vertical;
+        overflow: hidden;
+      }
+    }
+
+    // 处理最后一行的边距
+    :deep(.van-row) {
+      margin-bottom: -12px;
+
+      .van-col {
+        margin-bottom: 12px;
+      }
+    }
+  }
+
+  .machine-sales {
+    margin: 10px;
+
+    .sales-list {
+      display: grid;
+      gap: 12px;
+
+      .sales-item {
+        background: white;
+        border-radius: var(--card-radius);
+        overflow: hidden;
+        box-shadow: var(--shadow);
+
+        .machine-header {
+          padding: 12px 16px;
+          font-weight: 500;
+          color: var(--text-primary);
+          border-bottom: 1px solid #eee;
+          display: flex;
+          align-items: center;
+
+          .machine-icon {
+            margin-right: 8px;
+            font-size: 18px;
+            color: var(--primary-color);
+          }
+        }
+
+        .sale-cell {
+          &:deep(.van-cell__title) {
+            flex: none;
+            width: 100px;
+          }
+
+          &:deep(.van-cell__value) {
+            color: var(--text-primary);
+            font-weight: 500;
+          }
+        }
+
+        .total-cell {
+          &:deep(.van-cell__value) {
+            color: var(--primary-color);
+            font-weight: 600;
+          }
+        }
+      }
+    }
+  }
+}
 </style>

文件差異過大導致無法顯示
+ 1103 - 0
src/views/orderCenter/index copy.vue


文件差異過大導致無法顯示
+ 1066 - 358
src/views/orderCenter/index.vue


文件差異過大導致無法顯示
+ 1095 - 0
src/views/user copy.vue


文件差異過大導致無法顯示
+ 801 - 298
src/views/user.vue