shuishen
2025-09-29 b9a26af5b08d3d92a6c38fd90e023bc4706f19eb
feat:一些基础文件格式化处理
44 files modified
598 ■■■■ changed files
build/config/index.js 2 ●●● patch | view | raw | blame | history
build/config/proxy.js 10 ●●●● patch | view | raw | blame | history
build/plugins/autoImport.js 6 ●●●● patch | view | raw | blame | history
build/plugins/component.js 6 ●●●● patch | view | raw | blame | history
build/plugins/index.js 20 ●●●● patch | view | raw | blame | history
build/plugins/replaceUrl.js 6 ●●●● patch | view | raw | blame | history
build/plugins/unocss.js 6 ●●●● patch | view | raw | blame | history
build/plugins/visualizer.js 6 ●●●● patch | view | raw | blame | history
cz.config.js 2 ●●● patch | view | raw | blame | history
scripts/post-upgrade.js 14 ●●●● patch | view | raw | blame | history
scripts/verify-commit.js 23 ●●●● patch | view | raw | blame | history
src/App.vue 29 ●●●● patch | view | raw | blame | history
src/components/agree-privacy/index.vue 67 ●●●●● patch | view | raw | blame | history
src/components/lang-select/index.vue 39 ●●●● patch | view | raw | blame | history
src/components/theme-picker/index.vue 23 ●●●● patch | view | raw | blame | history
src/hooks/use-clipboard/index.js patch | view | raw | blame | history
src/hooks/use-loading/index.js patch | view | raw | blame | history
src/hooks/use-location/index.js patch | view | raw | blame | history
src/hooks/use-modal/index.js patch | view | raw | blame | history
src/hooks/use-permission/index.js patch | view | raw | blame | history
src/hooks/use-share/index.js patch | view | raw | blame | history
src/hooks/use-theme/index.js patch | view | raw | blame | history
src/locales/index.js patch | view | raw | blame | history
src/main.js patch | view | raw | blame | history
src/pages/inspectionTask/index.vue 21 ●●●● patch | view | raw | blame | history
src/pages/login/index.vue 91 ●●●● patch | view | raw | blame | history
src/pages/map/index.vue 16 ●●●● patch | view | raw | blame | history
src/pages/user/index.vue 23 ●●●●● patch | view | raw | blame | history
src/pages/work/index.vue 38 ●●●●● patch | view | raw | blame | history
src/plugins/index.js patch | view | raw | blame | history
src/plugins/permission.js patch | view | raw | blame | history
src/plugins/ui.js patch | view | raw | blame | history
src/router/index.js patch | view | raw | blame | history
src/store/index.js patch | view | raw | blame | history
src/store/modules/app/index.js 44 ●●●● patch | view | raw | blame | history
src/store/modules/user/index.js 52 ●●●● patch | view | raw | blame | history
src/subPackages/404/index.vue 7 ●●●●● patch | view | raw | blame | history
src/subPackages/theme/index.vue 47 ●●●● patch | view | raw | blame | history
src/utils/auth/index.js patch | view | raw | blame | history
src/utils/common/index.js patch | view | raw | blame | history
src/utils/modals/index.js patch | view | raw | blame | history
src/utils/request/index.js patch | view | raw | blame | history
src/utils/request/interceptors.js patch | view | raw | blame | history
src/utils/storage/index.js patch | view | raw | blame | history
build/config/index.js
@@ -1 +1 @@
export * from './proxy';
export * from './proxy'
build/config/proxy.js
@@ -9,15 +9,15 @@
 * 2025-09-28 09:31:16
 */
export const createViteProxy = (env) => {
  const { VITE_APP_PROXY, VITE_API_PREFIX, VITE_API_BASE_URL } = env;
  const { VITE_APP_PROXY, VITE_API_PREFIX, VITE_API_BASE_URL } = env
  // 不使用代理直接返回
  if (!JSON.parse(VITE_APP_PROXY)) return undefined;
  if (!JSON.parse(VITE_APP_PROXY)) return undefined
  const proxy = {
    [VITE_API_PREFIX]: {
      target: VITE_API_BASE_URL,
      changeOrigin: true,
      rewrite: path => path.replace(new RegExp(`^${VITE_API_PREFIX}`), ''),
    },
  };
  return proxy;
};
  }
  return proxy
}
build/plugins/autoImport.js
@@ -12,7 +12,7 @@
 * @name AutoImportDeps
 * @description 按需加载,自动引入
 */
import AutoImport from 'unplugin-auto-import/vite';
import AutoImport from 'unplugin-auto-import/vite'
export const AutoImportDeps = () => {
  return AutoImport({
@@ -20,5 +20,5 @@
    // 禁用生成 .d.ts 文件,因为项目已转为 JavaScript
    dts: false,
    vueTemplate: true,
  });
};
  })
}
build/plugins/component.js
@@ -2,11 +2,11 @@
 * @name AutoRegistryComponents
 * @description 按需加载,自动引入
 */
