Houjie
3 天以前 51c7896fd8e45085dd5cdfff11e79a00ee0a7379
pages/login/login.vue
@@ -1,343 +1,543 @@
  <template>
     <view class="zai-box">
        <scroll-view scroll-y class="page">
           <view class="text-center" :style="[{animation: 'show ' + 0.4+ 's 1'}]">
              <image src="/static/logo-lx.png" mode='aspectFit' class="zai-logo "></image>
              <view class="zai-title text-shadow ">设备管理 </view>
           </view>
           <view class="box padding-lr-xl login-paddingtop" :style="[{animation: 'show ' + 0.6+ 's 1'}]">
              <block v-if="loginWay==1">
                 <view class="cu-form-group margin-top  shadow-warp" :class="[shape=='round'?'round':'']">
                    <view class="title"><text class="cuIcon-people margin-right-xs"></text>账号:</view>
                    <input placeholder="请输入账号" name="input" v-model="userName"></input>
                 </view>
                 <view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']">
                    <view class="title"><text class="cuIcon-lock margin-right-xs"></text>密码:</view>
                    <input class="uni-input" placeholder="请输入密码" :password="!showPassword" v-model="password" />
                    <view class="action text-lg">
                       <text :class="[showPassword ? 'cuIcon-attention' : 'cuIcon-attentionforbid']"
                          @click="changePassword"></text>
                    </view>
                 </view>
                 <view class="padding text-center margin-top">
                    <button class="cu-btn bg-blue lg margin-right shadow" :loading="loading"
                       :class="[shape=='round'?'round':'']" @tap="onLogin"><text
                          space="emsp">{{loading ? "登录中...":" 登录 "}}</text>
                    </button>
                    <!-- <button class="cu-btn line-blue lg margin-left shadow" :loading="loading" :class="[shape=='round'?'round':'']"
                     @tap="loginWay=3-loginWay">短信登录
                  </button> -->
                 </view>
              </block>
              <block v-else>
                 <view class="cu-form-group margin-top  shadow-warp" :class="[shape=='round'?'round':'']">
                    <view class="title"><text class="cuIcon-mobile margin-right-xs"></text>手机号:</view>
                    <input placeholder="请输入手机号" type="number" maxlength="11" v-model="phoneNo"></input>
                 </view>
                 <view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']">
                    <view class="title"><text class="cuIcon-lock margin-right-xs"></text>验证码:</view>
                    <input class="uni-input" placeholder="请输入验证码" v-model="smsCode" />
                    <view class="action">
                       <button class="cu-btn line-blue sm" :disabled="!isSendSMSEnable" @click="onSMSSend">
                          {{ getSendBtnText }}</button>
                    </view>
                 </view>
                 <view class="padding text-center margin-top">
                    <button class="cu-btn bg-blue lg margin-right shadow" :loading="loading"
                       :class="[shape=='round'?'round':'']" @tap="onSMSLogin"><text
                          space="emsp">{{loading ? "登录中...":" 登录 "}}</text>
                    </button>
                    <button class="cu-btn line-blue lg margin-left shadow" :loading="loading"
                       :class="[shape=='round'?'round':'']" @tap="loginWay=1">账户登录
                    </button>
                 </view>
              </block>
