Browse Source

Merge branch 'dev' of xiongzhu/paimaide into master

熊竹 2 years ago
parent
commit
bcaf032818

+ 20 - 0
android/app/prod/release/output-metadata.json

@@ -0,0 +1,20 @@
+{
+  "version": 3,
+  "artifactType": {
+    "type": "APK",
+    "kind": "Directory"
+  },
+  "applicationId": "com.bigauction.mobile",
+  "variantName": "prodRelease",
+  "elements": [
+    {
+      "type": "SINGLE",
+      "filters": [],
+      "attributes": [],
+      "versionCode": 142,
+      "versionName": "1.0.0",
+      "outputFile": "app-prod-release.apk"
+    }
+  ],
+  "elementType": "File"
+}

BIN
android/app/src/main/assets/cdvasset.manifest


BIN
public/fonts/AlimamaShuHeiTi-Bold.ttf


+ 1 - 1
public/meta.js

@@ -1 +1 @@
-window.www_version = 1127
+window.www_version = 1128

+ 3 - 2
src/App.vue

@@ -227,6 +227,7 @@ async function open(item) {
 provide('showCS', () => {
     showCustmer.value = true
 })
+provide('promptLogin', promptLogin)
 </script>
 <style lang="less" scoped>
 .consult {
@@ -300,7 +301,7 @@ provide('showCS', () => {
                 color: #ffffff;
                 line-height: 24px;
                 // text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5);
-                .gradient-text(#ddffc1, #55f6ca);
+                .gradient-text(linear-gradient(180deg, #ddffc1 0%, #55f6ca 100%));
                 text-transform: uppercase;
                 .f-col();
                 align-items: center;
@@ -310,7 +311,7 @@ provide('showCS', () => {
                     color: #ffef33;
                     line-height: 24px;
                     // text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5);
-                    .gradient-text(#fff797, #ffef33);
+                    .gradient-text(linear-gradient(180deg, #fff797 0%, #ffef33 100%));
                     margin-top: 5px;
                 }
             }

BIN
src/assets/icon-anniu-home.png


BIN
src/assets/icon-anniu-not.png


BIN
src/assets/icon-anniu.png


BIN
src/assets/icon-huiyuan-not.png


BIN
src/assets/icon-huiyuan-user.png


BIN
src/assets/icon-huiyuan.png


BIN
src/assets/icon-kapian-not.png


BIN
src/assets/icon-kapian.png


BIN
src/assets/icon_gouxuan_pre1.png


BIN
src/assets/png-cishu.png


BIN
src/assets/png-huiyuan-bg-not.png


BIN
src/assets/png-huiyuan-bg.png


BIN
src/assets/png-huiyuan.png


+ 36 - 4
src/locales/en.json

@@ -73,10 +73,13 @@
         "amount": "Enter stake amount",
         "minAmount": "minimum stake amount: ",
         "time": "Choose a stake duration",
-        "promptAmount": "Please enter stake amount"
+        "promptAmount": "Please enter stake amount",
+        "sure": "",
+        "tips1": "",
+        "tips2": ""
     },
     "common": {
-        "alert": "Hint",
+        "alert": "Alert",
         "cancel": "Cancel",
         "close": "Close",
         "confirm": "Confirm",
@@ -110,7 +113,9 @@
         "customer": "Add your broker",
         "reward": "Reward",
         "official": "Official",
-        "broker": "Broker"
+        "broker": "Broker",
+        "error": "Some error occurred, please try again later",
+        "yes": "Yes"
     },
     "delegate": {
         "increase": "Increase",
@@ -119,7 +124,9 @@
         "sellPrice": "Selling Price",
         "tip": "The consignment has not started yet, it will start today at {time}",
         "tip1": "Note: entrusting the platform to sell services, the maximum price of the product can be increased by {riseRatePercent}% each time, and the platform will charge {serviceCharge}% of the custody service fee",
-        "title": "Consignment"
+        "title": "Consignment",
+        "tips2": "",
+        "tips3": ""
     },
     "distribution": {
         "commission": "Commission",
@@ -309,5 +316,30 @@
         "notLogin": "User is not logged in, do you want to log in now?",
         "profile": "Edit profile",
         "wallet": "My Wallet"
+    },
+    "vip": {
+        "auto": "automatic acquisition",
+        "btn": "Renew",
+        "btn1": "recharge",
+        "isOpen": "Turned on",
+        "rights": "Rights",
+        "time": "Expire date:",
+        "title": "valued member",
+        "isClose": "Closed",
+        "day": " Days",
+        "recharge": "Recharge member",
+        "tips": "Enjoy automatic acquisition rights",
+        "TopUp": "Recharge members enjoy benefits",
+        "expired": "membership has expired",
+        "lost": "Membership rights have been lost for {day} days",
+        "noVip": "no member",
+        "setAuto": "Set the interval for automatic purchase",
+        "dayTimes": "You can snap up {times} times a day",
+        "pla": "Please enter",
+        "to": "to",
+        "togglePrompt": "Are you sure you want to {status} automatic acquisition?",
+        "closed": "Closed successfully",
+        "errorInterval": "Please enter the correct interval",
+        "opened": "Opened successfully"
     }
 }

+ 311 - 310
src/locales/es.json

@@ -1,315 +1,316 @@
 {
-    "balance": {
-        "availableWidthdrawAmount": "Cantidad que se puede retirar",
-        "balance": "Saldo de la cuenta",
-        "chooseAmount": "seleccionar cantidad",
-        "confirmWithdraw": "Confirmar retiro",
-        "inputCustomAmount": "Ingrese una cantidad personalizada",
-        "realReceipt": "cuenta real",
-        "recharge": "recargar",
-        "rechargeMax": "El monto máximo de recarga es {value}",
-        "rechargeMin": "El monto mínimo de recarga es {value}",
-        "rechargeSuccess": "recarga exitosa",
-        "record": {
-            "bonus": "otorgar",
-            "commission": "comisión",
-            "pay": "pagar",
-            "receipt": "recolectar dinero",
-            "recharge": "recargar",
-            "redeem": "redención",
-            "return": "devolver",
-            "stake": "promesa",
-            "withdraw": "retirar"
-        },
-        "symbol": "₱",
-        "totalWithdraw": "Retiro acumulativo",
-        "unit": "Yuan",
-        "withdraw": "retirar",
-        "withdrawAmount": "Cantidad de retiro",
-        "withdrawAmountError": "Ingrese el monto de retiro correcto",
-        "withdrawFee": "Cargo por retiro",
-        "withdrawFeeTip": "Nota: 1/3 de la tarifa de transacción total de la compra es <span class=\"tarifa1\">{tarifa1}</span>, y la parte excedente es <span class=\"tarifa2\">{tarifa2}</span>",
-        "withdrawInputTip": "Por favor ingrese el monto del retiro",
-        "withdrawModalTitle": "Solicitar retiro",
-        "withdrawSuccess": "La solicitud de retiro ha sido enviada y se espera que llegue dentro de las 24 horas",
-        "withdrawing": "Retiro",
-        "insufficientBalance": "Saldo insuficiente",
-        "withdrawMin": "El monto mínimo de retiro es {value}",
-        "credits": "integral",
-        "totalRechargeAmount": "recarga acumulada"
+  "balance": {
+    "availableWidthdrawAmount": "Cantidad que se puede retirar",
+    "balance": "Saldo de la cuenta",
+    "chooseAmount": "seleccionar cantidad",
+    "confirmWithdraw": "Confirmar retiro",
+    "inputCustomAmount": "Ingrese una cantidad personalizada",
+    "realReceipt": "cuenta real",
+    "recharge": "recargar",
+    "rechargeMax": "El monto máximo de recarga es {value}",
+    "rechargeMin": "El monto mínimo de recarga es {value}",
+    "rechargeSuccess": "recarga exitosa",
+    "record": {
+      "bonus": "otorgar",
+      "commission": "comisión",
+      "pay": "pagar",
+      "receipt": "recolectar dinero",
+      "recharge": "recargar",
+      "redeem": "redención",
+      "return": "devolver",
+      "stake": "promesa",
+      "withdraw": "retirar"
     },
-    "bank": {
-        "account": "número de tarjeta bancaria",
-        "addBankCard": "añadir tarjeta bancaria",
-        "bank": "banco",
-        "deleteBankCard": "borrar tarjeta bancaria",
-        "deletePrompt": "¿Está seguro de eliminar esta tarjeta bancaria?",
-        "deleteSuccess": "eliminado con éxito",
-        "editBankCard": "editar tarjeta",
-        "invalidAccount": "Número de tarjeta bancaria no válido",
-        "invalidBank": "nombre de banco inválido",
-        "invalidCard": "Número de tarjeta bancaria no válido",
-        "invalidMail": "email inválido",
-        "invalidName": "Nombre del titular de la tarjeta no válido",
-        "invalidPhone": "numero de telefono invalido",
-        "mail": "Correo",
-        "name": "Nombre del tarjetahabiente",
-        "phone": "Número de teléfono",
-        "saveBankCard": "guardar tarjeta bancaria",
-        "selectBank": "elegir banco"
+    "symbol": "₱",
+    "totalWithdraw": "Retiro acumulativo",
+    "unit": "Yuan",
+    "withdraw": "retirar",
+    "withdrawAmount": "Cantidad de retiro",
+    "withdrawAmountError": "Ingrese el monto de retiro correcto",
+    "withdrawFee": "Cargo por retiro",
+    "withdrawFeeTip": "Nota: 1/3 de la tarifa de transacción total de la compra es <span class=\"tarifa1\">{tarifa1}</span>, y la parte excedente es <span class=\"tarifa2\">{tarifa2}</span>",
+    "withdrawInputTip": "Por favor ingrese el monto del retiro",
+    "withdrawModalTitle": "Solicitar retiro",
+    "withdrawSuccess": "La solicitud de retiro ha sido enviada y se espera que llegue dentro de las 24 horas",
+    "withdrawing": "Retiro",
+    "insufficientBalance": "Saldo insuficiente",
+    "withdrawMin": "El monto mínimo de retiro es {value}",
+    "credits": "integral",
+    "totalRechargeAmount": "recarga acumulada"
+  },
+  "bank": {
+    "account": "número de tarjeta bancaria",
+    "addBankCard": "añadir tarjeta bancaria",
+    "bank": "banco",
+    "deleteBankCard": "borrar tarjeta bancaria",
+    "deletePrompt": "¿Está seguro de eliminar esta tarjeta bancaria?",
+    "deleteSuccess": "eliminado con éxito",
+    "editBankCard": "editar tarjeta",
+    "invalidAccount": "Número de tarjeta bancaria no válido",
+    "invalidBank": "nombre de banco inválido",
+    "invalidCard": "Número de tarjeta bancaria no válido",
+    "invalidMail": "email inválido",
+    "invalidName": "Nombre del titular de la tarjeta no válido",
+    "invalidPhone": "numero de telefono invalido",
+    "mail": "Correo",
+    "name": "Nombre del tarjetahabiente",
+    "phone": "Número de teléfono",
+    "saveBankCard": "guardar tarjeta bancaria",
+    "selectBank": "elegir banco"
+  },
+  "blf": {
+    "buy": "agárrate ahora",
+    "buying": "absorbido, ingresos",
+    "computing": "Cálculo de ingresos",
+    "end": "encima",
+    "finish": "agotado",
+    "has": "Lugares restantes",
+    "not": "sin actividad todavía",
+    "pending": "Muy pronto",
+    "pic": "ilustrar",
+    "sucess": "compra exitosa",
+    "tips": "Prometa su saldo y disfrute de los dividendos de hoy a través de la relación de saldo de compromiso general de la plataforma, y ​​el ingreso químico diario más alto puede obtener 1%",
+    "amount": "Ingrese el monto de la promesa",
+    "minAmount": "oferta mínima",
+    "sure": "",
+    "time": "Elija un tiempo de compromiso",
+    "tips1": "",
+    "tips2": ""
+  },
+  "common": {
+    "alert": "pista",
+    "cancel": "Cancelar",
+    "close": "cierre",
+    "confirm": "Seguro",
+    "copied": "copiado",
+    "customerService": "Servicio de consejería",
+    "eula": "Acuerdo de plataforma",
+    "exitApp": "Presione de nuevo para salir de la aplicación",
+    "guide": "tutorial",
+    "home": "página delantera",
+    "loadFinish": "Carga completada",
+    "loading": "Cargando",
+    "mine": "mío",
+    "more": "Más",
+    "news": "noticias",
+    "noRecords": "No hay registros",
+    "notAvailable": "aún no abierto",
+    "ok": "DE ACUERDO",
+    "open": "Abierto",
+    "profit": "dividendos",
+    "pullRefresh": "Tire hacia abajo para refrescar",
+    "rank": "tabla de clasificación",
+    "register": "registro",
+    "save": "ahorrar",
+    "saveFailed": "Error al guardar",
+    "saveSuccess": "Guardado exitosamente",
+    "saving": "ahorro",
+    "serviceTime": "Horario de atención al cliente de 9:00 am a 9:00 pm",
+    "skip": "salte por encima",
+    "updateSuccess": "actualización completada",
+    "wait": "Manténganse al tanto",
+    "customer": "Atención al cliente exclusiva",
+    "reward": "otorgar",
+    "broker": "agente",
+    "official": "oficial"
+  },
+  "delegate": {
+    "increase": "margen",
+    "originalPrice": "precio original",
+    "payServiceCharge": "pagar tarifas de procesamiento",
+    "sellPrice": "precio de venta",
+    "tip": "La venta a consignación aún no ha comenzado, comenzará hoy a las {time}",
+    "tip1": "Nota: al confiar a la plataforma la venta de servicios, el precio máximo del producto puede incrementarse en un {riseRatePercent}% cada vez, y la plataforma cobrará un {serviceCharge}% de la tarifa del servicio de custodia.",
+    "title": "Envío",
+    "tips2": "",
+    "tips3": ""
+  },
+  "distribution": {
+    "commission": "comisión",
+    "copy": "Copiar",
+    "empty": "no todavía",
+    "invite": "invitar",
+    "joinTeamAt": "tiempo para unirse al equipo",
+    "myInvitor": "mi recomendador",
+    "myProfit": "mis ingresos",
+    "orderNum": "número de orden",
+    "profitDetails": "Detalles de ingresos",
+    "qrCode": "mi código de promoción",
+    "saveImg": "guardar foto",
+    "shareUrl": "Compartir enlace:",
+    "teamNum": "Tamaño del equipo",
+    "totalProfit": "Los ingresos totales",
+    "viewDetail": "ver detalles",
+    "viewTeam": "ver equipo"
+  },
+  "home": {
+    "all": "todo",
+    "trialPrompt": "Solo abierto a nuevos usuarios que se registren dentro de las 72 horas"
+  },
+  "loginPage": {
+    "agreement": "Acuerdo del Usuario",
+    "codePla": "por favor ingrese el código de verificación",
+    "enterUsername": "por favor ingrese el nombre de usuario",
+    "goRegister": "Regístrate ahora",
+    "invitorPla": "Código de invitación (opcional)",
+    "isRead": "he leído y estoy de acuerdo",
+    "login": "Acceso",
+    "loginByCode": "Código de verificación de inicio de sesión",
+    "loginNow": "Ya tienes una cuenta, inicia sesión ahora",
+    "loginPwd": "contraseña de acceso",
+    "loginSuceess": "Inicio de sesión correcto",
+    "nameError": "Longitud de cuenta 6-20, solo puede contener números, letras y guiones bajos, no números puros",
+    "noAcount": "¿No tienes cuenta aún?",
+    "noAgree": "Lea y acepte el acuerdo de registro",
+    "phone": "Número de teléfono",
+    "phoneError": "Formato incorrecto del número de teléfono",
+    "phonePla": "Por favor ingrese el número de teléfono",
+    "psd": "contraseña",
+    "psdAgainError": "Las dos contraseñas ingresadas son inconsistentes",
+    "psdAgainPla": "por favor ingrese la contraseña de nuevo",
+    "psdEorror": "Longitud de la contraseña 6-20, solo puede contener números, letras y guiones bajos, no números puros",
+    "psdPla": "Por favor, ingrese contraseña",
+    "register": "Registrar una cuenta",
+    "registerSuccess": "registración exitosa",
+    "sendCode": "Enviar el código de verificación",
+    "sended": "Ha sido enviado"
+  },
+  "mine": {
+    "address": "Dirección de envío",
+    "avatar": "avatar",
+    "bankCard": "mi tarjeta bancaria",
+    "bindPhone": "Vincular número de teléfono móvil",
+    "common": "Funciones comunes",
+    "female": "femenino",
+    "logout": "desconectar",
+    "male": "masculino",
+    "newProfit": "nuevos ingresos",
+    "nickPlac": "Por favor escribe un apodo",
+    "nickname": "Apodo",
+    "noBind": "sin consolidar",
+    "noLogin": "Sin iniciar sesión",
+    "noSetting": "no establecido",
+    "settings": "configuración",
+    "sex": "género",
+    "sure": "Confirmar los cambios",
+    "user": "usuario",
+    "username": "nombre de usuario"
+  },
+  "news": {
+    "newsNull": "No hay noticias ~"
+  },
+  "order": {
+    "applyShip": "Solicitar entrega",
+    "buyNow": "Compre ya",
+    "confirmReceipt": "confirmar la recepción de mercancías",
+    "countDown": "Cancelar automáticamente el pedido cuando expire la cuenta atrás del pago",
+    "createSuccess": "Pedido creado con éxito",
+    "createdAt": "tiempo de la orden",
+    "detail": "detalles del pedido",
+    "hasProblem": "¿Tienes problemas?",
+    "id": "número de orden",
+    "minute": "punto",
+    "my": "Mi pedido",
+    "payAt": "tiempo de pago",
+    "payInfo": "Información del pago",
+    "payMethod": "método de pago",
+    "payMethodName": {
+      "balance": "pago del saldo"
     },
-    "blf": {
-        "buy": "agárrate ahora",
-        "buying": "absorbido, ingresos",
-        "computing": "Cálculo de ingresos",
-        "end": "encima",
-        "finish": "agotado",
-        "has": "Lugares restantes",
-        "not": "sin actividad todavía",
-        "pending": "Muy pronto",
-        "pic": "ilustrar",
-        "sucess": "compra exitosa",
-        "tips": "Prometa su saldo y disfrute de los dividendos de hoy a través de la relación de saldo de compromiso general de la plataforma, y ​​el ingreso químico diario más alto puede obtener 1%",
-        "amount": "Ingrese el monto de la promesa",
-        "minAmount": "oferta mínima",
-        "sure": "Confirmar compromiso",
-        "time": "Elija un tiempo de compromiso",
-        "tips1": "Comprometa su saldo, disfrute de los dividendos de hoy a través de la relación de saldo de compromiso general de la plataforma y obtenga los ingresos químicos diarios más altos",
-        "tips2": "‰"
+    "payNow": "paga inmediatamente",
+    "paySuccess": "Pago exitoso",
+    "paying": "Pagos",
+    "processing": "Procesando",
+    "second": "Segundo",
+    "status": {
+      "ALL": "todo",
+      "CANCELED": "Cancelado",
+      "CONFIRMED": "ser confiado",
+      "NOT_CONFIRMED": "Esperando a que el vendedor confirme la recepción",
+      "NOT_PAID": "Pago pendiente",
+      "RECEIVED": "recibió",
+      "SELLING": "encomendado",
+      "SHIPPED": "Enviado",
+      "SOLD": "Vendido",
+      "SOLD_NOT_CONFIRMED": "A confirmar recibo",
+      "SOLD_NOT_PAID": "pendiente de pago del comprador",
+      "NOT_SHIPPED": "para ser entregado"
     },
-    "common": {
-        "alert": "pista",
-        "cancel": "Cancelar",
-        "close": "cierre",
-        "confirm": "Seguro",
-        "copied": "copiado",
-        "customerService": "Servicio de consejería",
-        "eula": "Acuerdo de plataforma",
-        "exitApp": "Presione de nuevo para salir de la aplicación",
-        "guide": "tutorial",
-        "home": "página delantera",
-        "loadFinish": "Carga completada",
-        "loading": "Cargando",
-        "mine": "mío",
-        "more": "Más",
-        "news": "noticias",
-        "noRecords": "No hay registros",
-        "notAvailable": "aún no abierto",
-        "ok": "DE ACUERDO",
-        "open": "Abierto",
-        "profit": "dividendos",
-        "pullRefresh": "Tire hacia abajo para refrescar",
-        "rank": "tabla de clasificación",
-        "register": "registro",
-        "save": "ahorrar",
-        "saveFailed": "Error al guardar",
-        "saveSuccess": "Guardado exitosamente",
-        "saving": "ahorro",
-        "serviceTime": "Horario de atención al cliente de 9:00 am a 9:00 pm",
-        "skip": "salte por encima",
-        "updateSuccess": "actualización completada",
-        "wait": "Manténganse al tanto",
-        "customer": "Atención al cliente exclusiva",
-        "reward": "otorgar",
-        "broker": "agente",
-        "official": "oficial"
+    "statusDesc": {
+      "CANCELED": "El pedido ha sido cancelado, si tiene alguna pregunta, comuníquese con el servicio al cliente",
+      "CONFIRMED": "Para ser confiado, se colocará en los estantes dentro de las 24 horas posteriores a la confianza.",
+      "NOT_CONFIRMED": "Esperando a que el vendedor confirme la recepción",
+      "NOT_PAID": "Pague lo antes posible, los pedidos de horas extras no pagadas se cancelarán automáticamente",
+      "NOT_SHIPPED": "para ser entregado",
+      "RECEIVED": "recibió",
+      "SELLING": "En encomienda, si tiene alguna pregunta, comuníquese con el servicio al cliente",
+      "SHIPPED": "Enviado",
+      "SOLD": "Vendido, póngase en contacto con el servicio de atención al cliente si tiene alguna pregunta.",
+      "SOLD_NOT_CONFIRMED": "A confirmar recibo",
+      "SOLD_NOT_PAID": "pendiente de pago del comprador"
     },
-    "delegate": {
-        "increase": "margen",
-        "originalPrice": "precio original",
-        "payServiceCharge": "pagar tarifas de procesamiento",
-        "sellPrice": "precio de venta",
-        "tip": "La venta a consignación aún no ha comenzado, comenzará hoy a las {time}",
-        "tip1": "Nota: al confiar a la plataforma la venta de servicios, el precio máximo del producto puede incrementarse en un {riseRatePercent}% cada vez, y la plataforma cobrará un {serviceCharge}% de la tarifa del servicio de custodia.",
-        "title": "Envío",
-        "tips2": "Los artículos enumerados se congelarán",
-        "tips3": "Hora"
-    },
-    "distribution": {
-        "commission": "comisión",
-        "copy": "Copiar",
-        "empty": "no todavía",
-        "invite": "invitar",
-        "joinTeamAt": "tiempo para unirse al equipo",
-        "myInvitor": "mi recomendador",
-        "myProfit": "mis ingresos",
-        "orderNum": "número de orden",
-        "profitDetails": "Detalles de ingresos",
-        "qrCode": "mi código de promoción",
-        "saveImg": "guardar foto",
-        "shareUrl": "Compartir enlace:",
-        "teamNum": "Tamaño del equipo",
-        "totalProfit": "Los ingresos totales",
-        "viewDetail": "ver detalles",
-        "viewTeam": "ver equipo"
-    },
-    "home": {
-        "all": "todo",
-        "trialPrompt": "Solo abierto a nuevos usuarios que se registren dentro de las 72 horas"
-    },
-    "loginPage": {
-        "agreement": "Acuerdo del Usuario",
-        "codePla": "por favor ingrese el código de verificación",
-        "enterUsername": "por favor ingrese el nombre de usuario",
-        "goRegister": "Regístrate ahora",
-        "invitorPla": "Código de invitación (opcional)",
-        "isRead": "he leído y estoy de acuerdo",
-        "login": "Acceso",
-        "loginByCode": "Código de verificación de inicio de sesión",
-        "loginNow": "Ya tienes una cuenta, inicia sesión ahora",
-        "loginPwd": "contraseña de acceso",
-        "loginSuceess": "Inicio de sesión correcto",
-        "nameError": "Longitud de cuenta 6-20, solo puede contener números, letras y guiones bajos, no números puros",
-        "noAcount": "¿No tienes cuenta aún?",
-        "noAgree": "Lea y acepte el acuerdo de registro",
-        "phone": "Número de teléfono",
-        "phoneError": "Formato incorrecto del número de teléfono",
-        "phonePla": "Por favor ingrese el número de teléfono",
-        "psd": "contraseña",
-        "psdAgainError": "Las dos contraseñas ingresadas son inconsistentes",
-        "psdAgainPla": "por favor ingrese la contraseña de nuevo",
-        "psdEorror": "Longitud de la contraseña 6-20, solo puede contener números, letras y guiones bajos, no números puros",
-        "psdPla": "Por favor, ingrese contraseña",
-        "register": "Registrar una cuenta",
-        "registerSuccess": "registración exitosa",
-        "sendCode": "Enviar el código de verificación",
-        "sended": "Ha sido enviado"
-    },
-    "mine": {
-        "address": "Dirección de envío",
-        "avatar": "avatar",
-        "bankCard": "mi tarjeta bancaria",
-        "bindPhone": "Vincular número de teléfono móvil",
-        "common": "Funciones comunes",
-        "female": "femenino",
-        "logout": "desconectar",
-        "male": "masculino",
-        "newProfit": "nuevos ingresos",
-        "nickPlac": "Por favor escribe un apodo",
-        "nickname": "Apodo",
-        "noBind": "sin consolidar",
-        "noLogin": "Sin iniciar sesión",
-        "noSetting": "no establecido",
-        "settings": "configuración",
-        "sex": "género",
-        "sure": "Confirmar los cambios",
-        "user": "usuario",
-        "username": "nombre de usuario"
-    },
-    "news": {
-        "newsNull": "No hay noticias ~"
-    },
-    "order": {
-        "applyShip": "Solicitar entrega",
-        "buyNow": "Compre ya",
-        "confirmReceipt": "confirmar la recepción de mercancías",
-        "countDown": "Cancelar automáticamente el pedido cuando expire la cuenta atrás del pago",
-        "createSuccess": "Pedido creado con éxito",
-        "createdAt": "tiempo de la orden",
-        "detail": "detalles del pedido",
-        "hasProblem": "¿Tienes problemas?",
-        "id": "número de orden",
-        "minute": "punto",
-        "my": "Mi pedido",
-        "payAt": "tiempo de pago",
-        "payInfo": "Información del pago",
-        "payMethod": "método de pago",
-        "payMethodName": {
-            "balance": "pago del saldo"
-        },
-        "payNow": "paga inmediatamente",
-        "paySuccess": "Pago exitoso",
-        "paying": "Pagos",
-        "processing": "Procesando",
-        "second": "Segundo",
-        "status": {
-            "ALL": "todo",
-            "CANCELED": "Cancelado",
-            "CONFIRMED": "ser confiado",
-            "NOT_CONFIRMED": "Esperando a que el vendedor confirme la recepción",
-            "NOT_PAID": "Pago pendiente",
-            "RECEIVED": "recibió",
-            "SELLING": "encomendado",
-            "SHIPPED": "Enviado",
-            "SOLD": "Vendido",
-            "SOLD_NOT_CONFIRMED": "A confirmar recibo",
-            "SOLD_NOT_PAID": "pendiente de pago del comprador",
-            "NOT_SHIPPED": "para ser entregado"
-        },
-        "statusDesc": {
-            "CANCELED": "El pedido ha sido cancelado, si tiene alguna pregunta, comuníquese con el servicio al cliente",
-            "CONFIRMED": "Para ser confiado, se colocará en los estantes dentro de las 24 horas posteriores a la confianza.",
-            "NOT_CONFIRMED": "Esperando a que el vendedor confirme la recepción",
-            "NOT_PAID": "Pague lo antes posible, los pedidos de horas extras no pagadas se cancelarán automáticamente",
-            "NOT_SHIPPED": "para ser entregado",
-            "RECEIVED": "recibió",
-            "SELLING": "En encomienda, si tiene alguna pregunta, comuníquese con el servicio al cliente",
-            "SHIPPED": "Enviado",
-            "SOLD": "Vendido, póngase en contacto con el servicio de atención al cliente si tiene alguna pregunta.",
-            "SOLD_NOT_CONFIRMED": "A confirmar recibo",
-            "SOLD_NOT_PAID": "pendiente de pago del comprador"
-        },
-        "stopSale": "Suspensión de venta",
-        "total": "total",
-        "totalPayment": "pago real",
-        "viewOrder": "revisar orden",
-        "walletPay": "pago de billetera"
-    },
-    "product": {
-        "dailyEarning": "Ingreso diario",
-        "detail": "Detalles de producto",
-        "hot": "Caliente rompiendo…",
-        "nowPrice": "Precio actual",
-        "owner": "titular actual",
-        "priceNow": "precio actual",
-        "riseDesc": "Ingreso diario",
-        "search": "buscar",
-        "searchNo": "No se encontraron productos ~",
-        "searchPla": "Ingrese las palabras clave de búsqueda",
-        "start": "{hora} abierto",
-        "tag": "ilustraciones digitales",
-        "tomorrowBuy": "Disponible mañana",
-        "delayTips": "Esta colección se está enfriando, ¡puedes ir a una sesión superior para verla!",
-        "soldOut": "Agotado"
-    },
-    "rank": {
-        "inviteNum": "nuevos reclutas",
-        "profit": "ingreso",
-        "profitInfo": "Información de ganancias",
-        "rank": "clasificación",
-        "rankByInvite": "sacar nueva clasificación",
-        "rankByProfit": "ranking de ingresos",
-        "stake": "promesa",
-        "userInfo": "usuario",
-        "withdrawRechargeRate": "Ratio de ingresos por recarga"
-    },
-    "settings": {
-        "checkUpdate": "Buscar actualizaciones",
-        "checkingUpdate": "Comprobando actualizaciones...",
-        "darkMode": "modo oscuro",
-        "upToDate": "Actualmente la última versión",
-        "updating": "Actualizando..."
-    },
-    "title": {
-        "balanceRecord": "Detalles de la transacción",
-        "commissionRecords": "registro de comisiones",
-        "distribution": "centro de ingresos",
-        "myTeam": "mi equipo"
-    },
-    "tutorial": {
-        "step1": "Paso 1: escanee el código QR del recomendador, complete la información de registro y haga clic en registrarse para completar el registro",
-        "step2": "Paso 2: después del registro, guarde el código QR y compártalo con más usuarios para registrarse",
-        "step3": "Paso 3: inicie sesión en la aplicación, haga clic en el monto de recarga de la billetera",
-        "step4": "Paso 4: Selecciona o ingresa el monto a recargar por recarga",
-        "step5": "Paso 5: seleccione la sesión, haga clic en inicio y seleccione la sesión inicial. Cada usuario de la sesión principal tiene un límite de dos colecciones por día, la sesión intermedia está abierta de 4:00 p. m. a 5:00 p. La sesión avanzada está abierta de 3:00 p. m. a 4:00 p. m. todos los días.",
-        "step6": "Paso 6: Compre colecciones, si el saldo de la billetera es insuficiente, debe recargar el saldo de la billetera",
-        "step7": "Paso 7: Al confiar la colección para colocarla en los estantes en el pedido, debe pagar a la plataforma una comisión del 4%, y el rango de recolección correspondiente a la colección es del 6% del tiempo de compra.",
-        "step8": "Paso 8: si la cantidad es suficiente, puede comprar directamente la colección y luego confiarla para que se coloque en el estante",
-        "step9": "Nota: después de comprar 2 piezas, el campo principal no puede comprar colecciones la próxima vez, y cada usuario está limitado a comprar 2 piezas por día"
-    },
-    "user": {
-        "notLogin": "El usuario no ha iniciado sesión, ¿quieres iniciar sesión ahora?",
-        "profile": "editar informacion",
-        "wallet": "My bolso"
-    }
-}
+    "stopSale": "Suspensión de venta",
+    "total": "total",
+    "totalPayment": "pago real",
+    "viewOrder": "revisar orden",
+    "walletPay": "pago de billetera"
+  },
+  "product": {
+    "dailyEarning": "Ingreso diario",
+    "detail": "Detalles de producto",
+    "hot": "Caliente rompiendo…",
+    "nowPrice": "Precio actual",
+    "owner": "titular actual",
+    "priceNow": "precio actual",
+    "riseDesc": "Ingreso diario",
+    "search": "buscar",
+    "searchNo": "No se encontraron productos ~",
+    "searchPla": "Ingrese las palabras clave de búsqueda",
+    "start": "{hora} abierto",
+    "tag": "ilustraciones digitales",
+    "tomorrowBuy": "Disponible mañana",
+    "delayTips": "Esta colección se está enfriando, ¡puedes ir a una sesión superior para verla!",
+    "soldOut": "Agotado"
+  },
+  "rank": {
+    "inviteNum": "nuevos reclutas",
+    "profit": "ingreso",
+    "profitInfo": "Información de ganancias",
+    "rank": "clasificación",
+    "rankByInvite": "sacar nueva clasificación",
+    "rankByProfit": "ranking de ingresos",
+    "stake": "promesa",
+    "userInfo": "usuario",
+    "withdrawRechargeRate": "Ratio de ingresos por recarga"
+  },
+  "settings": {
+    "checkUpdate": "Buscar actualizaciones",
+    "checkingUpdate": "Comprobando actualizaciones...",
+    "darkMode": "modo oscuro",
+    "upToDate": "Actualmente la última versión",
+    "updating": "Actualizando..."
+  },
+  "title": {
+    "balanceRecord": "Detalles de la transacción",
+    "commissionRecords": "registro de comisiones",
+    "distribution": "centro de ingresos",
+    "myTeam": "mi equipo"
+  },
+  "tutorial": {
+    "step1": "Paso 1: escanee el código QR del recomendador, complete la información de registro y haga clic en registrarse para completar el registro",
+    "step2": "Paso 2: después del registro, guarde el código QR y compártalo con más usuarios para registrarse",
+    "step3": "Paso 3: inicie sesión en la aplicación, haga clic en el monto de recarga de la billetera",
+    "step4": "Paso 4: Selecciona o ingresa el monto a recargar por recarga",
+    "step5": "Paso 5: seleccione la sesión, haga clic en inicio y seleccione la sesión inicial. Cada usuario de la sesión principal tiene un límite de dos colecciones por día, la sesión intermedia está abierta de 4:00 p. m. a 5:00 p. La sesión avanzada está abierta de 3:00 p. m. a 4:00 p. m. todos los días.",
+    "step6": "Paso 6: Compre colecciones, si el saldo de la billetera es insuficiente, debe recargar el saldo de la billetera",
+    "step7": "Paso 7: Al confiar la colección para colocarla en los estantes en el pedido, debe pagar a la plataforma una comisión del 4%, y el rango de recolección correspondiente a la colección es del 6% del tiempo de compra.",
+    "step8": "Paso 8: si la cantidad es suficiente, puede comprar directamente la colección y luego confiarla para que se coloque en el estante",
+    "step9": "Nota: después de comprar 2 piezas, el campo principal no puede comprar colecciones la próxima vez, y cada usuario está limitado a comprar 2 piezas por día"
+  },
+  "user": {
+    "notLogin": "El usuario no ha iniciado sesión, ¿quieres iniciar sesión ahora?",
+    "profile": "editar informacion",
+    "wallet": "My bolso"
+  },
+  "vip": {}
+}

+ 28 - 1
src/locales/zh.json

@@ -110,7 +110,9 @@
         "customer": "专属客服",
         "reward": "奖励",
         "official": "官方",
-        "broker": "代理人"
+        "broker": "代理人",
+        "error": "发生错误,请稍后再试",
+        "yes": "是的"
     },
     "delegate": {
         "increase": "加价",
@@ -192,6 +194,31 @@
         "user": "用户",
         "username": "用户名"
     },
+    "vip": {
+        "title": "尊贵的会员",
+        "time": "到期时间:",
+        "btn": "续费",
+        "btn1": "充值",
+        "rights": "权益",
+        "auto": "自动收购",
+        "isOpen": "已开启",
+        "isClose": "未开启",
+        "recharge": "充值会员",
+        "tips": "享受自动收购权益",
+        "day": "天",
+        "noVip": "无会员",
+        "expired": "会员已过期",
+        "TopUp": "充值会员享权益",
+        "lost": "已失去会员权益{day}天",
+        "setAuto": "设置自动购买的区间",
+        "dayTimes": "每日最多可抢购{times}次",
+        "pla": "请输入",
+        "to": "至",
+        "togglePrompt": "确认要{status}自动收购吗?",
+        "closeSu": "关闭成功",
+        "errorInterval": "请输入正确区间",
+        "openSu": "开启成功"
+    },
     "news": {
         "newsNull": "没有任何新闻哦~"
     },

+ 5 - 1
src/stores/system.js

@@ -7,6 +7,7 @@ export const useSystemStore = defineStore('system', () => {
     const riseRate = ref(0)
     const platformCommission = ref(0)
     const saleBatches = ref([])
+    const vipEnabled = ref(false)
 
     const riseRatePercent = computed(() => {
         return riseRate.value * 100
@@ -22,7 +23,10 @@ export const useSystemStore = defineStore('system', () => {
         retry(() => http.post('/saleBatch/all', { size: 10000 }, { body: 'json' })).then(res => {
             saleBatches.value = res.content
         })
+        retry(() => http.get('/sysConfig/get/vip_enabled')).then(res => {
+            vipEnabled.value = res.value == '1'
+        })
     }
 
-    return { riseRate, riseRatePercent, platformCommission, saleBatches, getSysConfigs }
+    return { riseRate, riseRatePercent, platformCommission, saleBatches, getSysConfigs, vipEnabled }
 })

+ 2 - 2
src/styles/common.less

@@ -26,8 +26,8 @@
     -webkit-line-clamp: @line;
     -webkit-box-orient: vertical;
 }
-.gradient-text(@color1, @color2, @deg : 180deg) {
-    background: linear-gradient(@deg, @color1 0%, @color2 100%);
+.gradient-text(@gd) {
+    background: @gd;
     -webkit-background-clip: text;
     background-clip: text;
     -webkit-text-fill-color: transparent;

+ 76 - 2
src/views/HomePage.vue

@@ -158,6 +158,18 @@
                 </div>
             </template>
 
+            <div class="vip-content" v-if="isVip">
+                <img class="vip-bg" src="@/assets/png-cishu.png" alt="" />
+                <div class="vip-box">
+                    <img class="icon" src="@/assets/png-huiyuan.png" alt="" />
+                    <div class="num">
+                        <img src="@/assets/icon-anniu.png" alt="" />
+                        <span>{{ vipUserInfo.validityPeriod || 0 }}</span>
+                    </div>
+                    <span>times left</span>
+                </div>
+            </div>
+
             <div class="tools">
                 <!-- <div class="tool">
                     <img src="../assets/home_icon_pingtaixieyi.png" alt="" />
@@ -198,9 +210,13 @@ import { useUserStore } from '@/stores/user'
 import { showVideoModal } from '@/components/videoModal'
 import NewsModal from '@/components/NewsModal.vue'
 import { UseElementBounding } from '@vueuse/components'
+import { onIonViewWillEnter } from '@ionic/vue'
+import { storeToRefs } from 'pinia'
 
+const promptLogin = inject('promptLogin')
 const router = useRouter()
 const i18n = useI18n()
+const { user } = storeToRefs(useUserStore())
 
 const topBanners = ref([])
 function getBanner() {
@@ -282,6 +298,21 @@ const refresh = () => {
     getSaleBatch()
     getNews()
 }
+
+const isVip = computed(() => {
+    return vipUserInfo.value && vipUserInfo.value.status === 'EFFECTIVE'
+})
+const vipUserInfo = ref({})
+function getMineVip() {
+    if (user.value == null) return
+    http.get('/vip/mine').then(res => {
+        vipUserInfo.value = res
+    })
+}
+onIonViewWillEnter(() => {
+    getMineVip()
+})
+
 const turtorialShown = useStorage('turtorialShown', false)
 const newsShown = useStorage('newsShown', null)
 const packageSynced = inject('packageSynced', false)
@@ -311,9 +342,8 @@ onMounted(async () => {
 
 const goList = info => {
     if (info.id == 11547) {
-        const { user } = useUserStore()
         if (!user) {
-            emitter.emit('promptLogin')
+            promptLogin()
             return
         }
         if (user && isAfter(parse(user.createdAt, 'yyyy-MM-dd HH:mm:ss', new Date()), addDays(new Date(), -3))) {
@@ -740,4 +770,48 @@ ion-content {
         transform: translateY(-50%);
     }
 }
+
+.vip-content {
+    position: absolute;
+    left: 125px;
+    top: calc(var(--ion-safe-area-top) + 380px);
+    .vip-bg {
+        width: 120px;
+        height: 28px;
+    }
+    span {
+        font-size: 11px;
+        font-weight: bold;
+        color: #0044b4;
+        line-height: 12px;
+    }
+    .vip-box {
+        position: absolute;
+        top: 3px;
+        left: 3px;
+        right: 0;
+        height: 22px;
+        .f();
+        .icon {
+            width: 18px;
+            height: 18px;
+        }
+
+        .num {
+            position: relative;
+            margin: 0 4px 0 3px;
+            img {
+                width: 28px;
+                height: 18px;
+                display: block;
+            }
+            span {
+                position: absolute;
+                left: 50%;
+                top: 50%;
+                transform: translate(-50%, -50%);
+            }
+        }
+    }
+}
 </style>

+ 722 - 8
src/views/MinePage.vue

@@ -4,7 +4,10 @@
             <div class="user-top">
                 <img class="userInfo-bg" src="../assets/userBg.png" alt="" />
                 <div class="userInfo" v-if="user" @click="goSetting">
-                    <van-image width="60" height="60" radius="60" :src="user.avatar"></van-image>
+                    <div class="avatar">
+                        <van-image width="60" height="60" radius="60" :src="user.avatar"></van-image>
+                        <img v-if="isVip" src="@/assets/icon-huiyuan-user.png" alt="" class="vip-icon" />
+                    </div>
 
                     <div class="name">
                         <span>{{ user.nickname }}</span>
@@ -131,6 +134,46 @@
                 </van-cell>
             </van-cell-group> -->
 
+            <div class="vip-box" v-if="(userVip && userVip.autoTradeEnabled) || typeList.length > 0">
+                <img v-if="isVip" src="@/assets/png-huiyuan-bg.png" alt="" class="bg" />
+                <img v-else src="@/assets/png-huiyuan-bg-not.png" alt="" class="bg" />
+                <div class="vip-content">
+                    <div class="vip-top">
+                        <img v-if="isVip" src="@/assets/icon-huiyuan.png" alt="" class="icon" />
+                        <img v-else src="@/assets/icon-huiyuan-not.png" alt="" class="icon" />
+                        <div class="vip-info">
+                            <div class="text1">{{ vipTitle }}</div>
+                            <div class="text2">{{ vipTime }}</div>
+                        </div>
+                        <div class="btn" @click="onVipPurchase">
+                            <img v-if="isVip" src="@/assets/icon-anniu.png" alt="" />
+                            <img v-else src="@/assets/icon-anniu-not.png" alt="" />
+                            <span>{{ isVip ? $t('vip.btn') : $t('balance.recharge') }}</span>
+                        </div>
+                    </div>
+                    <div class="vip-bottom">
+                        <div class="btn">
+                            <img v-if="isVip" src="@/assets/icon-kapian.png" alt="" />
+                            <img v-else src="@/assets/icon-kapian-not.png" alt="" />
+                            <span>{{ $t('vip.rights') }}</span>
+                        </div>
+                        <div class="tips">
+                            {{ $t('vip.auto') }}
+                            <span
+                                >({{
+                                    userVip && userVip.autoTradeEnabled ? $t('vip.isOpen') : $t('vip.isClose')
+                                }})</span
+                            >
+                        </div>
+                        <van-switch
+                            @click="toggleAutoTrade"
+                            :disabled="!isVip"
+                            :model-value="userVip && userVip.autoTradeEnabled"
+                        />
+                    </div>
+                </div>
+            </div>
+
             <van-cell-group class="menu" :border="false">
                 <van-cell :title="$t('user.wallet')" is-link :to="{ name: 'wallet' }" :border="false">
                     <template #icon>
@@ -180,24 +223,116 @@
                     </template>
                 </van-cell>
             </van-cell-group>
+
+            <ion-modal
+                id="modal-recharge"
+                class="modal"
+                :is-open="showVipPurchaseModal"
+                :initial-breakpoint="breakpoint"
+                :breakpoints="[0, breakpoint]"
+                @didDismiss="showVipPurchaseModal = false"
+            >
+                <ion-content class="modal-content" :style="{ height: `${breakpoint * 100}%` }">
+                    <div class="modal-box">
+                        <div class="bg"></div>
+                        <div class="modal-page">
+                            <div class="head">
+                                <div class="title">
+                                    <div class="text1">{{ $t('vip.recharge') }}</div>
+                                    <div class="text2">{{ $t('vip.tips') }}</div>
+                                </div>
+                                <div class="close" @click="showVipPurchaseModal = false">
+                                    <img src="@/assets/icon_close2.png" alt="" />
+                                </div>
+                            </div>
+                            <van-tabs v-model:active="chooseType" @change="getVipConfig">
+                                <van-tab
+                                    :title="item.name"
+                                    :name="item.id"
+                                    v-for="(item, index) in typeList"
+                                    :key="index"
+                                >
+                                </van-tab>
+                            </van-tabs>
+                            <div class="chooseBtns" :class="{ 'large-btn': vipList.length <= 3 }">
+                                <div
+                                    class="choose-btn"
+                                    :class="{ prim: chooseVip === item.id }"
+                                    v-for="(item, index) in vipList"
+                                    :key="index"
+                                    @click="chooseVip = item.id"
+                                >
+                                    <div class="choose-btn-bg">
+                                        <div class="text1">{{ item.validityPeriod }}{{ $t('vip.day') }}</div>
+                                        <div class="text2">
+                                            <small>{{ $t('balance.symbol') }}</small
+                                            >{{ item.price }}
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+
+                            <div class="wallet">
+                                <img class="icon" src="@/assets/info_icon_qianbao.png" alt="" />
+                                <span>{{ $t('order.walletPay') }}</span>
+                                <img class="next" src="@/assets/icon_gouxuan_pre1.png" alt="" />
+                            </div>
+
+                            <div class="footer">
+                                <van-button type="primary" block @click="recharge">{{ $t('order.payNow') }}</van-button>
+                            </div>
+                        </div>
+                    </div>
+                </ion-content>
+            </ion-modal>
+
+            <ion-modal class="stake-modal" :is-open="showSetModal" @didDismiss="showSetModal = false">
+                <div class="stake-modal-wrapper">
+                    <div class="stake-modal-content">
+                        <div class="title">
+                            <div class="text1">{{ $t('vip.setAuto') }}</div>
+                            <div class="text2">{{ $t('vip.dayTimes', { times: maxNumber }) }}</div>
+                        </div>
+
+                        <div class="close" @click="showSetModal = false">
+                            <img src="@/assets/icon_close2.png" alt="" />
+                        </div>
+                        <div class="fileds">
+                            <van-field v-model="maxPrice" type="digit" :placeholder="$t('vip.pla')" />
+                            <span class="fileds-text"> {{ $t('vip.to') }} </span>
+                            <van-field v-model="minPrice" type="digit" :placeholder="$t('vip.pla')" />
+                        </div>
+                        <div class="footer">
+                            <van-button type="primary" block @click="confirmInterval">{{
+                                $t('common.confirm')
+                            }}</van-button>
+                        </div>
+                    </div>
+                </div>
+            </ion-modal>
         </ion-content>
     </ion-page>
 </template>
 
 <script setup>
-import { computed } from 'vue'
+import { computed, ref, watch, inject } from 'vue'
 import { useUserStore } from '@/stores/user'
 import { useIonRouter } from '@ionic/vue'
 import defaultAvatar from '@/assets/png_moren.png'
-import { ref, watch } from 'vue'
 import { http } from '@/plugins/http'
 import { onIonViewWillEnter } from '@ionic/vue'
 import intoIcon from '@/assets/svgs/icon_inter.svg'
+import { useWindowSize } from '@vueuse/core'
+import { useI18n } from 'vue-i18n'
+import { differenceInDays, format } from 'date-fns'
+import Toast from '@/utils/toast'
+import { showConfirmDialog } from 'vant'
+import { storeToRefs } from 'pinia'
+import { useSystemStore } from '@/stores/system'
 
-const userStore = useUserStore()
-const user = computed(() => {
-    return userStore.user
-})
+const promptLogin = inject('promptLogin')
+const { user } = storeToRefs(useUserStore())
+const { vipEnabled } = storeToRefs(useSystemStore())
 
 const router = useIonRouter()
 const goLogin = () => {
@@ -214,15 +349,17 @@ function getBalance() {
         balance.value = res
     })
 }
-watch(user, val => {
+watch([user, vipEnabled], ([val]) => {
     if (val) {
         getBalance()
+        getVipType()
     } else {
         balance.value = {}
     }
 })
 onIonViewWillEnter(() => {
     getBalance()
+    getVipType()
 })
 const balanceText = computed(() => {
     if (balance.value && balance.value.balance != undefined) {
@@ -230,6 +367,171 @@ const balanceText = computed(() => {
     }
     return '-'
 })
+
+const showVipPurchaseModal = ref(false)
+const { height: windowHeight } = useWindowSize()
+const breakpoint = computed(() => {
+    return Number((530 / windowHeight.value).toFixed(2))
+})
+
+const vipList = ref([])
+const typeList = ref([])
+const chooseType = ref(null)
+const chooseVip = ref(null)
+
+const userVip = ref(null)
+function getVipType() {
+    if (vipEnabled.value == false) return
+    http.post('/vip/type/all', { query: { enabled: true } }, { body: 'json' }).then(res => {
+        typeList.value = res
+        if (res.length > 0) {
+            chooseType.value = res[0].id
+        }
+        getVipConfig()
+    })
+    getMineVip()
+}
+
+function getVipConfig() {
+    if (!chooseType.value) return
+    http.post('/vip/config/all', { query: { typeId: chooseType.value } }, { body: 'json' }).then(res => {
+        vipList.value = res
+        if (res.length > 0) {
+            chooseVip.value = res[0].id
+        }
+    })
+}
+function onVipPurchase() {
+    if (!user.value) {
+        promptLogin()
+        return
+    }
+    showVipPurchaseModal.value = true
+}
+function getMineVip() {
+    if (!user.value) {
+        userVip.value = null
+        return
+    }
+    http.get('/vip/mine').then(res => {
+        userVip.value = res
+        maxPrice.value = res.maxPrice
+        minPrice.value = res.minPrice
+        maxNumber.value = res.dailyFrequency
+    })
+}
+
+function recharge() {
+    Toast.loading({
+        message: t('common.loading') + '...',
+        forbidClick: true
+    })
+    http.post('/vip/purchaseVip', {
+        typeId: chooseType.value,
+        configId: chooseVip.value,
+        num: 1
+    })
+        .then(res => {
+            Toast.clear()
+            showVipPurchaseModal.value = false
+            getBalance()
+            getVipType()
+            Toast.success(t('blf.sucess'))
+        })
+        .catch(e => {
+            if (e.error) {
+                Toast(e.error)
+            }
+        })
+}
+
+const isVip = computed(() => {
+    return userVip.value && userVip.value.expired === false
+})
+const { t } = useI18n()
+const vipTitle = computed(() => {
+    if (isVip.value) {
+        return t('vip.title')
+    } else {
+        if (userVip.value) {
+            return t('vip.expired')
+        } else {
+            return t('vip.noVip')
+        }
+    }
+})
+const vipTime = computed(() => {
+    if (isVip.value) {
+        return t('vip.time') + ' ' + format(new Date(userVip.value.expireAt), 'yyyy-MM-dd', new Date())
+    } else {
+        if (userVip.value) {
+            let day = differenceInDays(new Date(), new Date(userVip.value.expireAt))
+            return t('vip.lost', { day: day })
+        } else {
+            return t('vip.TopUp')
+        }
+    }
+})
+
+const showSetModal = ref(false)
+const maxNumber = ref(0)
+const maxPrice = ref(0)
+const minPrice = ref(0)
+
+async function toggleAutoTrade() {
+    if (!isVip.value) {
+        return
+    }
+    try {
+        await showConfirmDialog({
+            title: t('common.alert'),
+            message: t('vip.togglePrompt', {
+                status: userVip.value.autoTradeEnabled ? t('common.close') : t('common.open')
+            }),
+            confirmButtonText: t('common.yes')
+        })
+        Toast.loading({
+            message: t('common.loading') + '...',
+            forbidClick: true
+        })
+        await http.post(userVip.value.autoTradeEnabled ? '/vip/disableAutoTrade' : '/vip/enableAutoTrade', {
+            minPrice: minPrice.value,
+            maxPrice: maxPrice.value
+        })
+        Toast.success(userVip.value.autoTradeEnabled ? t('vip.closed') : t('vip.opened'))
+        getMineVip()
+    } catch (e) {
+        if (e == 'cancel') return
+        Toast.error(e?.error || t('common.error'))
+    }
+}
+
+function confirmInterval() {
+    if (Number(minPrice.value) <= Number(maxPrice.value)) {
+        Toast('请输入正确区间')
+        return
+    }
+    Toast.loading({
+        message: t('common.loading') + '...',
+        forbidClick: true
+    })
+    http.post('/vip/enableAutoTrade', {
+        userId: user.value.id,
+        maxPrice: maxPrice.value,
+        minPrice: minPrice.value
+    })
+        .then(() => {
+            showSetModal.value = false
+            Toast.success(t('vip.opened'))
+            getMineVip()
+        })
+        .catch(e => {
+            Toast.clear()
+            if (e && e.error) {
+                Toast.success(e.error)
+            }
+        })
+}
 </script>
 
 <style lang="less" scoped>
@@ -336,6 +638,17 @@ ion-content {
             border-radius: 60px;
         }
     }
+    .avatar {
+        position: relative;
+    }
+    .vip-icon {
+        width: 24px;
+        height: 24px;
+        position: absolute;
+        bottom: 0;
+        right: 0;
+        z-index: 20;
+    }
     .icon {
         width: 15px;
         height: 15px;
@@ -484,4 +797,405 @@ ion-content {
     height: 24px;
     color: #9fbfb1;
 }
+
+.vip-box {
+    padding: 0 20px 20px 18px;
+    position: relative;
+    .bg {
+        width: 100%;
+        height: 100px;
+        display: block;
+    }
+
+    .vip-content {
+        position: absolute;
+        top: 0;
+        left: 20px;
+        right: 20px;
+        bottom: 20px;
+    }
+
+    .vip-top {
+        .f();
+        padding: 7px 10px;
+        overflow: hidden;
+        .icon {
+            width: 46px;
+            height: 46px;
+        }
+
+        .vip-info {
+            flex-grow: 1;
+            padding: 0 10px;
+            overflow: hidden;
+            .text1 {
+                font-size: 14px;
+                font-family: TsangerYuMo;
+                color: #0044b4;
+                line-height: 24px;
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;
+            }
+
+            .text2 {
+                font-size: 12px;
+                color: #0044b4;
+                line-height: 20px;
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;
+            }
+        }
+
+        .btn {
+            position: relative;
+            flex-shrink: 0;
+            img {
+                width: 82px;
+                display: block;
+            }
+            span {
+                font-size: 12px;
+                font-family: TsangerYuMo;
+                color: #0044b4;
+                line-height: 24px;
+                position: absolute;
+                top: 50%;
+                left: 50%;
+                transform: translate(-50%, -50%);
+                text-transform: uppercase;
+            }
+        }
+    }
+
+    .vip-bottom {
+        .f();
+        padding: 6px 8px;
+        .btn {
+            position: relative;
+            img {
+                width: 50px;
+                display: block;
+            }
+            span {
+                font-size: 10px;
+                font-weight: bold;
+                color: #0044b4;
+                line-height: 10px;
+                position: absolute;
+                top: 50%;
+                left: 50%;
+                transform: translate(-50%, -50%);
+            }
+        }
+
+        .tips {
+            flex-grow: 1;
+            font-size: 12px;
+            font-weight: bold;
+            color: #0044b4;
+            line-height: 24px;
+            padding: 0 8px;
+            span {
+                font-size: 10px;
+            }
+        }
+
+        .van-switch {
+            --van-switch-on-background: #0044b4;
+            --van-switch-background: rgba(0, 68, 180, 0.4);
+            --van-switch-width: 56px;
+            --van-switch-height: 20px;
+            --van-switch-node-size: 26px;
+            border: 1px solid #ffffff;
+            :deep(&.van-switch--on) {
+                border-color: #0044b4;
+            }
+            :deep(.van-switch__node) {
+                height: 16px;
+                border-radius: 16px;
+            }
+        }
+    }
+}
+
+.modal-box {
+    border-radius: 16px 16px 0px 0px;
+    position: relative;
+    padding-top: 2px;
+    overflow: hidden;
+    height: 100%;
+    .bg {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        z-index: 0;
+        height: 16px;
+        background: linear-gradient(180deg, rgba(209, 254, 204, 1), rgba(77, 245, 190, 0) 172px);
+    }
+
+    // .modal-box {
+    //     background-color: #2c302f;
+    //     border-radius: 16px 16px 0px 0px;
+    //     position: relative;
+    //     z-index: 1;
+    //     margin-top: 2px;
+    // }
+
+    .modal-page {
+        background-color: #2c302f;
+        border-radius: 16px 16px 0px 0px;
+        position: relative;
+        z-index: 1;
+        height: 100%;
+        .head {
+            .f();
+            justify-content: space-between;
+            padding: 16px 16px;
+            .title {
+                .text1 {
+                    font-size: 18px;
+                    font-family: TsangerYuMo;
+                    color: #ffffff;
+                    line-height: 24px;
+                    .gt();
+                }
+
+                .text2 {
+                    font-size: 12px;
+                    color: #ffffff;
+                    line-height: 17px;
+                }
+            }
+
+            .close {
+                img {
+                    width: 22px;
+                    height: 22px;
+                    display: block;
+                }
+            }
+        }
+
+        :deep(.van-tabs) {
+            --van-tabs-nav-background: #2c302f;
+            --van-tabs-bottom-bar-height: 2px;
+            margin: 0 20px 18px;
+            .van-tab {
+                color: #ffffff;
+                line-height: 24px;
+                font-size: 18px;
+                font-family: TsangerYuMo;
+                .gt();
+            }
+            .van-tabs__line {
+                background: #d8d8d8 linear-gradient(135deg, #deffce 0%, #85f9c4 51%, #3df3bc 100%);
+                bottom: 18px;
+            }
+        }
+
+        .chooseBtns {
+            padding: 8px 16px 20px;
+            .f();
+            overflow-x: auto;
+            .choose-btn {
+                position: relative;
+                width: calc((100vw - 32px - 22px - 22px - 30px) / 3);
+                height: 110px;
+                border-radius: 8px;
+                overflow: hidden;
+                margin-right: 22px;
+                flex-shrink: 0;
+
+                &::after {
+                    content: '';
+                    position: absolute;
+                    top: 0;
+                    right: 0;
+                    bottom: 0;
+                    left: 0;
+                    background-color: #7d7d7d;
+                    z-index: 0;
+                }
+
+                .choose-btn-bg {
+                    position: absolute;
+                    top: 1px;
+                    right: 1px;
+                    left: 1px;
+                    bottom: 1px;
+                    background: #3c403f;
+                    z-index: 1;
+                    border-radius: 8px;
+                    .f-col();
+                    align-items: center;
+                    justify-content: center;
+                    .text1 {
+                        font-size: 16px;
+                        font-weight: bold;
+                        color: #ffffff;
+                        line-height: 24px;
+                    }
+
+                    .text2 {
+                        .f();
+                        // align-items: flex-end;
+                        font-size: 26px;
+                        font-weight: bold;
+                        color: #c5f2ac;
+                        line-height: 30px;
+                        margin-top: 4px;
+                        .gt();
+                        small {
+                            font-size: 16px;
+                            margin-top: 5px;
+                        }
+                    }
+                }
+
+                &:last-child {
+                    margin-right: 0;
+                }
+                &.prim {
+                    &::after {
+                        background: linear-gradient(180deg, rgba(211, 254, 204, 1), rgba(75, 244, 189, 1));
+                    }
+                    .choose-btn-bg {
+                        background: #1a5244;
+                    }
+                }
+            }
+
+            &.large-btn {
+                .choose-btn {
+                    width: calc((100vw - 32px - 22px - 22px) / 3);
+                }
+            }
+        }
+        .wallet {
+            padding: 12px 16px 50px;
+            .f();
+            .icon {
+                width: 24px;
+                height: 24px;
+            }
+            span {
+                font-size: 14px;
+                font-weight: bold;
+                color: #ffffff;
+                line-height: 24px;
+                flex-grow: 1;
+                margin-left: 10px;
+            }
+            .next {
+                width: 24px;
+                height: 24px;
+            }
+        }
+
+        .footer {
+            position: absolute;
+            bottom: 0;
+            left: 0;
+            right: 0;
+            padding: 9px 27px;
+        }
+    }
+}
+
+.stake-modal {
+    --height: fit-content;
+    --border-radius: 8px;
+    --width: 80vw;
+}
+.stake-modal-wrapper {
+    background: linear-gradient(90deg, rgba(219, 255, 206, 1), rgba(61, 243, 188, 1));
+    padding: 2px;
+}
+.stake-modal-content {
+    background-color: #2c302f;
+    border-radius: 8px;
+    padding: 24px 16px;
+
+    .title {
+        .text1 {
+            font-size: 18px;
+            font-weight: bold;
+            color: #ffffff;
+            line-height: 24px;
+        }
+
+        .text2 {
+            font-size: 12px;
+            color: #39f3b9;
+            line-height: 17px;
+            margin-top: 5px;
+        }
+    }
+
+    .close {
+        position: absolute;
+        right: 16px;
+        top: 16px;
+        img {
+            width: 22px;
+            height: 22px;
+        }
+    }
+
+    .amount-filed {
+        --van-field-input-text-color: #39f3b9;
+        width: 100%;
+        margin: 14px 0 24px;
+        background: #3c403f;
+        border-radius: 8px;
+        border: 1px solid #4f5352;
+        font-size: 36px;
+        font-weight: bold;
+        line-height: 60px;
+        padding: 0 12px;
+        &::-webkit-input-placeholder {
+            font-size: 14px;
+        }
+        ::-webkit-input-placeholder {
+            font-size: 20px;
+            line-height: 60px;
+        }
+    }
+    .fileds {
+        .f();
+        padding: 20px 0 40px;
+        .van-field {
+            --van-field-input-text-color: #39f3b9;
+            background: #3c403f;
+            border-radius: 8px;
+            border: 1px solid #4f5352;
+            font-size: 36px;
+            font-weight: bold;
+            line-height: 60px;
+            padding: 0 12px;
+            text-align: center;
+            &::-webkit-input-placeholder {
+                font-size: 14px;
+            }
+            ::-webkit-input-placeholder {
+                font-size: 20px;
+                line-height: 60px;
+            }
+            :deep(.van-field__control) {
+                text-align: center;
+            }
+        }
+
+        .fileds-text {
+            font-size: 18px;
+            font-weight: bold;
+            color: #ffffff;
+            line-height: 24px;
+            margin: 0 13px;
+        }
+    }
+}
 </style>

+ 0 - 31
src/views/RankPage.vue

@@ -434,37 +434,6 @@ function goInvite() {
             }
         }
     }
-
-    &.rank-val_0 {
-        .val-content {
-            .text1 {
-                .gradient-text(#ed5701, #c40800);
-            }
-            .text2 {
-                .gradient-text(#ed5701, #c40800);
-            }
-        }
-    }
-    &.rank-val_1 {
-        .val-content {
-            .text1 {
-                .gradient-text(#004f84, #0a4268);
-            }
-            .text2 {
-                .gradient-text(#004f84, #0a4268);
-            }
-        }
-    }
-    &.rank-val_2 {
-        .val-content {
-            .text1 {
-                .gradient-text(#ae4432, #9d1c06);
-            }
-            .text2 {
-                .gradient-text(#ae4432, #9d1c06);
-            }
-        }
-    }
 }
 
 .nor {

+ 32 - 0
tests/order.mjs

@@ -0,0 +1,32 @@
+import axios from 'axios'
+import qs from 'qs'
+
+const http = axios.create({
+    baseURL: 'http://localhost:8080'
+})
+const { data: token } = await http.post(
+    'http://localhost:8080/auth/login',
+    qs.stringify({
+        username: '15077886171',
+        password: '123456'
+    })
+)
+http.defaults.headers.common['Authorization'] = `Bearer ${token}`
+const {
+    data: { content: list }
+} = await http.post('/product/list', {
+    page: 0,
+    size: 20,
+    sort: 'delayTo,asc;sales,desc;',
+    query: { batchId: '11548', status: 'IN_STOCK' }
+})
+
+for (let i = 0; i < 3; i++) {
+    http.post('/order/createOrder', `productId=${list[0].id}`)
+        .then(res => {
+            console.log('success')
+        })
+        .catch(e => {
+            console.log(e.response.data.error)
+        })
+}