import Components from 'unplugin-vue-components/vite';
import Components from 'unplugin-vue-components/vite'
export const AutoRegistryComponents = () => {
  return Components({
    // 禁用生成 .d.ts 文件,因为项目已转为 JavaScript
    dts: false,
  });
};
  })
}
build/plugins/index.js
@@ -1,11 +1,11 @@
import uniPlugin from '@dcloudio/vite-plugin-uni';
import ViteRestart from 'vite-plugin-restart';
import { AutoImportDeps } from './autoImport';
import uniPlugin from '@dcloudio/vite-plugin-uni'
import ViteRestart from 'vite-plugin-restart'
import { AutoImportDeps } from './autoImport'
// import { ConfigImageminPlugin } from './imagemin';
// import { ReplaceUrlPlugin } from './replaceUrl';
import { AutoRegistryComponents } from './component';
import { MCPPlugin } from './mcp';
import { ConfigUnoCSSPlugin } from './unocss';
import { AutoRegistryComponents } from './component'
import { MCPPlugin } from './mcp'
import { ConfigUnoCSSPlugin } from './unocss'
export default function createVitePlugins(isBuild) {
  const vitePlugins = [
@@ -23,7 +23,7 @@
    }),
    // 为项目开启 MCP Server
    MCPPlugin(),
  ];
  ]
  if (isBuild) {
    const buildPlugins = [
@@ -33,9 +33,9 @@
      // CleanImagePlugin(),
      // 打包视图分析
      // VisualizerPlugin(),
    ];
    vitePlugins.push(...buildPlugins);
    ]
    vitePlugins.push(...buildPlugins)
  }
  return vitePlugins;
  return vitePlugins
}
build/plugins/replaceUrl.js
@@ -2,12 +2,12 @@
 * @name ReplaceImageUrl
 * @description 替换图片地址
 */
import replaceImageUrl from 'vite-plugin-replace-image-url';
import replaceImageUrl from 'vite-plugin-replace-image-url'
export const ReplaceUrlPlugin = () => {
  return replaceImageUrl({
    publicPath: 'https://photo.example.com/miniprogram',
    sourceDir: 'src/static',
    verbose: true,
  });
};
  })
}
build/plugins/unocss.js
@@ -2,8 +2,8 @@
 * @name ConfigUnoCSSPlugin
 * @description UnoCSS相关配置
 */
import UnoCSS from 'unocss/vite';
import UnoCSS from 'unocss/vite'
export const ConfigUnoCSSPlugin = () => {
  return UnoCSS();
};
  return UnoCSS()
}
build/plugins/visualizer.js
@@ -12,12 +12,12 @@
 * @name VisualizerPlugin
 * @description 打包视图分析
 */
import { visualizer } from 'rollup-plugin-visualizer';
import { visualizer } from 'rollup-plugin-visualizer'
export const VisualizerPlugin = () => {
  return visualizer({
    emitFile: false,
    filename: 'stats.html', // 分析图生成的文件名
    open: true, // 如果存在本地服务端口,将在打包后自动展示
  });
};
  })
}
cz.config.js
@@ -55,4 +55,4 @@
  defaultIssues: '',
  defaultScope: '',
  defaultSubject: '',
};
}
scripts/post-upgrade.js
@@ -2,7 +2,7 @@
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
// # 只需要执行下面的命令即可
import { exec } from 'node:child_process';
import { exec } from 'node:child_process'
// 定义要执行的命令
const dependencies = [
@@ -20,17 +20,17 @@
  '@dcloudio/uni-mp-harmony',
  // vue 已经内置了 @vue/runtime-core,这里移除掉
  '@vue/runtime-core',
];
]
// 使用exec执行命令
exec(`pnpm remove ${dependencies.join(' ')}`, (error, stdout, stderr) => {
  if (error) {
    // 如果有错误,打印错误信息
    console.error(`执行出错: ${error}`);
    return;
    console.error(`执行出错: ${error}`)
    return
  }
  // 打印正常输出
  console.log(`stdout: ${stdout}`);
  console.log(`stdout: ${stdout}`)
  // 如果有错误输出,也打印出来
  console.error(`stderr: ${stderr}`);
});
  console.error(`stderr: ${stderr}`)
})
scripts/verify-commit.js
@@ -3,26 +3,25 @@
 * @link https://github.com/toplenboren/simple-git-hooks
 * @see 参考:https://github.com/vuejs/vue-next/blob/master/.github/commit-convention.md
 */