<template>
   <view class="zai-box">
      <scroll-view scroll-y class="page">
         <view class="text-center" :style="[{animation: 'show ' + 0.4+ 's 1'}]">
            <image src="/static/logo-lx.png" mode='aspectFit' class="zai-logo "></image>
            <view class="zai-title text-shadow ">设备管理 </view>
         </view>
         <view class="box padding-lr-xl login-paddingtop" :style="[{animation: 'show ' + 0.6+ 's 1'}]">
            <block v-if="loginWay==1">
               <view class="cu-form-group margin-top  shadow-warp" :class="[shape=='round'?'round':'']">
                  <view class="title"><text class="cuIcon-people margin-right-xs"></text>账号:</view>
                  <input placeholder="请输入账号" name="input" v-model="userName"></input>
               </view>
               <view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']">
                  <view class="title"><text class="cuIcon-lock margin-right-xs"></text>密码:</view>
                  <input class="uni-input" placeholder="请输入密码" :password="!showPassword" v-model="password" />
                  <view class="action text-lg">
                     <text :class="[showPassword ? 'cuIcon-attention' : 'cuIcon-attentionforbid']"
                        @click="changePassword"></text>
                  </view>
               </view>
               <view class="padding text-center margin-top">
                  <button class="cu-btn bg-blue lg margin-right shadow" :loading="loading"
                     :class="[shape=='round'?'round':'']" @tap="onLogin"><text
                        space="emsp">{{loading ? "登录中...":" 登录 "}}</text>
                  </button>
               </view>
            </block>
            <block v-else>
               <view class="cu-form-group margin-top  shadow-warp" :class="[shape=='round'?'round':'']">
                  <view class="title"><text class="cuIcon-mobile margin-right-xs"></text>手机号:</view>
                  <input placeholder="请输入手机号" type="number" maxlength="11" v-model="phoneNo"></input>
               </view>
               <view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']">
                  <view class="title"><text class="cuIcon-lock margin-right-xs"></text>验证码:</view>
                  <input class="uni-input" placeholder="请输入验证码" v-model="smsCode" />
                  <view class="action">
                     <button class="cu-btn line-blue sm" :disabled="!isSendSMSEnable" @click="onSMSSend">
                        {{ getSendBtnText }}</button>
                  </view>
               </view>
               <view class="padding text-center margin-top">
                  <button class="cu-btn bg-blue lg margin-right shadow" :loading="loading"
                     :class="[shape=='round'?'round':'']" @tap="onSMSLogin"><text
                        space="emsp">{{loading ? "登录中...":" 登录 "}}</text>
                  </button>
                  <button class="cu-btn line-blue lg margin-left shadow" :loading="loading"
                     :class="[shape=='round'?'round':'']" @tap="loginWay=1">账户登录
                  </button>
               </view>
            </block>
              <!-- #ifdef APP-PLUS -->
              <view class="padding flex flex-direction  text-center">
                 当前版本:{{version}}
              </view>
              <!-- #endif -->
            <!-- #ifdef APP-PLUS -->
            <view class="padding flex flex-direction  text-center">
               当前版本:{{version}}
            </view>
            <!-- #endif -->
           </view>
        </scroll-view>
        <!-- 登录加载弹窗 -->
        <view class="cu-load load-modal" v-if="loading">
           <!-- <view class="cuIcon-emojifill text-orange"></view> -->
           <image src="/static/logo-lx.png" mode="aspectFit" class="round"></image>
           <view class="gray-text">登录中...</view>
        </view>
     </view>
         </view>
      </scroll-view>
      <!-- 登录加载弹窗 -->
      <view class="cu-load load-modal" v-if="loading">
         <image src="/static/logo-lx.png" mode="aspectFit" class="round"></image>
         <view class="gray-text">登录中...</view>
      </view>
  </template>
      <!-- 产线选择弹窗 - 从底部弹出 -->
      <view class="line-modal-mask" v-if="showLineModal" @click="closeLineModal"></view>
      <view class="line-modal" v-if="showLineModal" :class="{ 'line-modal-active': showLineModal }">
         <view class="line-modal-header">
            <view class="line-modal-title">选择产线</view>
            <text class="cuIcon-close" @click="closeLineModal"></text>
         </view>
         <view class="line-modal-content">
            <view v-if="lineLoading" class="loading-view">
               <view class="cu-load"></view>
               <view class="text-gray">加载产线中...</view>
            </view>
            <view v-else-if="lineList.length === 0" class="empty-view">
               <text class="cuIcon-meh text-gray text-xl"></text>
               <view class="text-gray">暂无可用产线</view>
            </view>
            <view class="line-item" v-for="(line, index) in lineList" :key="line.id" @click="selectLine(line)"
               :class="{ 'line-item-selected': selectedLineId === line.id }">
               <view class="line-name">{{ line.name }}</view>
               <text class="cuIcon-check text-blue" v-if="selectedLineId === line.id"></text>
            </view>
         </view>
         <view class="line-modal-footer">
            <button class="cu-btn bg-blue lg" @click="confirmLineSelection" :disabled="!selectedLineId">
               确认选择
            </button>
         </view>
      </view>
   </view>
</template>
  <script>
     import {
        ACCESS_TOKEN,
        USER_NAME,
        USER_INFO
     } from "@/common/util/constants"
     import {
        mapActions
     } from "vuex"
     import configService from '@/common/service/config.service.js';
