chenyao
2025-11-08 5aeb8c60920f72382b57c81b3789b127a941f46a
Merge remote-tracking branch 'origin/master'
17 files modified
5 files added
1 files deleted
930 ■■■■■ changed files
src/components/WebViewPlus.vue 2 ●●● patch | view | raw | blame | history
src/manifest.json 10 ●●●●● patch | view | raw | blame | history
src/pages.json 16 ●●●● patch | view | raw | blame | history
src/pages/inspectionTask/index.vue 2 ●●● patch | view | raw | blame | history
src/pages/login/index.vue 416 ●●●●● patch | view | raw | blame | history
src/pages/user/index.vue 94 ●●●● patch | view | raw | blame | history
src/static/images/app-logo.png patch | view | raw | blame | history
src/static/images/tabbar/icon_home.png patch | view | raw | blame | history
src/static/images/tabbar/icon_home1.png patch | view | raw | blame | history
src/static/images/tabbar/icon_home2.png patch | view | raw | blame | history
src/static/images/tabbar/icon_home_selected.png patch | view | raw | blame | history
src/static/images/tabbar/icon_list.png patch | view | raw | blame | history
src/static/images/tabbar/icon_list_selected.png patch | view | raw | blame | history
src/static/images/tabbar/icon_me.png patch | view | raw | blame | history
src/static/images/tabbar/icon_me_selected.png patch | view | raw | blame | history
src/static/images/tabbar/icon_order.png patch | view | raw | blame | history
src/static/images/tabbar/icon_order_selected.png patch | view | raw | blame | history
src/subPackages/droneConsole/index.vue 32 ●●●● patch | view | raw | blame | history
src/subPackages/inProgress/index.vue 6 ●●●● patch | view | raw | blame | history
src/subPackages/userDetail/infos/index.vue 199 ●●●●● patch | view | raw | blame | history
src/subPackages/userDetail/password/index.vue 137 ●●●● patch | view | raw | blame | history
src/utils/common/index.js 14 ●●●● patch | view | raw | blame | history
src/utils/request/interceptors.js 2 ●●● patch | view | raw | blame | history
src/components/WebViewPlus.vue
@@ -22,7 +22,7 @@
function messageFun(e) {
  if (e.data.type === "WEB_INVOKE_APPSERVICE") {
    if (e.data.data.arg.type === "tokenExpired") {
      return uni.redirectTo({
      return uni.reLaunch({
        url: "/pages/login/index",
      });
    }
src/manifest.json
@@ -58,7 +58,15 @@
                "dSYMs" : false
            },
            /* SDK配置 */
            "sdkConfigs" : {}
            "sdkConfigs" : {},
            "icons" : {
                "android" : {
                    "hdpi" : "src/static/images/app-logo.png",
                    "xhdpi" : "src/static/images/app-logo.png",
                    "xxhdpi" : "src/static/images/app-logo.png",
                    "xxxhdpi" : "src/static/images/app-logo.png"
                }
            }
        }
    },
    /* 快应用特有相关 */
src/pages.json
@@ -158,14 +158,14 @@
    "backgroundColor": "#ffffff",
    "list": [
      {
        "iconPath": "static/images/tabbar/icon_home.svg",
        "selectedIconPath": "static/images/tabbar/icon_home_selected.svg",
        "iconPath": "static/images/tabbar/icon_home1.png",
        "selectedIconPath": "static/images/tabbar/icon_home_selected.png",
        "pagePath": "pages/map/index",
        "text": "地图"
      },
      {
        "iconPath": "static/images/tabbar/icon_list.svg",
        "selectedIconPath": "static/images/tabbar/icon_list_selected.svg",
        "iconPath": "static/images/tabbar/icon_list.png",
        "selectedIconPath": "static/images/tabbar/icon_list_selected.png",
        "pagePath": "pages/inspectionTask/index",
        "text": "任务"
      },
@@ -178,14 +178,14 @@
        "visible": "true"
      },
      {
        "iconPath": "static/images/tabbar/icon_order.svg",
        "selectedIconPath": "static/images/tabbar/icon_order_selected.svg",
        "iconPath": "static/images/tabbar/icon_order.png",
        "selectedIconPath": "static/images/tabbar/icon_order_selected.png",
        "pagePath": "pages/work/index",
        "text": "工单"
      },
      {
        "iconPath": "static/images/tabbar/icon_me.svg",
        "selectedIconPath": "static/images/tabbar/icon_me_selected.svg",
        "iconPath": "static/images/tabbar/icon_me.png",
        "selectedIconPath": "static/images/tabbar/icon_me_selected.png",
        "pagePath": "pages/user/index",
        "text": "我的"
      }
src/pages/inspectionTask/index.vue
@@ -28,7 +28,7 @@
onShow(() => {
  isApp.value = true
  // 清除数据
  const encodedData = encodeURIComponent(JSON.stringify({ device_sn: '', latitude: '25.9922904974658', longitude: '114.82299452904442' }))
  const encodedData = encodeURIComponent(JSON.stringify({ device_sn: '' }))
  uni.setStorageSync('webview_params', encodedData);
  uni.setTabBarItem({
    index: 2, // Tab 的索引(从0开始)
src/pages/login/index.vue
@@ -1,208 +1,264 @@
<!-- 登录页 -->
<template>
  <view class="login-form-wrap">
    <img class="logo" src="@/static/images/login/logo.svg" alt=""/>
    <div class="title">掌控智飞</div>
    <div class="user-name">
      <img :src="usernameSvg" alt="">
      <input
        v-model="loginForm.username"
        placeholder="请输入用户名"
      />
    <view class="login-form-wrap">
        <image class="logo" :src="logoSvg" />
        <div class="title">掌控智飞</div>
        <div class="user-name">
            <image :src="usernameSvg" />
            <input v-model="loginForm.username" placeholder="请输入用户名" />
        </div>
        <div class="pass-word">
            <image :src="passwordSvg" />
            <input v-model="loginForm.password" type="password" placeholder="请输入密码" />
        </div>
        <div class="remember-password">
            <label>
                <checkbox-group @change="toggleRemember">
                    <checkbox :checked="rememberPassword" color="#1D6FE9" style="transform:scale(0.7)" />
                </checkbox-group>
                记住密码
            </label>
        </div>
    </div>
    <div class="pass-word">
      <img :src="passwordSvg" alt="">
      <input
        v-model="loginForm.password"
        type="password"
        placeholder="请输入密码"
      />
    </div>
    <button class="login-btn" :style="[inputStyle]" @tap="submit">
      登录
    </button>
    <img class="lowerRightCorner" :src="droneSvg" alt=""/>
  </view>
        <button class="login-btn" :style="[inputStyle]" @tap="submit">
            登录
        </button>
        <image class="lowerRightCorner" :src="droneSvg" />
    </view>
</template>
<script setup>
import md5 from "js-md5";
import {loginByUsername} from "@/api/user/index.js";
import {useUserStore} from "@/store/index.js";
import droneSvg from '@/static/images/login/droneSvg.svg'
import usernameSvg from '@/static/images/login/username.svg'
import passwordSvg from '@/static/images/login/password.svg'
import {HOME_PATH, LOGIN_PATH, removeQueryString} from "@/router";
    import md5 from "js-md5";
    import {
        loginByUsername
    } from "@/api/user/index.js";
    import {
        useUserStore
    } from "@/store/index.js";
    import droneSvg from '@/static/images/login/droneSvg.svg'
    import usernameSvg from '@/static/images/login/username.svg'
    import passwordSvg from '@/static/images/login/password.svg'
    import logoSvg from '@/static/images/login/logo.svg'
    import {
        HOME_PATH,
        LOGIN_PATH,
        removeQueryString
    } from "@/router";
    import { onMounted } from 'vue';
const userStore = useUserStore();
const loginForm = ref({
  username: "",
  password: "",
});
const inputStyle = computed(() => {
  const style = {};
  if (loginForm.value.username && loginForm.value.password) {
    style.color = "#fff";
    style.backgroundColor = '#1D6FE9';
  }
  return style;
});
let redirect = HOME_PATH;
    const userStore = useUserStore();
    const loginForm = ref({
        username: "",
        password: "",
    });
    const rememberPassword = ref(false);
    const inputStyle = computed(() => {
        const style = {};
        if (loginForm.value.username && loginForm.value.password) {
            style.color = "#fff";
            style.backgroundColor = '#1D6FE9';
        }
        return style;
    });
    let redirect = HOME_PATH;
    function toggleRemember(e) {
        rememberPassword.value = e.detail.value.length > 0;
    }
async function submit() {
  let userInfo = {
    tenantId: "000000",
    deptId: "",
    roleId: "",
    username: loginForm.value.username,
    password: loginForm.value.password,
    type: "account",
    code: "",
    key: "",
  };
  loginByUsername(
    userInfo.tenantId,
    userInfo.deptId,
    userInfo.roleId,
    userInfo.username,
    md5(userInfo.password),
    userInfo.type,
    userInfo.key,
    userInfo.code
  ).then((res) => {
    userStore.setUserInfo(res.data);
    uni.reLaunch({
      url: "/pages/user/index",
    });
  });
}
    async function submit() {
        let userInfo = {
            tenantId: "000000",
            deptId: "",
            roleId: "",
            username: loginForm.value.username,
            password: loginForm.value.password,
            type: "account",
            code: "",
            key: "",
        };
onLoad((options) => {
  if (options.redirect && removeQueryString(options.redirect) !== LOGIN_PATH) {
    redirect = decodeURIComponent(options.redirect);
  }
});
        if (rememberPassword.value) {
            uni.setStorageSync('rememberedUser', {
                username: loginForm.value.username,
                password: loginForm.value.password
            });
        } else {
            uni.removeStorageSync('rememberedUser');
        }
        try {
            const res = await loginByUsername(
                userInfo.tenantId,
                userInfo.deptId,
                userInfo.roleId,
                userInfo.username,
                md5(userInfo.password),
                userInfo.type,
                userInfo.key,
                userInfo.code
            );
            userStore.setUserInfo(res.data);
            uni.reLaunch({
                url: "/pages/user/index",
            });
        } catch (error) {
            const errorMsg =error.data?.error_description !=="Bad credentials" ? error.data?.error_description:  "登录失败,请重试";
            uni.showToast({
                title: errorMsg,
                icon: "none",
                duration: 2000
            });
        }
    }
    // 从本地存储加载记住的密码
    onMounted(() => {
        const savedUserInfo = uni.getStorageSync('rememberedUser');
        console.log('记住密码',savedUserInfo)
        if (savedUserInfo) {
            loginForm.value.username = savedUserInfo.username;
            loginForm.value.password = savedUserInfo.password;
            rememberPassword.value = true;
        }
    });
    onLoad((options) => {
        if (options.redirect && removeQueryString(options.redirect) !== LOGIN_PATH) {
            redirect = decodeURIComponent(options.redirect);
        }
    });
</script>
<style lang="scss" scoped>
.login-form-wrap {
  position: relative;
  text-align: center;
  height: 100%;
  width: 100%;
    .login-form-wrap {
        position: relative;
        text-align: center;
        height: 100%;
        width: 100%;
  .logo {
    width: 127px;
    height: 51px;
    margin: 0 auto;
    margin-top: 104px;
    margin-bottom: 16px;
  }
        .logo {
            width: 127px;
            height: 51px;
            margin: 0 auto;
            margin-top: 104px;
            margin-bottom: 16px;
        }
  .lowerRightCorner {
    position: absolute;
    right: 0;
    bottom: 0;
    width: 425.61rpx;
    height: 316.46rpx;
  }
        .lowerRightCorner {
            position: absolute;
            right: 0;
            bottom: 0;
            width: 425.61rpx;
            height: 316.46rpx;
        }
  .title {
    font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
    font-weight: 400;
    font-size: 84rpx;
    line-height: 82rpx;
    letter-spacing: 1px;
    text-shadow: 0px 8px 8px rgba(0, 0, 0, 0.18);
    text-align: center;
    font-style: normal;
    text-transform: none;
    color: #1452D3;
    text-align: center;
    margin-bottom: 168rpx;
  }
  .user-name,.pass-word {
    display: flex;
    //justify-content: center;
    align-items: center;
    margin-left: 62rpx;
    margin-right: 58rpx;
    height: 118rpx;
    border-bottom: 2rpx solid #E3E3E3;
    img {
      width: 40rpx;
      height: 40rpx;
      margin-right: 12rpx;
    }
    :deep(uni-input) {
      height: 40rpx;
      line-height: 40rpx;
      width: 80%;
      margin-top: 20rpx;
    }
    :deep(.uni-input-placeholder) {
      font-size: 32rpx;
      color: #E3E3E3;
      font-weight: 500;
    }
    //:deep(.uni-input-input) {
    //  top: 10px !important;
    //}
  }
  .pass-word {
  }
        .title {
            font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
            font-weight: 400;
            font-size: 84rpx;
            line-height: 82rpx;
            letter-spacing: 1px;
            text-shadow: 0px 8px 8px rgba(0, 0, 0, 0.18);
            text-align: center;
            font-style: normal;
            text-transform: none;
            color: #1452D3;
            text-align: center;
            margin-bottom: 168rpx;
        }
  input {
    @apply pb-6rpx mb-10rpx text-left;
  }
        .user-name,
        .pass-word {
            display: flex;
            //justify-content: center;
            align-items: center;
            margin-left: 62rpx;
            margin-right: 58rpx;
            height: 118rpx;
            border-bottom: 2rpx solid #E3E3E3;
  .tips {
    @apply mt-8rpx mb-60rpx;
            image {
                width: 40rpx;
                height: 40rpx;
                margin-right: 12rpx;
            }
    color: $u-info;
  }
            :deep(uni-input) {
                height: 40rpx;
                line-height: 40rpx;
                width: 80%;
                margin-top: 20rpx;
            }
  .login-btn {
    @apply flex items-center justify-center py-12rpx px-0 text-30rpx  border-none;
    background: #5a93e6;
    color: white;
    width: 590rpx;
    height: 76rpx;
    border-radius: 40rpx 40rpx 40rpx 40rpx;
    margin-top: 100rpx;
            :deep(.uni-input-placeholder) {
                font-size: 32rpx;
                color: #E3E3E3;
                font-weight: 500;
            }
        }
    &::after {
      @apply border-none;
    }
  }
        .remember-password {
            display: flex;
            justify-content: flex-end;
            align-items: center;
            margin-left: 62rpx;
            margin-right: 58rpx;
            margin-top: 20rpx;
            color: #666;
            font-size: 28rpx;
            label {
                display: flex;
                align-items: center;
            }
        }
  .alternative {
    @apply flex justify-between mt-30rpx;
        input {
            @apply pb-6rpx mb-10rpx text-left;
        }
    color: $u-tips-color;
  }
}
        .tips {
            @apply mt-8rpx mb-60rpx;
            color: $u-info;
        }
.login-type-wrap {
  @apply flex justify-between pt-350rpx px-150rpx pb-150rpx;
        .login-btn {
            z-index: 1;
            @apply flex items-center justify-center py-12rpx px-0 text-30rpx border-none;
            background: #5a93e6;
            color: white;
            width: 590rpx;
            height: 76rpx;
            border-radius: 40rpx 40rpx 40rpx 40rpx;
            margin-top: 100rpx;
  .item {
    @apply flex items-center flex-col text-28rpx;
            &::after {
                @apply border-none;
            }
        }
    color: $u-content-color;
  }
}
        .alternative {
            @apply flex justify-between mt-30rpx;
            color: $u-tips-color;
        }
    }
.hint {
  @apply px-40rpx py-20rpx text-24rpx;
    .login-type-wrap {
        @apply flex justify-between pt-350rpx px-150rpx pb-150rpx;
  color: $u-tips-color;
        .item {
            @apply flex items-center flex-col text-28rpx;
            color: $u-content-color;
        }
    }
  .link {
    color: $u-warning;
  }
}
</style>
    .hint {
        @apply px-40rpx py-20rpx text-24rpx;
        color: $u-tips-color;
        .link {
            color: $u-warning;
        }
    }
</style>
src/pages/user/index.vue
@@ -1,48 +1,48 @@
<!-- 我的 -->
<template>
  <view class="page-wrap">
  <view class="userBox">
      <view class="flex items-center pb-30rpx pl-30rpx pr-20rpx">
        <view class="mr-20rpx">
          <u-avatar :src="user.avatar" size="70" />
        </view>
        <view class="flex-1">
          <view class="userName">{{ userStore?.userInfo?.nick_name }}</view>
          <view class="departs">
            <image src="@/static/images/user/mobile.svg" alt="" />
            <span>{{user.deptName}}</span>
          </view>
        </view>
        <view class="rightLogo">
          <image src="@/static/images/user/logo2.png" alt="" />
        </view>
      </view>
      <view class="personalInformation">
        <div class="message">
          <div class="messagebox">个人信息</div>
          <div class="editInfo" @click="handelEdit(1)">点击编辑
            <image src="@/static/images/user/rightBtn.svg" alt="" />
          </div>
        </div>
        <div class="passwordBox">
          <div class="messagebox">修改密码</div>
          <div class="editInfo" @click="handelEdit(2)">点击编辑 <image src="@/static/images/user/rightBtn.svg" alt="" />
          </div>
        </div>
      </view>
    <view class="flex items-center pb-30rpx pl-30rpx pr-20rpx">
      <view class="mr-20rpx">
        <u-avatar :src="user.avatar" size="70" />
      </view>
      <view class="flex-1">
        <view class="userName">{{ userStore?.userInfo?.nick_name }}</view>
        <view class="departs">
          <img src="/src/static/images/user/mobile.svg" alt="" />
          <span>{{user.deptName}}</span>
        </view>
      </view>
      <view class="rightLogo">
        <img src="/src/static/images/user/logo2.png" alt="" />
      </view>
    </view>
    <view class="personalInformation">
      <div class="message">
        <div class="messagebox">个人信息</div>
        <div class="editInfo" @click="handelEdit(1)">点击编辑
          <img src="/src/static/images/user/rightBtn.svg" alt="" />
        </div>
      </div>
      <div class="passwordBox">
        <div class="messagebox">修改密码</div>
        <div class="editInfo" @click="handelEdit(2)">点击编辑 <img src="/src/static/images/user/rightBtn.svg" alt="" />
        </div>
      </div>
    </view>
    <view class="mt-20rpx">
      <div class="goOutStyle" @click="logOut">退出登录</div>
    </view>
    <div class="bottomLogo"><img src="/src/static/images/user/logo1.png" alt="" /></div>
      <view class="mt-20rpx">
        <div class="goOutStyle" @click="logOut">退出登录</div>
      </view>
      <div class="bottomLogo"><image src="@/static/images/user/logo1.png" alt="" /></div>
  </view>
  </view>
</template>
<script setup>
  import {
    getUserInfo,
    updateInfo,
    updatePassword
  } from '@/api/user/index.js';
  import {
    useClipboard
@@ -71,14 +71,14 @@
  function logOut() {
    userStore.setUserInfo(null)
    uni.redirectTo({
    uni.reLaunch({
      url: '/pages/login/index'
    })
  }
  function getDeviceRegion() {
    getDeviceRegionApi().then(res => {
      // console.log(res.data.data, '用户设备')
    })
  }
  const handelEdit = (val) => {
@@ -113,9 +113,10 @@
    width: 100%;
    height: 100%;
    padding-top: 88rpx;
  }
.userBox {
     padding-top: 88rpx;
}
  .userName {
    font-weight: 700;
    font-size: 36rpx;
@@ -127,6 +128,11 @@
    display: flex;
    align-items: center;
    margin-top: 22rpx;
    image {
      width: 28rpx;
      height: 28rpx;
    }
    span {
      font-family: "Source Han Sans CN";
@@ -141,7 +147,7 @@
    width: 200rpx;
    height: 80rpx;
    img {
    image {
      width: 100%;
      height: 100%;
    }
@@ -155,7 +161,7 @@
    margin: 0 auto;
    text-align: center;
    img {
    image {
      width: 128rpx;
      height: 52rpx;
    }
@@ -203,7 +209,7 @@
        display: flex;
        align-items: center;
        img {
        image {
          width: 28rpx;
          height: 28rpx;
        }
@@ -211,13 +217,13 @@
    }
    .message {
      background: url('/src/static/images/user/info.png') no-repeat center;
      background: url(@/static/images/user/info.png) no-repeat center;
      background-size: 100% 100%;
    }
    .passwordBox {
      background: url('/src/static/images/user/password.png') no-repeat center;
      background: url(@/static/images/user/password.png) no-repeat center;
      background-size: 100% 100%;
    }
src/static/images/app-logo.png
src/static/images/tabbar/icon_home.png
Binary files differ
src/static/images/tabbar/icon_home1.png
src/static/images/tabbar/icon_home2.png
src/static/images/tabbar/icon_home_selected.png

src/static/images/tabbar/icon_list.png

src/static/images/tabbar/icon_list_selected.png

src/static/images/tabbar/icon_me.png

src/static/images/tabbar/icon_me_selected.png

src/static/images/tabbar/icon_order.png
src/static/images/tabbar/icon_order_selected.png
src/subPackages/droneConsole/index.vue
@@ -10,12 +10,10 @@
import dayjs from "dayjs";
import {useUserStore} from "@/store/index.js";
const wayLineJodInfoId = ref(null)
const queryParams = ref({})
const viewUrl = computed(() => {
  return getWebViewUrl('/DroneConsole', {
    wayLineJodInfoId: wayLineJodInfoId.value,
  })
  return getWebViewUrl('/DroneConsole', queryParams.value)
})
function onPostMessage(data) {
@@ -81,21 +79,23 @@
}
onLoad((options) => {
  wayLineJodInfoId.value = options.wayLineJodInfoId
  queryParams.value = options
})
// onShow(() => {
//   // #ifdef APP-PLUS
//   plus.screen.lockOrientation("landscape-primary");
//   // #endif
// });
//
// onHide(() => {
//   // #ifdef APP-PLUS
//   plus.screen.lockOrientation("portrait-primary");
//   // #endif
// });
onShow(() => {
  // #ifdef APP-PLUS
  plus.navigator.setFullscreen(true); // 加入全屏
  plus.screen.lockOrientation("landscape-primary");//横屏
  // #endif
});
onHide(() => {
  // #ifdef APP-PLUS
  plus.navigator.setFullscreen(false);
  plus.screen.lockOrientation("portrait-primary");
  // #endif
});
</script>
<style scoped lang="scss">
src/subPackages/inProgress/index.vue
@@ -17,7 +17,7 @@
    });
  } else if (data.type === 'control') {
    uni.navigateTo({
      url: `/subPackages/droneConsole/index?wayLineJodInfoId=${wayLineJodInfoId.value}`
      url: `/subPackages/droneConsole/index?wayLineJodInfoId=${wayLineJodInfoId.value}&dockSn=${data.dockSn}`
    });
  }
}
@@ -29,6 +29,10 @@
onShow(function () {
  showComponent.value = true
  // #ifdef APP-PLUS
  plus.navigator.setFullscreen(false);
  plus.screen.lockOrientation("portrait-primary");
  // #endif
})
onHide(() =>{
src/subPackages/userDetail/infos/index.vue
@@ -17,7 +17,7 @@
                    <div class="rowTitle">手机号</div>
                    <u-input input-align="right" v-model="userInfo.phone" type="number" placeholder="请输入手机号"
                        class="input-item" />
                </div>
                <div class="orderRow">
                    <div class="rowTitle">邮箱</div>
@@ -34,13 +34,18 @@
</template>
<script setup>
    import {getEnvObj, getWebViewUrl} from "@/utils/index.js";
    import {
        getEnvObj,
        getWebViewUrl
    } from "@/utils/index.js";
    import {
        getUserInfo,
        updateInfo,
        updatePassword
    } from '@/api/user/index.js';
    import {useUserStore} from "@/store/index.js";
    import {
        useUserStore
    } from "@/store/index.js";
    const userInfo = ref({
        id: '',
        avatar: '',
@@ -56,7 +61,7 @@
        if (!phone) return true
        const phoneRegex = /^1[3-9]\d{9}$/
        if (!phoneRegex.test(phone)) {
            uni.showToast({
                title: '请输入正确的手机号码',
                icon: 'none',
@@ -68,10 +73,10 @@
    }
    // 校验邮箱
    const validateEmail = () => {
        if (!userInfo.value.email) return true
        if (!userInfo.value.email) return true
        const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
        if (!emailRegex.test(userInfo.value.email)) {
            uni.showToast({
                title: '请输入正确的邮箱地址',
                icon: 'none',
@@ -100,76 +105,99 @@
        getUserInfoData();
    };
    const {VITE_API_BASE_URL} = getEnvObj()
    const {
        VITE_API_BASE_URL
    } = getEnvObj()
    function uploadUtil(options) {
      const {formData, file, url} = options
      return new Promise((resolve, reject) => {
        let accessToken = useUserStore()?.$state?.userInfo?.access_token;
        uni.uploadFile({
          url: `${VITE_API_BASE_URL}${url}`,
          name: 'file',
          header: {'Blade-Auth': 'bearer ' + accessToken},
          file,
          formData,
          success: (res) => {
            const resData = JSON.parse(res.data)
            if (resData.code === 200 || resData.code === 0) {
              resolve(res)
            } else {
              showToast(resData.message)
              reject(res)
            }
          },
          fail: (err) => {
            reject(err)
          }
        });
      })
        const {
            formData,
            filePath,
            url
        } = options;
        return new Promise((resolve, reject) => {
            let accessToken = useUserStore()?.$state?.userInfo?.access_token;
            uni.uploadFile({
                url: `${VITE_API_BASE_URL}${url}`,
                name: 'file',
                header: {
                    'Blade-Auth': 'bearer ' + accessToken
                },
                filePath: filePath,
                formData,
                success: (res) => {
                    const resData = JSON.parse(res.data)
                    if (resData.code === 200 || resData.code === 0) {
                        resolve(res)
                    } else {
                        showToast(resData.message)
                        reject(res)
                    }
                },
                fail: (err) => {
                    reject(err)
                }
            });
        })
    }
    const uploadAvatar = () => {
      uni.chooseImage({
        count: 1,
        success: (res) => {
          const tempFilePaths = res.tempFiles[0];
          // 显示加载中
          uni.showLoading({
            title: '上传中...'
          });
          // 上传文件
          uploadUtil({
            file: tempFilePaths,
            formData: {
              fileName: tempFilePaths.name,
              sn: 'avatar_upload'
            },
            url: '/blade-resource/oss/endpoint/put-file'
          }).then(res => {
            const resData = JSON.parse(res.data);
            if (resData.code === 200 || resData.code === 0) {
              // 更新头像显示
              userInfo.value.avatar = resData.data.link || resData.data.url;
              uni.hideLoading();
              uni.showToast({
                title: '头像上传成功',
                icon: 'success'
              });
            } else {
              throw new Error(resData.message || '上传失败');
            }
          }).catch(err => {
            uni.hideLoading();
            uni.showToast({
              title: err.message || '上传失败',
              icon: 'none'
            });
          });
        }
      });
        uni.chooseImage({
            count: 1,
            success: (res) => {
                const tempFile = res.tempFiles[0]; // 获取文件对象
                const filePath = tempFile.path || tempFile.tempFilePath;
                if (!filePath) {
                    uni.showToast({
                        title: '获取文件路径失败',
                        icon: 'none'
                    });
                    return;
                }
                let fileName = tempFile.name;
                if (!fileName) {
                    const pathWithoutProtocol = filePath.replace(/^file:\/\//, '');
                    fileName = pathWithoutProtocol.split('/').pop() || 'unknown.png';
                }
                // 显示加载中
                uni.showLoading({
                    title: '上传中...'
                });
                // 上传文件
                uploadUtil({
                    filePath: filePath,
                    formData: {
                        fileName: fileName,
                        sn: 'avatar_upload'
                    },
                    url: '/blade-resource/oss/endpoint/put-file'
                }).then(res => {
                    const resData = JSON.parse(res.data);
                    if (resData.code === 200 || resData.code === 0) {
                        // 更新头像显示
                        userInfo.value.avatar = resData.data.link || resData.data.url;
                        uni.hideLoading();
                        uni.showToast({
                            title: '头像上传成功',
                            icon: 'success'
                        });
                    } else {
                        throw new Error(resData.message || '上传失败');
                    }
                }).catch(err => {
                    uni.hideLoading();
                    uni.showToast({
                        title: err.message || '上传失败',
                        icon: 'none'
                    });
                });
            }
        });
    }
    const submit = () => {
        if (!validatePhone() || !validateEmail()) return
@@ -181,6 +209,7 @@
                    icon: 'none',
                    duration: 2000
                });
                    getUserInfoData()
            } else {
                uni.showToast({
@@ -188,10 +217,11 @@
                    icon: 'none',
                    duration: 2000
                });
                    getUserInfoData()
            }
        });
        getUserInfoData()
    };
    onShow(async () => {
        getUserInfoData()
@@ -200,19 +230,16 @@
<style lang="scss" scoped>
    .container {
        width: 100%;
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
    }
    .avatarBox {
        width: 228rpx;
        height: 228rpx;
        margin:76rpx 0;
        margin: 76rpx 0;
    }
    .detailBox {
@@ -221,9 +248,11 @@
        background: #FFFFFF;
        border-radius: 12rpx;
        box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
        .detailCon {
            padding: 0 24rpx;
        }
        .orderRow {
            display: flex;
            justify-content: space-between;
@@ -231,7 +260,7 @@
            height: 96rpx;
            border-bottom: 2rpx solid #f5f5f5;
            color: #7b7b7b;
            .rowTitle {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
@@ -239,6 +268,7 @@
                color: #222324;
                white-space: nowrap;
            }
            ::v-deep .u-input {
                border: none !important;
                background: transparent !important;
@@ -259,12 +289,13 @@
    .btngroup {
        display: flex;
        position: absolute;
        bottom:150rpx;
        bottom: 150rpx;
    }
    .u-button:first-child{
    .u-button:first-child {
        margin-right: 30rpx;
    }
    .custom-style {
        width: 276rpx;
        height: 76rpx;
src/subPackages/userDetail/password/index.vue
@@ -6,19 +6,16 @@
                <div class="orderRow">
                    <!-- type="password" :password-icon="passwordIcon" -->
                    <div class="rowTitle">原始密码</div>
                    <u-input input-align="right" v-model="passwordForm.oldPassword"   placeholder="请输入"
                        class="input-item" />
                    <input type="password" v-model="passwordForm.oldPassword" placeholder="请输入" class="input-item" />
                </div>
                <div class="orderRow">
                    <div class="rowTitle">新密码</div>
                    <u-input input-align="right" v-model="passwordForm.newPassword" placeholder="请输入"
                        class="input-item" />
                    <input type="password" v-model="passwordForm.newPassword" placeholder="请输入" class="input-item" />
                </div>
                <div class="orderRow">
                    <div class="rowTitle">确认密码</div>
                    <u-input input-align="right" v-model="passwordForm.newPassword1"  placeholder="请输入"
                        class="input-item" />
                    <input type="password" v-model="passwordForm.newPassword1" placeholder="请输入" class="input-item" />
                </div>
            </div>
        </view>
@@ -39,16 +36,49 @@
        updateInfo,
        updatePassword
    } from '@/api/user/index.js';
 const passwordIcon={
      show: 'fas fa-eye',     // 显示密码时的图标
      hide: 'fas fa-eye-slash' // 隐藏密码时的图标
    }
    const passwordIcon = {
        show: 'fas fa-eye', // 显示密码时的图标
        hide: 'fas fa-eye-slash' // 隐藏密码时的图标
    }
    const userStore = useUserStore();
    const passwordForm = ref({
        oldPassword: '',
        newPassword: '',
        newPassword1: '',
    });
    // 校验密码不能包含中文
    const validatePasswordNoChinese = (password) => {
        const chineseRegex = /[\u4e00-\u9fa5]/;
        if (chineseRegex.test(password)) {
            uni.showToast({
                title: '密码不能包含中文字符',
                icon: 'none',
                duration: 2000
            });
            return false;
        }
        return true;
    };
    // 校验密码复杂度(长度≥6位,包含字母和数字)
    const validatePasswordStrength = (password) => {
        if (password.length < 6) {
            uni.showToast({
                title: '密码长度不能少于6位',
                icon: 'none',
                duration: 2000
            });
            return false;
        }
        // const hasLetter = /[a-zA-Z]/.test(password);
        // const hasNumber = /\d/.test(password);
        // if (!hasLetter || !hasNumber) {
        //   ElMessage.error('密码需同时包含字母和数字');
        //   return false;
        // }
        return true;
    };
    const clearForm = () => {
        passwordForm.value = {
            oldPassword: '',
@@ -60,7 +90,41 @@
        clearForm();
    };
    const submit = () => {
        // 原始密码校验
        if (!passwordForm.value.oldPassword) {
            uni.showToast({
                title: '请输入原始密码',
                icon: 'none',
                duration: 2000
            });
            return;
        }
        // 中文校验
        if (!validatePasswordNoChinese(passwordForm.value.newPassword)) return;
        // 新密码校验
        if (!passwordForm.value.newPassword) {
            uni.showToast({
                title: '请输入新密码',
                icon: 'none',
                duration: 2000
            });
            return;
        }
        // 确认密码校验
        if (passwordForm.value.newPassword !== passwordForm.value.newPassword1) {
            uni.showToast({
                title: '两次输入的新密码不一致',
                icon: 'none',
                duration: 2000
            });
            return;
        }
        // 复杂度校验
        if (!validatePasswordStrength(passwordForm.value.newPassword)) return;
        updatePassword(md5(passwordForm.value.oldPassword),
            md5(passwordForm.value.newPassword),
            md5(passwordForm.value.newPassword1)).then(res => {
@@ -72,7 +136,8 @@
                });
                clearForm();
                userStore.setUserInfo(null)
                uni.redirectTo({
                uni.removeStorageSync('rememberedUser');
                uni.reLaunch({
                    url: '/pages/login/index'
                })
@@ -103,23 +168,25 @@
    .detailBox {
        margin-top: 40rpx;
    width: 702rpx;
        width: 702rpx;
        min-height: 326rpx;
        background: #FFFFFF;
        border-radius: 12rpx;
        box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.detailCon {
        .detailCon {
            padding: 0 24rpx;
        }
        .orderRow {
        display: flex;
        justify-content: space-between;
        align-items: center;
        height: 96rpx;
        border-bottom: 2rpx solid #f5f5f5;
        color: #7b7b7b;
            display: flex;
            justify-content: space-between;
            align-items: center;
            height: 96rpx;
            border-bottom: 2rpx solid #f5f5f5;
            color: #7b7b7b;
            .rowTitle {
                font-family: Source Han Sans CN, Source Han Sans CN;
@@ -128,31 +195,39 @@
                color: #222324;
                white-space: nowrap;
            }
            ::v-deep .u-input {
                border: none !important;
                background: transparent !important;
            }
            ::v-deep .u-input__input {
            input.input-item {
                text-align: right;
                border: none !important;
                box-shadow: none !important;
                background: transparent !important;
                box-shadow: none !important;
                padding: 0 !important;
                margin: 0 !important;
                height: auto !important;
                outline: none;
                font-size: 30rpx;
                color: #222324;
                font-family: Source Han Sans CN, Source Han Sans CN;
            }
            input.input-item::placeholder {
                color: #c9c9c9;
                font-size: 30rpx;
            }
        }
    }
.btngroup {
    .btngroup {
        display: flex;
        position: absolute;
        bottom:150rpx;
        bottom: 150rpx;
    }
    .u-button:first-child{
    .u-button:first-child {
        margin-right: 30rpx;
    }
    .custom-style {
        width: 276rpx;
        height: 76rpx;
src/utils/common/index.js
@@ -40,7 +40,14 @@
    })
  })
}
export function getStatusBarHeight() {
  try {
    const systemInfo = uni.getSystemInfoSync();
    return systemInfo.statusBarHeight || 0;
  } catch (error) {
    return 0;
  }
}
export function getEnvObj() {
  const envName = JSON.parse(__APP_ENV__?.UNI_CUSTOM_DEFINE||null)?.ENV_NAME
  return configEnv[envName || import.meta.env.VITE_APP_ENV] || {}
@@ -48,10 +55,13 @@
export function getWebViewUrl (targetUrl, otherParams) {
  const userStore = useUserStore()
  const url = getEnvObj().VITE_APP_WEBVIEW_URL
  const uniPlatform = __APP_ENV__.UNI_PLATFORM
  const statusBarHeight = getStatusBarHeight();
  // 1. 处理用户参数
  const userParams = userStore?.userInfo ? JSON.stringify(userStore.userInfo) : '{}'
  // 2. 构建查询参数字符串
  let queryString = `params=${encodeURIComponent(userParams)}`
let queryString = `params=${encodeURIComponent(userParams)}&topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}`
  // 3. 处理 otherParams 对象
  if (otherParams && typeof otherParams === 'object') {
    Object.keys(otherParams).forEach(key => {
src/utils/request/interceptors.js
@@ -82,7 +82,7 @@
    if (response.statusCode === 401) {
      const pages = getCurrentPages()
      const currentPage = pages[pages.length - 1]
      uni.redirectTo({
      uni.reLaunch({
        url: `/pages/login/index?redirect=/${currentPage.route}`
      })
    }