import { readFileSync } from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import pico from 'picocolors';
import { readFileSync } from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import pico from 'picocolors'
const msgPath = path.resolve('.git/COMMIT_EDITMSG');
const msg = readFileSync(msgPath, 'utf-8').trim();
const msgPath = path.resolve('.git/COMMIT_EDITMSG')
const msg = readFileSync(msgPath, 'utf-8').trim()
const commitRE
    = /^(?:revert: )?(?:feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|mod|release|strengthen)(?:\(.+\))?: .{1,50}/;
  = /^(?:revert: )?(?:feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|mod|release|strengthen)(?:\(.+\))?: .{1,50}/
if (!commitRE.test(msg)) {
  console.log(pico.yellow(`\n提交的信息: ${msg}\n`));
  console.log(pico.yellow(`\n提交的信息: ${msg}\n`))
  console.error(
    `  ${pico.white(pico.bgRed(' 格式错误 '))} ${pico.red(
      '无效的提交信息格式.',
    )}\n\n${
      pico.red('  正确的提交消息格式. 例如:\n\n')
    )}\n\n${pico.red('  正确的提交消息格式. 例如:\n\n')
    }    ${pico.green('feat: add a new feature')}\n`
    + `    ${pico.green('fix: fixed an bug')}`,
  );
  process.exit(1);
  )
  process.exit(1)
}
src/App.vue
@@ -9,35 +9,34 @@
 * 2025-09-28 09:31:16
-->
<script setup>
import { onHide, onLaunch, onShow } from "@dcloudio/uni-app"
import { useAppStore } from "@/store"
import { mpUpdate } from "@/utils/index"
import { onHide, onLaunch, onShow } from "@dcloudio/uni-app";
import { useAppStore } from "@/store";
import { mpUpdate } from "@/utils/index";
const appStore = useAppStore()
const appStore = useAppStore();
onLaunch(() => {
  console.log("App Launch")
  console.log("App Launch");
  // 初始化系统信息
  appStore.initSystemInfo()
  appStore.initSystemInfo();
  // #ifdef MP-WEIXIN
  mpUpdate()
  mpUpdate();
  // #endif
})
});
onShow(() => {
  console.log("App Show")
})
  console.log("App Show");
});
onHide(() => {
  console.log("App Hide")
})
  console.log("App Hide");
});
</script>
<style lang="scss">
/* 每个页面公共css */
@import 'uview-plus/index.scss';
@import '@/static/styles/common.scss';
@import "uview-plus/index.scss";
@import "@/static/styles/common.scss";
</style>
src/components/agree-privacy/index.vue
@@ -7,19 +7,27 @@
      <view class="flex flex-col">
        <span class="pt-30rpx text-black font-bold">{{ initSubTitle }}</span>
        <span class="pt-30rpx text-sm text-black">1.为向您提供基本的服务,我们会遵循正当、合法、必要的原则收集和使用必要的信息。</span>
        <span class="pt-30rpx text-sm text-black">2.基于您的授权我们可能会收集和使用您的相关信息,您有权拒绝或取消授权。</span>
        <span class="pt-30rpx text-sm text-black">3.未经您的授权同意,我们不会将您的信息共享给第三方或用于您未授权的其他用途。</span>
        <span class="pt-30rpx text-sm text-black">4.详细信息请您完整阅读<text class="text-decoration" @click="openPrivacyContract">{{
          initPrivacyContractName
        }}</text></span>
        <span class="pt-30rpx text-sm text-black"
          >1.为向您提供基本的服务,我们会遵循正当、合法、必要的原则收集和使用必要的信息。</span
        >
        <span class="pt-30rpx text-sm text-black"
          >2.基于您的授权我们可能会收集和使用您的相关信息,您有权拒绝或取消授权。</span
        >
        <span class="pt-30rpx text-sm text-black"
          >3.未经您的授权同意,我们不会将您的信息共享给第三方或用于您未授权的其他用途。</span
        >
        <span class="pt-30rpx text-sm text-black"
          >4.详细信息请您完整阅读<text
            class="text-decoration"
            @click="openPrivacyContract"
            >{{ initPrivacyContractName }}</text
          ></span
        >
      </view>
      <view class="mt-30rpx flex items-center justify-around pt-10rpx">
        <view class="min-w-100px">
          <button class="button button-default" @click="disagree">
            拒绝
          </button>
          <button class="button button-default" @click="disagree">拒绝</button>
        </view>
        <view class="min-w-100px">
          <button