<script>
   import {
      ACCESS_TOKEN,
      USER_NAME,
      USER_INFO
   } from "@/common/util/constants"
   import {
      mapActions,
      mapState,
      mapGetters
   } from "vuex" // 新增mapGetters
   import configService from '@/common/service/config.service.js';
     export default {
        data() {
           return {
              shape: '', //round 圆形
              loading: false,
              userName: '',
              password: '',
              phoneNo: '',
              smsCode: '',
              showPassword: false, //是否显示明文
              loginWay: 1, //1: 账密,2:验证码
              smsCountDown: 0,
              smsCountInterval: null,
              toggleDelay: false,
              version: '',
              //第三方登录相关信息
              thirdType: "",
              thirdLoginInfo: "",
              thirdLoginState: false,
              bindingPhoneModal: false,
              thirdUserUuid: '',
              id: '',
              url: {
                 bindingThirdPhone: '/sys/thirdLogin/bindingThirdPhone'
              }
           };
        },
        onLoad: function(options) {
           this.id = options.id;
           // #ifdef APP-PLUS
           var that = this
           plus.runtime.getProperty(plus.runtime.appid, function(wgtinfo) {
              that.version = wgtinfo.version
           });
           // #endif
        },
        computed: {
           isSendSMSEnable() {
              return this.smsCountDown <= 0 && this.phoneNo.length > 4;
           },
           getSendBtnText() {
              if (this.smsCountDown > 0) {
                 return this.smsCountDown + '秒后发送';
              } else {
                 return '发送验证码';
              }
           },
           canSMSLogin() {
              return this.userName.length > 4 && this.smsCode.length > 4;
           },
           canPwdLogin() {
              return this.userName.length > 4 && this.password.length > 4;
           },
        },
        methods: {
           ...mapActions(["mLogin", "PhoneLogin", "ThirdLogin"]),
           onLogin: function() {
              if (!this.userName || this.userName.length == 0) {
                 this.$tip.toast('请填写用户名');
                 return;
              }
              if (!this.password || this.password.length == 0) {
                 this.$tip.toast('请填写密码');
                 return;
              }
              let loginParams = {
                 username: this.userName,
                 password: this.password
              }
              this.loading = true;
              this.mLogin(loginParams).then((res) => {
                 this.loading = false;
                 if (res.data.success) {
                    // #ifdef APP-PLUS
                    this.saveClientId()
                    // #endif
                    // #ifndef APP-PLUS
                    this.$tip.success('登录成功!')
                    this.$Router.replaceAll({
                       name: 'index'
                    })
                    // if (this.id) {
                    //    uni.navigateTo({
                    //       url: `/pages/device/deviceWebDeils/deviceWebDeils?equipmentId=${this.id}`
                    //    });
   export default {
      data() {
         return {
            shape: '', //round 圆形
            loading: false,
            userName: '',
            password: '',
            phoneNo: '',
            smsCode: '',
            showPassword: false, //是否显示明文
            loginWay: 1, //1: 账密,2:验证码
            smsCountDown: 0,
            smsCountInterval: null,
            toggleDelay: false,
            version: '',
            //第三方登录相关信息
            thirdType: "",
            thirdLoginInfo: "",
            thirdLoginState: false,
            bindingPhoneModal: false,
            thirdUserUuid: '',
            id: '',
            url: {
               bindingThirdPhone: '/sys/thirdLogin/bindingThirdPhone'
            },
            // 产线选择相关
            showLineModal: false, // 产线弹窗是否显示
            lineList: [], // 产线列表
            selectedLineId: null, // 当前选中的产线ID
            lineLoading: false // 产线加载状态
         };
      },
      computed: {
         ...mapState(['currentLineId']),
         ...mapGetters(['currentLineName']), // 新增:获取当前产线名称(用于验证)
         isSendSMSEnable() {
            return this.smsCountDown <= 0 && this.phoneNo.length > 4;
         },
         getSendBtnText() {
            if (this.smsCountDown > 0) {
               return this.smsCountDown + '秒后发送';
            } else {
               return '发送验证码';
            }
         },
         canSMSLogin() {
            return this.userName.length > 4 && this.smsCode.length > 4;
         },
         canPwdLogin() {
            return this.userName.length > 4 && this.password.length > 4;
         },
      },
      onLoad: function(options) {
         this.id = options.id;
         // #ifdef APP-PLUS
         var that = this
         plus.runtime.getProperty(plus.runtime.appid, function(wgtinfo) {
            that.version = wgtinfo.version
         });
         // #endif
      },
      methods: {
         ...mapActions(["mLogin", "PhoneLogin", "ThirdLogin", "fetchLineList", "setCurrentLine"]),
                    // } else {
                    //    this.$tip.success('登录成功!')
                    //    this.$Router.replaceAll({
                    //       name: 'index'
                    //    })
                    // }
         onLogin: function() {
            if (!this.userName || this.userName.length == 0) {
               this.$tip.toast('请填写用户名');
               return;
            }
            if (!this.password || this.password.length == 0) {
               this.$tip.toast('请填写密码');
               return;
            }
            let loginParams = {
               username: this.userName,
               password: this.password
            }
            this.loading = true;
            this.mLogin(loginParams).then((res) => {
               this.loading = false;
               if (res.data.success) {
                  console.log('账号登录成功,准备处理产线选择');
                  // #ifdef APP-PLUS
                  this.saveClientId()
                  // #endif
                  // #ifndef APP-PLUS
                  this.handleLoginSuccess()
                  // #endif
               } else {
                  this.$tip.alert(res.data.message);
               }
            }).catch((err) => {
               let msg = err.data.message || "请求出现错误,请稍后再试"
               this.loading = false;
               this.$tip.alert(msg);
            }).finally(() => {
               this.loading = false;
            })
         },
         saveClientId() {
            var info = plus.push.getClientInfo();
            var cid = info.clientid;
            this.$http.get("/sys/user/saveClientId", {
               params: {
                  clientId: cid
               }
            }).then(res => {
               console.log("res::saveClientId>", res)
               this.handleLoginSuccess();
            }).catch(err => {
               console.error('保存clientId失败:', err);
               this.handleLoginSuccess();
            })
         },
                    // #endif
                 } else {
                    this.$tip.alert(res.data.message);
                 }
              }).catch((err) => {
                 let msg = err.data.message || "请求出现错误,请稍后再试"
                 this.loading = false;
                 this.$tip.alert(msg);
              }).finally(() => {
                 this.loading = false;
              })
           },
           saveClientId() {
              var info = plus.push.getClientInfo();
              var cid = info.clientid;
              this.$http.get("/sys/user/saveClientId", {
                 params: {
                    clientId: cid
                 }
              }).then(res => {
                 console.log("res::saveClientId>", res)
                 this.$tip.success('登录成功!')
                 this.$Router.replaceAll({
                    name: 'index'
                 })
              })
           },
           changePassword() {
              this.showPassword = !this.showPassword;
           },
           onSMSSend() {
              let smsParams = {};
              smsParams.mobile = this.phoneNo;
              smsParams.smsmode = "0";
              let checkPhone = new RegExp(/^[1]([3-9])[0-9]{9}$/);
              if (!smsParams.mobile || smsParams.mobile.length == 0) {
                 this.$tip.toast('请输入手机号');
                 return false
              }
              if (!checkPhone.test(smsParams.mobile)) {
                 this.$tip.toast('请输入正确的手机号');
                 return false
              }
              this.$http.post("/sys/sms", smsParams).then(res => {
                 if (res.data.success) {
                    this.smsCountDown = 60;
                    this.startSMSTimer();
                 } else {
                    this.smsCountDown = 0;
                    this.$tip.toast(res.data.message);
                 }
              });
           },
           startSMSTimer() {
              this.smsCountInterval = setInterval(() => {
                 this.smsCountDown--;
                 if (this.smsCountDown <= 0) {
                    clearInterval(this.smsCountInterval);
                 }
              }, 1000);
           },
           onSMSLogin() {
              let checkPhone = new RegExp(/^[1]([3-9])[0-9]{9}$/);
         changePassword() {
            this.showPassword = !this.showPassword;
         },
              if (!this.phoneNo || this.phoneNo.length == 0) {
                 this.$tip.toast('请填写手机号');
                 return;
              }
              if (!checkPhone.test(this.phoneNo)) {
                 this.$tip.toast('请输入正确的手机号');
                 return false
              }
              if (!this.smsCode || this.smsCode.length == 0) {
                 this.$tip.toast('请填短信验证码');
                 return;
              }
              let loginParams = {
                 mobile: this.phoneNo,
                 captcha: this.smsCode
              };
              this.PhoneLogin(loginParams).then((res) => {
                 console.log("res====》", res)
                 if (res.data.success) {
                    this.$tip.success('登录成功!')
                    this.$Router.replaceAll({
                       name: 'index'
                    })
                 } else {
                    this.$tip.error(res.data.message);
                 }
              }).catch((err) => {
                 let msg = ((err.response || {}).data || {}).message || err.data.message || "请求出现错误,请稍后再试"
                 this.$tip.error(msg);
              });
           },
           loginSuccess() {
              // 登陆成功,重定向到主页
              this.$Router.replace({
                 name: 'index'
              })
           },
           requestFailed(err) {
              this.$message.warning("登录失败")
           },
        },
        beforeDestroy() {
           if (this.smsCountInterval) {
              clearInterval(this.smsCountInterval);
           }
        },
     }
  </script>
         onSMSSend() {
            let smsParams = {};
            smsParams.mobile = this.phoneNo;
            smsParams.smsmode = "0";
            let checkPhone = new RegExp(/^[1]([3-9])[0-9]{9}$/);
            if (!smsParams.mobile || smsParams.mobile.length == 0) {
               this.$tip.toast('请输入手机号');
               return false
            }
            if (!checkPhone.test(smsParams.mobile)) {
               this.$tip.toast('请输入正确的手机号');
               return false
            }
            this.$http.post("/sys/sms", smsParams).then(res => {
               if (res.data.success) {
                  this.smsCountDown = 60;
                  this.startSMSTimer();
               } else {
                  this.smsCountDown = 0;
                  this.$tip.toast(res.data.message);
               }
            });
         },
  <style>
     .login-paddingtop {
        padding-top: 100upx;
     }
         startSMSTimer() {
            this.smsCountInterval = setInterval(() => {
               this.smsCountDown--;
               if (this.smsCountDown <= 0) {
                  clearInterval(this.smsCountInterval);
               }
            }, 1000);
         },
     .zai-box {
        padding: 0 20upx;
        padding-top: 100upx;
        position: relative;
     }
         onSMSLogin() {
            let checkPhone = new RegExp(/^[1]([3-9])[0-9]{9}$/);
     .zai-logo {
        width: 200upx;
        height: 150px;
     }
            if (!this.phoneNo || this.phoneNo.length == 0) {
               this.$tip.toast('请填写手机号');
               return;
            }
            if (!checkPhone.test(this.phoneNo)) {
               this.$tip.toast('请输入正确的手机号');
               return false
            }
            if (!this.smsCode || this.smsCode.length == 0) {
               this.$tip.toast('请填短信验证码');
               return;
            }
            let loginParams = {
               mobile: this.phoneNo,
               captcha: this.smsCode
            };
            this.PhoneLogin(loginParams).then((res) => {
               console.log("短信登录成功,准备处理产线选择");
               if (res.data.success) {
                  this.handleLoginSuccess();
               } else {
                  this.$tip.error(res.data.message);
               }
            }).catch((err) => {
               let msg = ((err.response || {}).data || {}).message || err.data.message || "请求出现错误,请稍后再试"
               this.$tip.error(msg);
            });
         },
     .zai-title {
        font-size: 58upx;
        color: #000000;
        text-align: center;
     }
         // 登录成功后的处理
         handleLoginSuccess() {
            console.log('进入handleLoginSuccess方法');
            this.$tip.success('登录成功!');
     .input-placeholder,
     .zai-input {
        color: #94afce;
     }
            // 检查是否已有保存的产线(包含ID和名称)
            const savedLineId = uni.getStorageSync('currentLineId');
            const savedLineName = uni.getStorageSync('currentLineName');
            console.log('本地存储的产线ID:', savedLineId, '名称:', savedLineName);
     .zai-label {
        padding: 60upx 0;
        text-align: center;
        font-size: 30upx;
        color: #a7b6d0;
     }
            if (savedLineId && savedLineName) {
               // 已有完整的产线信息,直接进入首页
               console.log('已有保存的产线,直接进入首页');
               this.navigateToHome();
            } else {
               // 无保存的产线,获取产线列表并显示弹窗
               console.log('无保存的产线,准备获取产线列表');
               this.getLineListAndShowModal();
            }
         },
     .zai-btn {
        background: #ff65a3;
        color: #fff;
        border: 0;
        border-radius: 100upx;
        font-size: 36upx;
     }
         // 获取产线列表并显示弹窗
         getLineListAndShowModal() {
            console.log('进入getLineListAndShowModal方法');
            this.lineLoading = true;
            this.lineList = [];
            this.selectedLineId = null;
     .zai-btn:after {
        border: 0;
     }
            // 调用Vuex action获取产线列表
            this.fetchLineList().then(lineList => {
               console.log('获取产线列表成功:', lineList);
               this.lineList = lineList || [];
               this.lineLoading = false;
     /*按钮点击效果*/
     .zai-btn.button-hover {
        transform: translate(1upx, 1upx);
     }
  </style>
               // 如果有产线,默认选中第一条并显示弹窗
               if (this.lineList.length > 0) {
                  this.selectedLineId = this.lineList[0].id;
                  this.showLineModal = true;
                  console.log('产线列表不为空,显示弹窗');
               } else {
                  console.log('产线列表为空,不显示弹窗');
                  this.$tip.toast('未获取到产线数据,请联系管理员');
                  this.navigateToHome();
               }
            }).catch(err => {
               console.error('获取产线列表失败:', err);
               this.lineLoading = false;
               this.$tip.toast('获取产线列表失败,请重试');
               // 3秒后重试
               setTimeout(() => {
                  this.getLineListAndShowModal();
               }, 3000);
            });
         },
         // 选择产线
         selectLine(line) {
            console.log('选中的产线:', line.id, line.name); // 打印选中的产线名称
            this.selectedLineId = line.id;
         },
         // 确认选择产线(触发Vuex保存ID和名称)
         confirmLineSelection() {
            if (!this.selectedLineId) return;
            // 调用setCurrentLine,Vuex会自动同步名称
            this.setCurrentLine(this.selectedLineId).then(() => {
               console.log('产线选择成功,ID:', this.selectedLineId, '名称:', this.currentLineName);
               this.showLineModal = false;
               this.navigateToHome();
            });
         },
         // 关闭产线选择弹窗
         closeLineModal() {
            this.showLineModal = false;
         },
         // 跳转到首页
         navigateToHome() {
            this.$Router.replaceAll({
               name: 'index'
            });
         },
         loginSuccess() {
            this.$Router.replace({
               name: 'index'
            })
         },
         requestFailed(err) {
            this.$message.warning("登录失败")
         },
      },
      beforeDestroy() {
         if (this.smsCountInterval) {
            clearInterval(this.smsCountInterval);
         }
      }
   }
