shuishen
2025-09-29 b9a26af5b08d3d92a6c38fd90e023bc4706f19eb
feat:一些基础文件格式化处理
44 files modified
842 ■■■■ 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 22 ●●●● 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 2 ●●● patch | view | raw | blame | history
src/hooks/use-loading/index.js 2 ●●● patch | view | raw | blame | history
src/hooks/use-location/index.js 8 ●●●● patch | view | raw | blame | history
src/hooks/use-modal/index.js 2 ●●● patch | view | raw | blame | history
src/hooks/use-permission/index.js 2 ●●● patch | view | raw | blame | history
src/hooks/use-share/index.js 2 ●●● patch | view | raw | blame | history
src/hooks/use-theme/index.js 2 ●●● patch | view | raw | blame | history
src/locales/index.js 2 ●●● patch | view | raw | blame | history
src/main.js 2 ●●● patch | view | raw | blame | history
src/pages/inspectionTask/index.vue 23 ●●●● patch | view | raw | blame | history
src/pages/login/index.vue 91 ●●●● patch | view | raw | blame | history
src/pages/map/index.vue 132 ●●●● 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 2 ●●● patch | view | raw | blame | history
src/plugins/permission.js 6 ●●●● patch | view | raw | blame | history
src/plugins/ui.js 2 ●●● patch | view | raw | blame | history
src/router/index.js 14 ●●●● patch | view | raw | blame | history
src/store/index.js 2 ●●● patch | view | raw | blame | history
src/store/modules/app/index.js 58 ●●●● patch | view | raw | blame | history
src/store/modules/user/index.js 66 ●●●● 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 8 ●●●● patch | view | raw | blame | history
src/utils/common/index.js 4 ●●●● patch | view | raw | blame | history
src/utils/modals/index.js 8 ●●●● patch | view | raw | blame | history
src/utils/request/index.js 12 ●●●● patch | view | raw | blame | history
src/utils/request/interceptors.js 4 ●●●● patch | view | raw | blame | history
src/utils/storage/index.js 10 ●●●● 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,13 +1,13 @@
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) {
export default function createVitePlugins (isBuild) {
  const vitePlugins = [
    // UnoCSS配置
    ConfigUnoCSSPlugin(),
@@ -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
@@ -7,7 +7,7 @@
 * // 获取剪切板
 * const data = await getClipboardData()
 */
export default function useClipboard() {
export default function useClipboard () {
  const setClipboardData = ({ data, showToast = true }) => {
    return new Promise((resolve, reject) => {
      uni.setClipboardData({
src/hooks/use-loading/index.js
@@ -7,7 +7,7 @@
 * // 隐藏loading
 * hideLoading()
 */
export default function useLoading() {
export default function useLoading () {
  const showLoading = (content = "加载中") => {
    uni.showLoading({
      title: content,
src/hooks/use-location/index.js
@@ -5,7 +5,7 @@
 * - 地址解析
 * - 距离计算
 */
export default function useLocation() {
export default function useLocation () {
  // 当前位置信息
  const location = ref(null)
@@ -191,9 +191,9 @@
    const a =
      Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos((lat1 * Math.PI) / 180) *
        Math.cos((lat2 * Math.PI) / 180) *
        Math.sin(dLon / 2) *
        Math.sin(dLon / 2)
      Math.cos((lat2 * Math.PI) / 180) *
      Math.sin(dLon / 2) *
      Math.sin(dLon / 2)
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
    const distance = R * c
src/hooks/use-modal/index.js
@@ -4,7 +4,7 @@
 * const {showModal} = useModal()
 * showModal('提示内容')
 */
export default function useModal() {
export default function useModal () {
  const showModal = (content, options) => {
    return new Promise((resolve, reject) => {
      uni.showModal({
src/hooks/use-permission/index.js
@@ -5,6 +5,6 @@
// 1.微信小程序端点击tabbar的底层逻辑不触发uni.switchTab
// 2.h5在浏览器地址栏输入url后跳转不触发uni的路由api
// 3.首次启动加载的页面不触发uni的路由api
export default async function usePermission() {
export default async function usePermission () {
  return hasPerm(currentRoute())
}
src/hooks/use-share/index.js
@@ -10,7 +10,7 @@
 * onShareAppMessage()
 * onShareTimeline()
 */
export default function useShare(options) {
export default function useShare (options) {
  // #ifdef MP-WEIXIN
  const title = options?.title ?? ""
  const path = options?.path ?? ""
src/hooks/use-theme/index.js
@@ -14,7 +14,7 @@
/**
 * 主题Hook
 */
export default function useTheme() {
export default function useTheme () {
  const appStore = useAppStore()
  // 当前主题模式
src/locales/index.js
@@ -12,7 +12,7 @@
  }
})
function setupI18n(app) {
function setupI18n (app) {
  app.use(i18n)
}
src/main.js
@@ -15,7 +15,7 @@
import "virtual:uno.css"
import 'leaflet/dist/leaflet.css'
export function createApp() {
export function createApp () {
  const app = createSSRApp(App)
  app.use(setupPlugins)
src/pages/inspectionTask/index.vue
@@ -1,4 +1,4 @@
 <!-- 巡检任务 -->
<!-- 巡检任务 -->
<template>
  <z-paging ref="pagingRef" v-model="dataList" @query="queryList">
    <view v-for="(item, index) in dataList" :key="index">
@@ -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,84 +1,84 @@
<!-- 地图 -->
<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)
   // #ifdef APP-PLUS
onShow(() => {
  console.log(222);
  // #ifdef APP-PLUS
   console.log(111)
   plus.screen.lockOrientation('landscape-primary');
   // #endif
  })
  console.log(111);
  plus.screen.lockOrientation("landscape-primary");
  // #endif
});
  onHide(() => {
    console.log(123123)
    // #ifdef APP-PLUS
        plus.screen.lockOrientation('portrait-primary');
        // #endif
  })
onHide(() => {
  console.log(123123);
  // #ifdef APP-PLUS
  plus.screen.lockOrientation("portrait-primary");
  // #endif
});
</script>
<script module="leaflet" lang="renderjs">
  import L from 'leaflet'
  import {
    onBeforeUnmount,
    onMounted
  } from 'vue';
import L from 'leaflet'
import {
  onBeforeUnmount,
  onMounted
} from 'vue';
  export default {
    mounted() {
      var basemapLayer0 = L.tileLayer(
        'http://t1.tianditu.com/vec_c/wmts?layer=vec&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
          maxZoom: 18,
          minZoom: 1,
          tileSize: 256,
          zoomOffset: 1
        });
      var basemapLayer1 = L.tileLayer(
        'http://t1.tianditu.com/cva_c/wmts?layer=cva&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
          maxZoom: 18,
          minZoom: 1,
          tileSize: 256,
          zoomOffset: 1
        });
      var basemapLayer2 = L.tileLayer(
        'http://t1.tianditu.com/img_c/wmts?layer=img&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
          maxZoom: 18,
          minZoom: 1,
          tileSize: 256,
          zoomOffset: 1
        });
      var basemapLayer3 = L.tileLayer(
        'http://t1.tianditu.com/cia_c/wmts?layer=cia&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
          maxZoom: 18,
          minZoom: 1,
          tileSize: 256,
          zoomOffset: 1
        });
      var basemap0 = L.layerGroup([basemapLayer0, basemapLayer1]);
      var basemap1 = L.layerGroup([basemapLayer2, basemapLayer3]);
export default {
  mounted() {
    var basemapLayer0 = L.tileLayer(
      'http://t1.tianditu.com/vec_c/wmts?layer=vec&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
        maxZoom: 18,
        minZoom: 1,
        tileSize: 256,
        zoomOffset: 1
      });
    var basemapLayer1 = L.tileLayer(
      'http://t1.tianditu.com/cva_c/wmts?layer=cva&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
        maxZoom: 18,
        minZoom: 1,
        tileSize: 256,
        zoomOffset: 1
      });
    var basemapLayer2 = L.tileLayer(
      'http://t1.tianditu.com/img_c/wmts?layer=img&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
        maxZoom: 18,
        minZoom: 1,
        tileSize: 256,
        zoomOffset: 1
      });
    var basemapLayer3 = L.tileLayer(
      'http://t1.tianditu.com/cia_c/wmts?layer=cia&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', {
        maxZoom: 18,
        minZoom: 1,
        tileSize: 256,
        zoomOffset: 1
      });
    var basemap0 = L.layerGroup([basemapLayer0, basemapLayer1]);
    var basemap1 = L.layerGroup([basemapLayer2, basemapLayer3]);
      let map = L.map('map', {
        preferCanvas: true,
        crs: L.CRS.EPSG4326,
        layers: [basemap1],
        zoomControl: false,
        attributionControl: false,
        doubleClickZoom: false,
        editable: true //绘制控件
      }).setView([25.992338, 114.823254], 13);
    }
    let map = L.map('map', {
      preferCanvas: true,
      crs: L.CRS.EPSG4326,
      layers: [basemap1],
      zoomControl: false,
      attributionControl: false,
      doubleClickZoom: false,
      editable: true //绘制控件
    }).setView([25.992338, 114.823254], 13);
  }
}
</script>
<style scoped>
  /* Leaflet 默认容器要有高度 */
  .map {
    width: 100%;
    height: 100%;
  }
/* Leaflet 默认容器要有高度 */
.map {
  width: 100%;
  height: 100%;
}
</style>
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
@@ -5,7 +5,7 @@
import setupUI from "./ui"
export default {
  install(app) {
  install (app) {
    // UI扩展配置
    setupUI(app)
    // 状态管理
src/plugins/permission.js
@@ -20,7 +20,7 @@
 * @param {string} path
 * @returns {boolean} 是否有权限
 */
export function hasPerm(path = "") {
export function hasPerm (path = "") {
  if (!isPathExists(path) && path !== "/") {
    uni.redirectTo({
      url: ERROR404_PATH
@@ -38,14 +38,14 @@
  return hasPermission
}
function setupPermission() {
function setupPermission () {
  // 注意:拦截uni.switchTab本身没有问题。但是在微信小程序端点击tabbar的底层逻辑并不是触发uni.switchTab。
  // 所以误认为拦截无效,此类场景的解决方案是在tabbar页面的页面生命周期onShow中处理。
  ;["navigateTo", "redirectTo", "reLaunch", "switchTab"].forEach(item => {
    // https://uniapp.dcloud.net.cn/api/interceptor.html
    uni.addInterceptor(item, {
      // 页面跳转前进行拦截, invoke根据返回值进行判断是否继续执行跳转
      invoke(args) {
      invoke (args) {
        // args为所拦截api中的参数,比如拦截的是uni.redirectTo(OBJECT),则args对应的是OBJECT参数
        return hasPerm(args.url)
      }
src/plugins/ui.js
@@ -1,6 +1,6 @@
import uviewPlus, { setConfig } from "uview-plus"
function setupUI(app) {
function setupUI (app) {
  // 下面的在特殊场景下才需要配置,通常不用配置即可直接使用uview-plus框架。
  // 调用setConfig方法,方法内部会进行对象属性深度合并,可以放心嵌套配置
  // 需要在app.use(uview-plus)之后执行
src/router/index.js
@@ -20,7 +20,7 @@
 * @param {object} pagesJson
 * @returns [{"path": "/pages/tab/home/index","needLogin": false},...]
 */
function parseRoutes(pagesJson = {}) {
function parseRoutes (pagesJson = {}) {
  if (!pagesJson.pages) {
    pagesJson.pages = []
  }
@@ -28,7 +28,7 @@
    pagesJson.subPackages = []
  }
  function parsePages(pages = [], rootPath = "") {
  function parsePages (pages = [], rootPath = "") {
    const routes = []
    for (let i = 0; i < pages.length; i++) {
      routes.push({
@@ -39,7 +39,7 @@
    return routes
  }
  function parseSubPackages(subPackages = []) {
  function parseSubPackages (subPackages = []) {
    const routes = []
    for (let i = 0; i < subPackages.length; i++) {
      routes.push(...parsePages(subPackages[i].pages, subPackages[i].root))
@@ -58,7 +58,7 @@
 * 当前路由
 * @returns {string} 当前路由
 */
export function currentRoute() {
export function currentRoute () {
  // getCurrentPages() 至少有1个元素,所以不再额外判断
  const pages = getCurrentPages()
  const currentPage = pages[pages.length - 1]
@@ -70,7 +70,7 @@
 * @param {string} path
 * @returns {string} 去除查询字符串后的路径
 */
export function removeQueryString(path = "") {
export function removeQueryString (path = "") {
  return path.split("?")[0]
}
@@ -79,7 +79,7 @@
 * @param {string} path
 * @returns {boolean} 路径是否存在
 */
export function isPathExists(path = "") {
export function isPathExists (path = "") {
  const cleanPath = removeQueryString(path)
  return routes.some(item => item.path === cleanPath)
}
@@ -89,7 +89,7 @@
 * @param {string} path
 * @returns {boolean} 是否是tabbar页面
 */
export function isTabBarPath(path = "") {
export function isTabBarPath (path = "") {
  const cleanPath = removeQueryString(path)
  return (
    pagesJson.tabBar?.list?.some(item => `/${item.pagePath}` === cleanPath) ===
src/store/index.js
@@ -7,7 +7,7 @@
import useUserStore from "./modules/user"
// 安装pinia状态管理插件
function setupStore(app) {
function setupStore (app) {
  const store = createPinia()
  const piniaPersist = createPersistedState({
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: () => ({
@@ -10,63 +10,63 @@
    theme: storage.get(THEME_KEY) || 'light'
  }),
  getters: {
    getSystemInfo(state) {
      return state.systemInfo;
    getSystemInfo (state) {
      return state.systemInfo
    },
    getTheme(state) {
      return state.theme;
    getTheme (state) {
      return state.theme
    }
  },
  actions: {
    setSystemInfo(info) {
      this.systemInfo = info;
    setSystemInfo (info) {
      this.systemInfo = info
    },
    initSystemInfo() {
    initSystemInfo () {
      uni.getSystemInfo({
        success: (res) => {
          this.setSystemInfo(res);
          this.setSystemInfo(res)
        },
        fail: (err) => {
          console.error(err);
          console.error(err)
        }
      });
      })
    },
    /**
     * 设置主题
     */
    setTheme(theme) {
      this.theme = theme;
    setTheme (theme) {
      this.theme = theme
      // 保存到本地存储
      storage.set(THEME_KEY, this.theme);
      storage.set(THEME_KEY, this.theme)
    },
    checkUpdate() {
      const updateManager = uni.getUpdateManager();
    checkUpdate () {
      const updateManager = uni.getUpdateManager()
      updateManager.onCheckForUpdate((res) => {
        // 请求完新版本信息的回调
        console.log(res.hasUpdate);
      });
        console.log(res.hasUpdate)
      })
      updateManager.onUpdateReady(() => {
        uni.showModal({
          title: '更新提示',
          content: '新版本已经准备好,是否重启应用?',
          success(res) {
          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: () => ({
@@ -20,68 +20,68 @@
    token: ''
  }),
  getters: {
    userInfo(state) {
      return { ...state };
    userInfo (state) {
      return { ...state }
    }
  },
  actions: {
    // 设置用户的信息
    setInfo(partial) {
      this.$patch(partial);
    setInfo (partial) {
      this.$patch(partial)
    },
    // 重置用户信息
    resetInfo() {
      this.$reset();
    resetInfo () {
      this.$reset()
    },
    // 获取用户信息
    async info() {
      const result = await UserApi.profile();
      this.setInfo(result);
    async info () {
      const result = await UserApi.profile()
      this.setInfo(result)
    },
    // 异步登录并存储token
    login(loginForm) {
    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();
    async logout () {
      await UserApi.logout()
      this.resetInfo()
      clearToken()
    },
    // 小程序授权登录
    authLogin(provider = 'weixin') {
    authLogin (provider = 'weixin') {
      return new Promise((resolve, reject) => {
        uni.login({
          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
@@ -1,15 +1,15 @@
const TokenKey = "admin-token"
const TokenPrefix = "Bearer "
function isLogin() {
function isLogin () {
  return !!uni.getStorageSync(TokenKey)
}
function getToken() {
function getToken () {
  return uni.getStorageSync(TokenKey)
}
function setToken(token) {
function setToken (token) {
  uni.setStorageSync(TokenKey, token)
}
function clearToken() {
function clearToken () {
  uni.removeStorageSync(TokenKey)
}
export { clearToken, getToken, isLogin, setToken, TokenPrefix }
src/utils/common/index.js
@@ -1,5 +1,5 @@
// 小程序更新检测
export function mpUpdate() {
export function mpUpdate () {
  const updateManager = uni.getUpdateManager()
  updateManager.onCheckForUpdate(res => {
    // 请求完新版本信息的回调
@@ -9,7 +9,7 @@
    uni.showModal({
      title: "更新提示",
      content: "检测到新版本,是否下载新版本并重启小程序?",
      success(res) {
      success (res) {
        if (res.confirm) {
          // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
          updateManager.applyUpdate()
src/utils/modals/index.js
@@ -3,7 +3,7 @@
 * @param {string} content 提示内容
 * @param {object} option 配置
 */
export function Toast(content, option = {}) {
export function Toast (content, option = {}) {
  uni.showToast({
    title: content,
    icon: "none",
@@ -34,7 +34,7 @@
 * @param {string} content 提示内容
 * @param {object} option 配置
 */
export function Dialog(content, option = {}) {
export function Dialog (content, option = {}) {
  option.showCancel = false
  return new Promise((resolve, reject) => {
    uni.showModal({
@@ -42,10 +42,10 @@
      content,
      showCancel: false,
      confirmColor: "#1677FF",
      success(res) {
      success (res) {
        if (res.confirm) resolve(res)
      },
      fail() {
      fail () {
        reject(new Error("Alert 调用失败 !"))
      },
      ...option
src/utils/request/index.js
@@ -4,7 +4,7 @@
const http = new Request()
// 引入拦截器配置
export function setupRequest() {
export function setupRequest () {
  http.setConfig(defaultConfig => {
    /* defaultConfig 为默认全局配置 */
    defaultConfig.baseURL = import.meta.env.VITE_API_BASE_URL
@@ -19,7 +19,7 @@
  responseInterceptors(http)
}
export function request(config) {
export function request (config) {
  return new Promise((resolve, reject) => {
    http
      .request(config)
@@ -35,19 +35,19 @@
  })
}
export function get(url, config) {
export function get (url, config) {
  return request({ ...config, url, method: "GET" })
}
export function post(url, config) {
export function post (url, config) {
  return request({ ...config, url, method: "POST" })
}
export function upload(url, config) {
export function upload (url, config) {
  return request({ ...config, url, method: "UPLOAD" })
}
export function download(url, config) {
export function download (url, config) {
  return request({ ...config, url, method: "DOWNLOAD" })
}
src/utils/request/interceptors.js
@@ -66,7 +66,7 @@
  })
}
function requestInterceptors(http) {
function requestInterceptors (http) {
  /**
   * 请求拦截
   * @param {object} http
@@ -109,7 +109,7 @@
    ) => Promise.reject(config)
  )
}
function responseInterceptors(http) {
function responseInterceptors (http) {
  /**
   * 响应拦截
   * @param {object} http
src/utils/storage/index.js
@@ -1,20 +1,20 @@
const storage = {
  set(key, value) {
  set (key, value) {
    if (key !== null && value !== null) uni.setStorageSync(key, value)
  },
  get(key) {
  get (key) {
    if (key === null) return null
    return uni.getStorageSync(key)
  },
  setJSON(key, jsonValue) {
  setJSON (key, jsonValue) {
    if (jsonValue !== null) this.set(key, JSON.stringify(jsonValue))
  },
  getJSON(key) {
  getJSON (key) {
    const value = this.get(key)
    if (value) return JSON.parse(value)
  },
  remove(key) {
  remove (key) {
    uni.removeStorageSync(key)
  }
}