@@ -39,41 +47,41 @@
<script setup>
const props = withDefaults(defineProps(), {
  modelValue: false,
  title: '',
  subTitle: '',
  title: "",
  subTitle: "",
  disableCheckPrivacy: true,
  agreePrivacyId: 'agree-btn',
  agreePrivacyId: "agree-btn",
});
const emit = defineEmits([
  'update:modelValue',
  'needPrivacyAuthorization',
  'agree',
  'disagree',
  "update:modelValue",
  "needPrivacyAuthorization",
  "agree",
  "disagree",
]);
// 初始化的标题
const initTitle = ref('隐私政策概要');
const initTitle = ref("隐私政策概要");
// 初始化的副标题
const initSubTitle = ref('');
const initSubTitle = ref("");
// 隐私政策
const initPrivacyContractName = ref('隐私政策');
const initPrivacyContractName = ref("隐私政策");
// 打开隐私
function openAgreePrivacy() {
  emit('update:modelValue', true);
  emit("update:modelValue", true);
}
// 关闭隐私
function closeAgreePrivacy() {
  emit('update:modelValue', false);
  emit("update:modelValue", false);
}
// 需要初始化的数据
function initData() {
  initTitle.value = props.title || initTitle.value;
  initSubTitle.value
    = props.subTitle
      || `亲爱的用户,感谢您一直以来的支持!为了更好地保护您的权益,同时遵守相关监管要求,请认真阅读${initPrivacyContractName.value},特向您说明如下:`;
  initSubTitle.value =
    props.subTitle ||
    `亲爱的用户,感谢您一直以来的支持!为了更好地保护您的权益,同时遵守相关监管要求,请认真阅读${initPrivacyContractName.value},特向您说明如下:`;
}
// 检测是否授权
@@ -89,8 +97,7 @@
          // 需要弹出隐私协议
          openAgreePrivacy();
        }
      }
      else {
      } else {
        // 用户已经同意过隐私协议,所以不需要再弹出隐私协议,也能调用已声明过的隐私接口
        // wx.getUserProfile()
      }
@@ -112,14 +119,14 @@
// 同意
function agree(e) {
  const buttonId = e.target.id || 'agree-btn';
  emit('agree', buttonId);
  emit('update:modelValue', false);
  const buttonId = e.target.id || "agree-btn";
  emit("agree", buttonId);
  emit("update:modelValue", false);
}
// 拒绝
function disagree() {
  emit('disagree');
  emit("disagree");
  closeAgreePrivacy();
}
src/components/lang-select/index.vue
@@ -14,39 +14,38 @@
</template>
<script setup>
import { useI18n } from "vue-i18n"
import { useI18n } from "vue-i18n";
const props = defineProps({
  size: {
    type: Number,
    default: 40,
    required: false
  }
})
const emit = defineEmits(["change"])
const { locale, t } = useI18n()
    required: false,
  },
});
const emit = defineEmits(["change"]);
const { locale, t } = useI18n();
const langStyle = {
  fontSize: `${props.size}rpx`
}
  fontSize: `${props.size}rpx`,
};
const langOptions = computed(() => {
  return [
    { label: t("locale.en"), value: "en" },
    { label: t("locale.zh-hans"), value: "zh-Hans" }
  ]
})
    { label: t("locale.zh-hans"), value: "zh-Hans" },
  ];
});
const langIndex = computed(() => {
  return langOptions.value.findIndex(item => {
    return item.value === locale.value
  })
})
  return langOptions.value.findIndex((item) => {
    return item.value === locale.value;
  });
});
function handleLangChange(event) {
  const lang = langOptions.value[event.detail.value].value
  locale.value = lang
  uni.setLocale(lang)
  emit("change", lang)
  const lang = langOptions.value[event.detail.value].value;
  locale.value = lang;
  uni.setLocale(lang);
  emit("change", lang);
}
</script>
<style lang="scss" scoped></style>
src/components/theme-picker/index.vue
@@ -1,9 +1,11 @@
<template>
  <view class="flex flex-wrap gap-3">
    <view
      v-for="(item, index) in colors" :key="index"
      v-for="(item, index) in colors"
      :key="index"
      class="mb-10rpx h-40rpx w-40rpx center cursor-pointer border-4rpx border-gray-300 rounded-4rpx border-solid"
      :class="{ 'border-white': theme === item.name }" :style="{ backgroundColor: item.color }"
      :class="{ 'border-white': theme === item.name }"
      :style="{ backgroundColor: item.color }"
      @click="changeTheme(item.name)"
    >
      <view v-if="theme === item.name" class="i-mdi-check text-32rpx c-#fff" />