</script>
<style>
   /* 原有样式保持不变 */
   .login-paddingtop {
      padding-top: 100upx;
   }
   .zai-box {
      padding: 0 20upx;
      padding-top: 100upx;
      position: relative;
   }
   .zai-logo {
      width: 200upx;
      height: 150px;
   }
   .zai-title {
      font-size: 58upx;
      color: #000000;
      text-align: center;
   }
   .input-placeholder,
   .zai-input {
      color: #94afce;
   }
   .zai-label {
      padding: 60upx 0;
      text-align: center;
      font-size: 30upx;
      color: #a7b6d0;
   }
   .zai-btn {
      background: #ff65a3;
      color: #fff;
      border: 0;
      border-radius: 100upx;
      font-size: 36upx;
   }
   .zai-btn:after {
      border: 0;
   }
   .zai-btn.button-hover {
      transform: translate(1upx, 1upx);
   }
   /* 产线选择弹窗样式 */
   .line-modal-mask {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.5);
      z-index: 998;
      opacity: 0;
      transition: opacity 0.3s ease;
   }
   .line-modal {
      position: fixed;
      left: 0;
      right: 0;
      bottom: -100%;
      background-color: #fff;
      border-top-left-radius: 20upx;
      border-top-right-radius: 20upx;
      z-index: 999;
      transition: bottom 0.3s ease;
      max-height: 80vh;
   }
   .line-modal-active {
      bottom: 0;
   }
   .line-modal-active+.line-modal-mask {
      opacity: 1;
   }
   .line-modal-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 20upx 30upx;
      border-bottom: 1px solid #eee;
   }
   .line-modal-title {
      font-size: 34upx;
      font-weight: bold;
   }
   .line-modal-content {
      padding: 20upx;
      overflow-y: auto;
      max-height: calc(80vh - 180upx);
   }
   .line-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 25upx 20upx;
      border-bottom: 1px solid #f5f5f5;
      font-size: 30upx;
   }
   .line-item-selected {
      background-color: #f0f7ff;
   }
   .line-item:last-child {
      border-bottom: none;
   }
   .line-modal-footer {
      padding: 20upx 30upx;
      border-top: 1px solid #eee;
   }
   .loading-view {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 60upx 0;
   }
   .empty-view {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 60upx 0;
   }
</style>