@@ -12,25 +14,24 @@
</template>
<script setup>
import { useAppStore } from "@/store"
import { useAppStore } from "@/store";
const appStore = useAppStore()
const appStore = useAppStore();
const colors = [
  {
    name: "",
    color: "#21d59d"
    color: "#21d59d",
  },
  {
    name: "blue",
    color: "#3c9cff"
  }
]
    color: "#3c9cff",
  },
];
const theme = computed(() => appStore.getTheme)
const theme = computed(() => appStore.getTheme);
function changeTheme(theme) {
  appStore.setTheme(theme)
  appStore.setTheme(theme);
}
</script>
src/hooks/use-clipboard/index.js
src/hooks/use-loading/index.js
src/hooks/use-location/index.js
src/hooks/use-modal/index.js
src/hooks/use-permission/index.js
src/hooks/use-share/index.js
src/hooks/use-theme/index.js
src/locales/index.js
src/main.js
src/pages/inspectionTask/index.vue
@@ -17,9 +17,9 @@
</template>
<script setup>
const pagingRef = ref(null)
const pagingRef = ref(null);
const dataList = ref([])
const dataList = ref([]);
const urls = [
  "https://picsum.photos/100/100?random=1",
@@ -31,22 +31,22 @@
  "https://picsum.photos/100/100?random=7",
  "https://picsum.photos/100/100?random=8",
  "https://picsum.photos/100/100?random=9",
  "https://picsum.photos/100/100?random=10"
]
  "https://picsum.photos/100/100?random=10",
];
function queryList(pageNo, pageSize) {
  console.log("[ pageNo ] >", pageNo)
  console.log("[ pageSize ] >", pageSize)
  console.log("[ pageNo ] >", pageNo);
  console.log("[ pageSize ] >", pageSize);
  // 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
  // 这里的请求只是演示,请替换成自己的项目的网络请求,并在网络请求回调中通过pagingRef.value.complete(请求回来的数组)将请求结果传给z-paging
  setTimeout(() => {
    // 1秒之后停止刷新动画
    const list = []
    const list = [];
    for (let i = 0; i < 30; i++)
      list.push(urls[uni.$u.random(0, urls.length - 1)])
      list.push(urls[uni.$u.random(0, urls.length - 1)]);
    pagingRef.value?.complete(list)
  }, 1000)
    pagingRef.value?.complete(list);
  }, 1000);
  // this.$request
  //   .queryList({ pageNo, pageSize })
  //   .then(res => {
@@ -60,5 +60,4 @@
  //     pagingRef.value.complete(false);
  //   });
}
</script>
src/pages/login/index.vue
@@ -2,12 +2,20 @@
<template>
  <view>
    <view class="login-form-wrap">
      <view class="title">
        欢迎登录
      </view>
      <input v-model="tel" class="u-border-bottom" type="number" placeholder="请输入手机号">
      <view class="title"> 欢迎登录 </view>
      <input
        v-model="tel"
        class="u-border-bottom"
        type="number"
        placeholder="请输入手机号"
      />
      <view class="u-border-bottom my-40rpx flex">
        <input v-model="code" class="flex-1" type="number" placeholder="请输入验证码">
        <input
          v-model="code"
          class="flex-1"
          type="number"
          placeholder="请输入验证码"
        />
        <view>
          <u-code ref="uCodeRef" @change="codeChange" />
          <u-button :text="tips" type="success" size="mini" @click="getCode" />
@@ -18,9 +26,7 @@
      </button>
      <view class="alternative">
        <view class="password">
          密码登录
        </view>
        <view class="password"> 密码登录 </view>
        <view class="issue flex items-center">
          遇到问题 <text class="i-mdi-help" />
        </view>
@@ -42,9 +48,7 @@
    </view>
    <view class="hint">
      登录代表同意
      <text class="link">
        用户协议、隐私政策,
      </text>
      <text class="link"> 用户协议、隐私政策, </text>
      并授权使用您的账号信息(如昵称、头像、收获地址)以便您统一管理
    </view>
  </view>
@@ -55,76 +59,75 @@
  HOME_PATH,
  isTabBarPath,
  LOGIN_PATH,
  removeQueryString
} from "@/router"
import { setToken } from "@/utils/auth"
  removeQueryString,
} from "@/router";
import { setToken } from "@/utils/auth";
// import { useUserStore } from '@/store';
// const userStore = useUserStore();
const tel = ref("18502811111")
const code = ref("1234")
const tips = ref()
const uCodeRef = ref(null)
let redirect = HOME_PATH
const tel = ref("18502811111");
const code = ref("1234");
const tips = ref();
const uCodeRef = ref(null);
let redirect = HOME_PATH;
const inputStyle = computed(() => {
  const style = {}
  const style = {};
  if (tel.value && code.value) {
    style.color = "#fff"
    style.backgroundColor = uni.$u.color.warning
    style.color = "#fff";
    style.backgroundColor = uni.$u.color.warning;
  }
  return style
})
  return style;
});
function codeChange(text) {
  tips.value = text
  tips.value = text;
}
function getCode() {
  if (uCodeRef.value?.canGetCode) {
    // 模拟向后端请求验证码
    uni.showLoading({
      title: "正在获取验证码"
    })
      title: "正在获取验证码",
    });
    setTimeout(() => {
      uni.hideLoading()
      uni.$u.toast("验证码已发送")
      uni.hideLoading();
      uni.$u.toast("验证码已发送");
      // 通知验证码组件内部开始倒计时
      uCodeRef.value?.start()
    }, 1000)
      uCodeRef.value?.start();
    }, 1000);
  } else {
    uni.$u.toast("倒计时结束后再发送")
    uni.$u.toast("倒计时结束后再发送");
  }
}
async function submit() {
  if (!uni.$u.test.mobile(Number(tel.value))) {
    uni.$u.toast("请输入正确的手机号")
    return
    uni.$u.toast("请输入正确的手机号");
    return;
  }
  if (!code.value) {
    uni.$u.toast("请输入验证码")
    return
    uni.$u.toast("请输入验证码");
    return;
  }
  // 登录请求
  // const res = await userStore.login({ phone: tel.value, code: code.value }).catch(() => {
  //   uni.$u.toast('登录失败');
  // });
  // if (!res) return;
  setToken("1234567890")
  setToken("1234567890");
  setTimeout(() => {
    uni.$u.route({
      type: isTabBarPath(redirect) ? "switchTab" : "redirectTo",
      url: redirect
    })
  }, 800)
      url: redirect,
    });
  }, 800);
}
onLoad(options => {
onLoad((options) => {
  if (options.redirect && removeQueryString(options.redirect) !== LOGIN_PATH) {
    redirect = decodeURIComponent(options.redirect)
    redirect = decodeURIComponent(options.redirect);
  }
})
});
</script>
<style lang="scss" scoped>
src/pages/map/index.vue
@@ -1,25 +1,25 @@
<!-- 地图 -->
<template>
  <!-- 地图容器 -->
  <view id="map" class="map" style="width: 100%; height: 80vh;"></view>
  <view id="map" class="map" style="width: 100%; height: 80vh"></view>
</template>
<script setup>
  onShow(() => {
   console.log(222)
  console.log(222);
   // #ifdef APP-PLUS
   console.log(111)
   plus.screen.lockOrientation('landscape-primary');
  console.log(111);
  plus.screen.lockOrientation("landscape-primary");
   // #endif
  })
});
  onHide(() => {
    console.log(123123)
  console.log(123123);
    // #ifdef APP-PLUS
        plus.screen.lockOrientation('portrait-primary');
  plus.screen.lockOrientation("portrait-primary");
        // #endif
  })
});
</script>
<script module="leaflet" lang="renderjs">
src/pages/user/index.vue
@@ -7,9 +7,7 @@
        <u-avatar src="/static/images/logo.png" size="70" />
      </view>
      <view class="flex-1">
        <view class="pb-20rpx text-36rpx">
          uni-app
        </view>
        <view class="pb-20rpx text-36rpx"> uni-app </view>
        <view class="u-tips-color text-28rpx" @click="toCopy">
          微信号:uni-app
        </view>
@@ -46,21 +44,20 @@
</template>
<script setup>
import { useClipboard, usePermission } from "@/hooks"
import { useClipboard, usePermission } from "@/hooks";
const { setClipboardData, getClipboardData } = useClipboard()
const { setClipboardData, getClipboardData } = useClipboard();
// 复制
const toCopy = async () => {
  await setClipboardData({ data: "1234567890" })
  const data = await getClipboardData()
  console.log("[ data ] >", data)
}
  await setClipboardData({ data: "1234567890" });
  const data = await getClipboardData();
  console.log("[ data ] >", data);
};
// 登录鉴权,微信小程序端点击tabbar的底层逻辑不触发uni.switchTab,需要在页面onShow生命周期中校验权限
onShow(async () => {
  const hasPermission = await usePermission()
  console.log(hasPermission ? "已登录" : "未登录,拦截跳转")
})
  const hasPermission = await usePermission();
  console.log(hasPermission ? "已登录" : "未登录,拦截跳转");
});
</script>
src/pages/work/index.vue
@@ -1,10 +1,15 @@
<!-- 事件工单 -->
<template>
  <view class="min-h-screen flex flex-col items-center">
    <image class="mb-50rpx mt-200rpx h-200rpx w-200rpx" src="@/static/images/logo.png" width="200rpx" height="200rpx" />
    <image
      class="mb-50rpx mt-200rpx h-200rpx w-200rpx"
      src="@/static/images/logo.png"
      width="200rpx"
      height="200rpx"
    />
    <view class="flex justify-center">
      <text class="font-size-36rpx">
        {{ $t('home.intro') }}
        {{ $t("home.intro") }}
      </text>
    </view>
    <view class="mt-100rpx flex gap-30rpx">
@@ -16,14 +21,18 @@
    <!-- #ifdef MP-WEIXIN -->
    <!-- 隐私协议组件 -->
    <agree-privacy v-model="showAgreePrivacy" :disable-check-privacy="false" @agree="handleAgree" />
    <agree-privacy
      v-model="showAgreePrivacy"
      :disable-check-privacy="false"
      @agree="handleAgree"
    />
    <!-- #endif -->
  </view>
</template>
<script setup>
// #ifdef MP-WEIXIN
import { useShare } from "@/hooks"
import { useShare } from "@/hooks";
// #endif
// #ifdef MP-WEIXIN
@@ -31,29 +40,28 @@
const { onShareAppMessage, onShareTimeline } = useShare({
  title: "首页",
  path: "pages/tab/home/index",
  imageUrl: ""
})
onShareAppMessage()
onShareTimeline()
  imageUrl: "",
});
onShareAppMessage();
onShareTimeline();
// #endif
const title = ref()
title.value = import.meta.env.VITE_APP_TITLE
const title = ref();
title.value = import.meta.env.VITE_APP_TITLE;
const showAgreePrivacy = ref(false)
const showAgreePrivacy = ref(false);
// 同意隐私协议
function handleAgree() {
  console.log("同意隐私政策")
  console.log("同意隐私政策");
}
// 打开github
function toGithub() {
  if (window?.open) {
    window.open("https://github.com/oyjt/uniapp-vue3-template")
    window.open("https://github.com/oyjt/uniapp-vue3-template");
  } else {
    uni.$u.toast("请使用浏览器打开")
    uni.$u.toast("请使用浏览器打开");
  }
}
</script>
src/plugins/index.js
src/plugins/permission.js
src/plugins/ui.js
src/router/index.js
src/store/index.js
src/store/modules/app/index.js
@@ -1,8 +1,8 @@
import { defineStore } from 'pinia';
import storage from '@/utils/storage';
import { defineStore } from 'pinia'
import storage from '@/utils/storage'
// 缓存的主题
const THEME_KEY = 'app-theme';
const THEME_KEY = 'app-theme'
const useAppStore = defineStore('app', {
  state: () => ({
@@ -11,40 +11,40 @@
  }),
  getters: {
    getSystemInfo(state) {
      return state.systemInfo;
      return state.systemInfo
    },
    getTheme(state) {
      return state.theme;
      return state.theme
    }
  },
  actions: {
    setSystemInfo(info) {
      this.systemInfo = info;
      this.systemInfo = info
    },
    initSystemInfo() {
      uni.getSystemInfo({
        success: (res) => {
          this.setSystemInfo(res);
          this.setSystemInfo(res)
        },
        fail: (err) => {
          console.error(err);
          console.error(err)
        }
      });
      })
    },
    /**
     * 设置主题
     */
    setTheme(theme) {
      this.theme = theme;
      this.theme = theme
      // 保存到本地存储
      storage.set(THEME_KEY, this.theme);
      storage.set(THEME_KEY, this.theme)
    },
    checkUpdate() {
      const updateManager = uni.getUpdateManager();
      const updateManager = uni.getUpdateManager()
      updateManager.onCheckForUpdate((res) => {
        // 请求完新版本信息的回调
        console.log(res.hasUpdate);
      });
        console.log(res.hasUpdate)
      })
      updateManager.onUpdateReady(() => {
        uni.showModal({
          title: '更新提示',
@@ -52,21 +52,21 @@
          success(res) {
            if (res.confirm) {
              // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
              updateManager.applyUpdate();
              updateManager.applyUpdate()
            }
          }
        });
      });
        })
      })
      updateManager.onUpdateFailed((res) => {
        console.error(res);
        console.error(res)
        // 新的版本下载失败
        uni.showToast({
          title: '更新失败',
          icon: 'error'
        });
      });
        })
      })
    }
  }
});
})
export default useAppStore;
export default useAppStore
src/store/modules/user/index.js
@@ -8,9 +8,9 @@
 * Copyright 2025 OBKoro1, All Rights Reserved. 
 * 2025-09-28 09:31:16
 */
import { defineStore } from 'pinia';
import { UserApi } from '@/api';
import { clearToken, setToken } from '@/utils/auth';
import { defineStore } from 'pinia'
import { UserApi } from '@/api'
import { clearToken, setToken } from '@/utils/auth'
const useUserStore = defineStore('user', {
  state: () => ({
@@ -21,44 +21,44 @@
  }),
  getters: {
    userInfo(state) {
      return { ...state };
      return { ...state }
    }
  },
  actions: {
    // 设置用户的信息
    setInfo(partial) {
      this.$patch(partial);
      this.$patch(partial)
    },
    // 重置用户信息
    resetInfo() {
      this.$reset();
      this.$reset()
    },
    // 获取用户信息
    async info() {
      const result = await UserApi.profile();
      this.setInfo(result);
      const result = await UserApi.profile()
      this.setInfo(result)
    },
    // 异步登录并存储token
    login(loginForm) {
      return new Promise((resolve, reject) => {
        UserApi.login(loginForm)
          .then((res) => {
            const token = res.token;
            const token = res.token
            if (token) {
              setToken(token);
              setToken(token)
            }
            resolve(res);
            resolve(res)
          })
          .catch((error) => {
            reject(error);
          });
      });
            reject(error)
          })
      })
    },
    // Logout
    async logout() {
      await UserApi.logout();
      this.resetInfo();
      clearToken();
      await UserApi.logout()
      this.resetInfo()
      clearToken()
    },
    // 小程序授权登录
    authLogin(provider = 'weixin') {
@@ -67,21 +67,21 @@
          provider,
          success: async (result) => {
            if (result.code) {
              const res = await UserApi.loginByCode({ code: result.code });
              resolve(res);
              const res = await UserApi.loginByCode({ code: result.code })
              resolve(res)
            } else {
              reject(new Error(result.errMsg));
              reject(new Error(result.errMsg))
            }
          },
          fail: (err) => {
            console.error(`login error: ${err}`);
            reject(err);
            console.error(`login error: ${err}`)
            reject(err)
          }
        });
      });
        })
      })
    }
  },
  persist: true
});
})
export default useUserStore;
export default useUserStore
src/subPackages/404/index.vue
@@ -23,15 +23,14 @@
</template>
<script setup>
import { HOME_PATH } from "@/router"
import { HOME_PATH } from "@/router";
function handleBack() {
  uni.$u.route({
    type: "switchTab",
    url: HOME_PATH
  })
    url: HOME_PATH,
  });
}
</script>
<style lang="scss" scoped>
src/subPackages/theme/index.vue
@@ -7,9 +7,11 @@
      </view>
      <view class="theme-bg-secondary overflow-hidden rounded-12rpx">
        <view
          v-for="option in themeOptions" :key="option.value"
          v-for="option in themeOptions"
          :key="option.value"
          class="flex items-center border-b px-5 py-7.5 transition-all duration-300"
          :class="{ 'theme-bg-secondary': theme === option.value }" @click="setTheme(option.value)"
          :class="{ 'theme-bg-secondary': theme === option.value }"
          @click="setTheme(option.value)"
        >
          <view class="mr-5">
            <view class="text-48rpx c-primary" :class="option.icon" />
@@ -36,9 +38,7 @@
      </view>
      <view class="theme-bg-secondary rounded-12rpx p-7.5">
        <view class="mb-7.5">
          <text class="theme-text text-28rpx font-medium">
            示例内容
          </text>
          <text class="theme-text text-28rpx font-medium"> 示例内容 </text>
        </view>
        <view class="mb-7.5 flex flex-wrap gap-5">
          <view class="min-w-120rpx flex-1">
@@ -55,34 +55,32 @@
          </view>
        </view>
        <view class="flex flex-col gap-2.5">
          <text class="theme-text text-28rpx font-medium">
            主要文字颜色
          </text>
          <text class="theme-text-content text-26rpx">
            内容文字颜色
          </text>
          <text class="theme-text-tips text-24rpx">
            提示文字颜色
          </text>
          <text class="theme-text text-28rpx font-medium"> 主要文字颜色 </text>
          <text class="theme-text-content text-26rpx"> 内容文字颜色 </text>
          <text class="theme-text-tips text-24rpx"> 提示文字颜色 </text>
        </view>
      </view>
    </view>
    <!-- 快速切换按钮 -->
    <view class="py-5 text-center">
      <u-button :text="isDark ? '切换到亮色主题' : '切换到暗色主题'" type="primary" @click="toggleTheme" />
      <u-button
        :text="isDark ? '切换到亮色主题' : '切换到暗色主题'"
        type="primary"
        @click="toggleTheme"
      />
    </view>
  </view>
</template>
<script setup>
import { useTheme } from "@/hooks"
import { useAppStore } from "@/store"
import { useTheme } from "@/hooks";
import { useAppStore } from "@/store";
const { theme, isDark, setTheme, toggleTheme } = useTheme()
const { theme, isDark, setTheme, toggleTheme } = useTheme();
const appStore = useAppStore()
const themeClass = computed(() => `theme-${appStore.theme}`)
const appStore = useAppStore();
const themeClass = computed(() => `theme-${appStore.theme}`);
// 主题选项
const themeOptions = [
@@ -90,14 +88,13 @@
    label: "亮色主题",
    value: "light",
    icon: "i-mdi-white-balance-sunny",
    description: "适合明亮环境使用"
    description: "适合明亮环境使用",
  },
  {
    label: "暗色主题",
    value: "dark",
    icon: "i-mdi-moon-and-stars",
    description: "适合暗光环境使用"
  }
]
    description: "适合暗光环境使用",
  },
];
</script>
src/utils/auth/index.js
src/utils/common/index.js
src/utils/modals/index.js
src/utils/request/index.js
src/utils/request/interceptors.js
src/utils/storage/index.js