车间能级提升-智能设备管理系统
baoshiwei
2025-04-17 bb79260cbeeac88cfbadc9606eea57002e8945bc
Merge remote-tracking branch 'origin/main'
已添加116个文件
已修改27个文件
19013 ■■■■■ 文件已修改
eims-ui-mobile/.commitlintrc.cjs 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.editorconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.eslintignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.eslintrc-auto-import.json 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.eslintrc.cjs 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.npmrc 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.prettierignore 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.prettierrc.cjs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.stylelintignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.stylelintrc.cjs 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/README.md 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env.development 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env.production 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env.test 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/index.html 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/manifest.config.ts 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/openapi-ts-request.config.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/package.json 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/pages.config.ts 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/pnpm-lock.yaml 13257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/scripts/postupgrade.js 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/App.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/components/.gitkeep 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/components/fg-tabbar/fg-tabbar.vue 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/components/fg-tabbar/tabbar.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/env.d.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/hooks/.gitkeep 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/hooks/useRequest.ts 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/hooks/useUpload.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/interceptors/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/interceptors/prototype.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/interceptors/request.ts 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/interceptors/route.ts 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/layouts/default.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/layouts/demo.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/layouts/tabbar.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/main.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/manifest.json 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages-sub/demo/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages.json 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/equ/components/request.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/equ/components/upload.vue 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/equ/index.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/home/index.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/login/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/my/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/spare/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/displayEnumLabel.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/index.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/pet.ts 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/pet.vuequery.ts 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/store.ts 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/store.vuequery.ts 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/types.ts 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/user.ts 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/app/user.vuequery.ts 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/index/foo.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/1024x1024.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/120x120.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/144x144.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/152x152.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/167x167.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/180x180.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/192x192.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/20x20.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/29x29.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/40x40.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/58x58.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/60x60.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/72x72.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/76x76.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/80x80.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/87x87.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/app/icons/96x96.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/.gitkeep 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/logo.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/tabbar/scan.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/store/index.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/store/user.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/style/iconfont.css 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/style/index.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/auto-import.d.ts 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/global.d.ts 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/shims-uni.d.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/uni-pages.d.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/typings.d.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/typings.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/uni.scss 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/uni_modules/.gitkeep 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/http.ts 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/index.ts 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/platform.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/request.ts 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/tsconfig.json 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/uno.config.ts 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/vite-plugins/copyNativeRes.ts 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/vite.config.ts 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/.env.production 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/index.ts 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/components/spare-modal.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/data.tsx 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/select-spare-table.vue 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare/data.tsx 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare/index.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/resources/application-prod.yml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutdtController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsSpareInoutdt.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareBo.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutBo.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutdtBo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutVo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutdtVo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareVo.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutdtMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareMapper.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutdtService.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutServiceImpl.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutdtServiceImpl.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareInoutdtMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.commitlintrc.cjs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const scopes = fs
  .readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
  .filter((dirent) => dirent.isDirectory())
  .map((dirent) => dirent.name.replace(/s$/, ''))
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
  .toString()
  .trim()
  .split('\n')
  .find((r) => ~r.indexOf('M  src'))
  ?.replace(/(\/)/g, '%%')
  ?.match(/src%%((\w|-)*)/)?.[1]
  ?.replace(/s$/, '')
module.exports = {
  ignores: [(commit) => commit.includes('init')],
  extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [1, 'always'],
    'header-max-length': [2, 'always', 108],
    'subject-empty': [2, 'never'],
    'type-empty': [2, 'never'],
    'subject-case': [0],
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'perf',
        'style',
        'docs',
        'test',
        'refactor',
        'build',
        'ci',
        'chore',
        'revert',
        'wip',
        'workflow',
        'types',
        'release',
      ],
    ],
  },
  prompt: {
    /** @use `pnpm commit :f` */
    alias: {
      f: 'docs: fix typos',
      r: 'docs: update README',
      s: 'style: update code format',
      b: 'build: bump dependencies',
      c: 'chore: update config',
    },
    customScopesAlign: !scopeComplete ? 'top' : 'bottom',
    defaultScope: scopeComplete,
    scopes: [...scopes, 'mock'],
    allowEmptyIssuePrefixs: false,
    allowCustomIssuePrefixs: false,
    // English
    typesAppend: [
      { value: 'wip', name: 'wip:      work in process' },
      { value: 'workflow', name: 'workflow: workflow improvements' },
      { value: 'types', name: 'types:    type definition file changes' },
    ],
    // ä¸­è‹±æ–‡å¯¹ç…§ç‰ˆ
    // messages: {
    //   type: '选择你要提交的类型 :',
    //   scope: '选择一个提交范围 (可选):',
    //   customScope: '请输入自定义的提交范围 :',
    //   subject: '填写简短精炼的变更描述 :\n',
    //   body: '填写更加详细的变更描述 (可选)。使用 "|" æ¢è¡Œ :\n',
    //   breaking: '列举非兼容性重大的变更 (可选)。使用 "|" æ¢è¡Œ :\n',
    //   footerPrefixsSelect: '选择关联issue前缀 (可选):',
    //   customFooterPrefixs: '输入自定义issue前缀 :',
    //   footer: '列举关联issue (可选) ä¾‹å¦‚: #31, #I3244 :\n',
    //   confirmCommit: '是否提交或修改commit ?',
    // },
    // types: [
    //   { value: 'feat', name: 'feat:     æ–°å¢žåŠŸèƒ½' },
    //   { value: 'fix', name: 'fix:      ä¿®å¤ç¼ºé™·' },
    //   { value: 'docs', name: 'docs:     æ–‡æ¡£å˜æ›´' },
    //   { value: 'style', name: 'style:    ä»£ç æ ¼å¼' },
    //   { value: 'refactor', name: 'refactor: ä»£ç é‡æž„' },
    //   { value: 'perf', name: 'perf:     æ€§èƒ½ä¼˜åŒ–' },
    //   { value: 'test', name: 'test:     æ·»åŠ ç–æ¼æµ‹è¯•æˆ–å·²æœ‰æµ‹è¯•æ”¹åŠ¨' },
    //   { value: 'build', name: 'build:    æž„建流程、外部依赖变更 (如升级 npm åŒ…、修改打包配置等)' },
    //   { value: 'ci', name: 'ci:       ä¿®æ”¹ CI é…ç½®ã€è„šæœ¬' },
    //   { value: 'revert', name: 'revert:   å›žæ»š commit' },
    //   { value: 'chore', name: 'chore:    å¯¹æž„建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
    //   { value: 'wip', name: 'wip:      æ­£åœ¨å¼€å‘中' },
    //   { value: 'workflow', name: 'workflow: å·¥ä½œæµç¨‹æ”¹è¿›' },
    //   { value: 'types', name: 'types:    ç±»åž‹å®šä¹‰æ–‡ä»¶ä¿®æ”¹' },
    // ],
    // emptyScopesAlias: 'empty:      ä¸å¡«å†™',
    // customScopesAlias: 'custom:     è‡ªå®šä¹‰',
  },
}
eims-ui-mobile/.editorconfig
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
root = true
[*] # è¡¨ç¤ºæ‰€æœ‰æ–‡ä»¶é€‚用
charset = utf-8 # è®¾ç½®æ–‡ä»¶å­—符集为 utf-8
indent_style = space # ç¼©è¿›é£Žæ ¼ï¼ˆtab | space)
indent_size = 2 # ç¼©è¿›å¤§å°
end_of_line = lf # æŽ§åˆ¶æ¢è¡Œç±»åž‹(lf | cr | crlf)
trim_trailing_whitespace = true # åŽ»é™¤è¡Œé¦–çš„ä»»æ„ç©ºç™½å­—ç¬¦
insert_final_newline = true # å§‹ç»ˆåœ¨æ–‡ä»¶æœ«å°¾æ’入一个新行
[*.md] # è¡¨ç¤ºä»… md æ–‡ä»¶é€‚用以下规则
max_line_length = off # å…³é—­æœ€å¤§è¡Œé•¿åº¦é™åˆ¶
trim_trailing_whitespace = false # å…³é—­æœ«å°¾ç©ºæ ¼ä¿®å‰ª
eims-ui-mobile/.eslintignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
src/uni_modules/
eims-ui-mobile/.eslintrc-auto-import.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
{
  "globals": {
    "Component": true,
    "ComponentPublicInstance": true,
    "ComputedRef": true,
    "EffectScope": true,
    "ExtractDefaultPropTypes": true,
    "ExtractPropTypes": true,
    "ExtractPublicPropTypes": true,
    "InjectionKey": true,
    "PropType": true,
    "Ref": true,
    "VNode": true,
    "WritableComputedRef": true,
    "computed": true,
    "createApp": true,
    "customRef": true,
    "defineAsyncComponent": true,
    "defineComponent": true,
    "effectScope": true,
    "getCurrentInstance": true,
    "getCurrentScope": true,
    "h": true,
    "inject": true,
    "isProxy": true,
    "isReactive": true,
    "isReadonly": true,
    "isRef": true,
    "markRaw": true,
    "nextTick": true,
    "onActivated": true,
    "onAddToFavorites": true,
    "onBackPress": true,
    "onBeforeMount": true,
    "onBeforeUnmount": true,
    "onBeforeUpdate": true,
    "onDeactivated": true,
    "onError": true,
    "onErrorCaptured": true,
    "onHide": true,
    "onLaunch": true,
    "onLoad": true,
    "onMounted": true,
    "onNavigationBarButtonTap": true,
    "onNavigationBarSearchInputChanged": true,
    "onNavigationBarSearchInputClicked": true,
    "onNavigationBarSearchInputConfirmed": true,
    "onNavigationBarSearchInputFocusChanged": true,
    "onPageNotFound": true,
    "onPageScroll": true,
    "onPullDownRefresh": true,
    "onReachBottom": true,
    "onReady": true,
    "onRenderTracked": true,
    "onRenderTriggered": true,
    "onResize": true,
    "onScopeDispose": true,
    "onServerPrefetch": true,
    "onShareAppMessage": true,
    "onShareTimeline": true,
    "onShow": true,
    "onTabItemTap": true,
    "onThemeChange": true,
    "onUnhandledRejection": true,
    "onUnload": true,
    "onUnmounted": true,
    "onUpdated": true,
    "provide": true,
    "reactive": true,
    "readonly": true,
    "ref": true,
    "resolveComponent": true,
    "shallowReactive": true,
    "shallowReadonly": true,
    "shallowRef": true,
    "toRaw": true,
    "toRef": true,
    "toRefs": true,
    "toValue": true,
    "triggerRef": true,
    "unref": true,
    "useAttrs": true,
    "useCssModule": true,
    "useCssVars": true,
    "useRequest": true,
    "useSlots": true,
    "useUpload": true,
    "useUpload2": true,
    "watch": true,
    "watchEffect": true,
    "watchPostEffect": true,
    "watchSyncEffect": true,
    "DirectiveBinding": true,
    "MaybeRef": true,
    "MaybeRefOrGetter": true,
    "onWatcherCleanup": true,
    "useId": true,
    "useModel": true,
    "useTemplateRef": true
  }
}
eims-ui-mobile/.eslintrc.cjs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:vue/vue3-essential',
    // eslint-plugin-import æ’件, @see https://www.npmjs.com/package/eslint-plugin-import
    'plugin:import/recommended',
    // eslint-config-airbnb-base æ’ä»¶ å·²ç»æ”¹ç”¨ eslint-config-standard æ’ä»¶
    'standard',
    // 1. æŽ¥å…¥ prettier çš„规则
    'prettier',
    'plugin:prettier/recommended',
    './.eslintrc-auto-import.json',
  ],
  overrides: [
    {
      env: {
        node: true,
      },
      files: ['.eslintrc.{js,cjs}'],
      parserOptions: {
        sourceType: 'script',
      },
    },
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
  },
  plugins: [
    '@typescript-eslint',
    'vue',
    // 2. åŠ å…¥ prettier çš„ eslint æ’ä»¶
    'prettier',
    // eslint-import-resolver-typescript æ’件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
    'import',
  ],
  rules: {
    // 3. æ³¨æ„è¦åŠ ä¸Šè¿™ä¸€å¥ï¼Œå¼€å¯ prettier è‡ªåŠ¨ä¿®å¤çš„åŠŸèƒ½
    'prettier/prettier': 'error',
    // turn on errors for missing imports
    'import/no-unresolved': 'off',
    // å¯¹åŽç¼€çš„æ£€æµ‹ï¼Œå¦åˆ™ import ä¸€ä¸ªts文件也会报错,需要手动添加'.ts', å¢žåŠ äº†ä¸‹é¢çš„é…ç½®åŽå°±ä¸ç”¨äº†
    'import/extensions': [
      'error',
      'ignorePackages',
      { js: 'never', jsx: 'never', ts: 'never', tsx: 'never' },
    ],
    // åªå…è®¸1个默认导出,关闭,否则不能随意export xxx
    'import/prefer-default-export': ['off'],
    'no-console': ['off'],
    // 'no-unused-vars': ['off'],
    // '@typescript-eslint/no-unused-vars': ['off'],
    // è§£å†³vite.config.ts报错问题
    'import/no-extraneous-dependencies': 'off',
    'no-plusplus': 'off',
    'no-shadow': 'off',
    'vue/multi-word-component-names': 'off',
    'vue/no-unused-vars': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    'no-underscore-dangle': 'off',
    'no-use-before-define': 'off',
    'no-undef': 'off',
    'no-unused-vars': 'off',
    'no-param-reassign': 'off',
    '@typescript-eslint/no-unused-vars': 'off',
    // é¿å… `eslint` å¯¹äºŽ `typescript` å‡½æ•°é‡è½½çš„误报
    'no-redeclare': 'off',
    '@typescript-eslint/no-redeclare': 'error',
  },
  // eslint-import-resolver-typescript æ’件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
  settings: {
    'import/parsers': {
      '@typescript-eslint/parser': ['.ts', '.tsx'],
    },
    'import/resolver': {
      typescript: {},
    },
  },
  globals: {
    $t: true,
    uni: true,
    UniApp: true,
    wx: true,
    WechatMiniprogram: true,
    getCurrentPages: true,
    UniHelper: true,
    Page: true,
    App: true,
    NodeJS: true,
  },
}
eims-ui-mobile/.npmrc
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
# registry = https://registry.npmjs.org
registry = https://registry.npmmirror.com
strict-peer-dependencies=false
auto-install-peers=true
shamefully-hoist=true
eims-ui-mobile/.prettierignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
# unplugin-auto-import ç”Ÿæˆçš„类型文件,每次提交都改变,所以加入这里吧,与 .gitignore é…åˆä½¿ç”¨
auto-import.d.ts
# vite-plugin-uni-pages ç”Ÿæˆçš„类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
uni-pages.d.ts
# æ’件生成的文件
src/pages.json
src/manifest.json
# å¿½ç•¥è‡ªåŠ¨ç”Ÿæˆæ–‡ä»¶
src/service/app/**
eims-ui-mobile/.prettierrc.cjs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
// @see https://prettier.io/docs/en/options
module.exports = {
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: false,
  trailingComma: 'all',
  endOfLine: 'auto',
  htmlWhitespaceSensitivity: 'ignore',
  overrides: [
    {
      files: '*.json',
      options: {
        trailingComma: 'none',
      },
    },
  ],
}
eims-ui-mobile/.stylelintignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
src/uni_modules/
eims-ui-mobile/.stylelintrc.cjs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
// .stylelintrc.cjs
module.exports = {
  root: true,
  extends: [
    // stylelint-config-standard æ›¿æ¢æˆäº†æ›´å®½æ¾çš„ stylelint-config-recommended
    'stylelint-config-recommended',
    // stylelint-config-standard-scss æ›¿æ¢æˆäº†æ›´å®½æ¾çš„ stylelint-config-recommended-scss
    'stylelint-config-recommended-scss',
    'stylelint-config-recommended-vue/scss',
    'stylelint-config-html/vue',
    'stylelint-config-recess-order',
  ],
  plugins: ['stylelint-prettier'],
  overrides: [
    // æ‰«æ .vue/html æ–‡ä»¶ä¸­çš„<style>标签内的样式
    {
      files: ['**/*.{vue,html}'],
      customSyntax: 'postcss-html',
    },
    {
      files: ['**/*.{css,scss}'],
      customSyntax: 'postcss-scss',
    },
  ],
  // è‡ªå®šä¹‰è§„则
  rules: {
    'prettier/prettier': true,
    // å…è®¸ global ã€export ã€v-deep等伪类
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],
      },
    ],
    'unit-no-unknown': [
      true,
      {
        ignoreUnits: ['rpx'],
      },
    ],
    // å¤„理小程序page标签不认识的问题
    'selector-type-no-unknown': [
      true,
      {
        ignoreTypes: ['page'],
      },
    ],
    'comment-empty-line-before': 'never', // never|always|always-multi-line|never-multi-line
    'custom-property-empty-line-before': 'never',
    'no-empty-source': null,
    'comment-no-empty': null,
    'no-duplicate-selectors': null,
    'scss/comment-no-empty': null,
    'selector-class-pattern': null,
    'font-family-no-missing-generic-family-keyword': null,
  },
}
eims-ui-mobile/LICENSE
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 è²é¸½
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
eims-ui-mobile/README.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
<p align="center">
  <a href="https://github.com/feige996/unibest">
    <img width="160" src="./src/static/logo.svg">
  </a>
</p>
<h1 align="center">
  <a href="https://github.com/feige996/unibest" target="_blank">unibest - æœ€å¥½çš„ uniapp å¼€å‘框架</a>
</h1>
<div align="center">
旧仓库 codercup è¿›ä¸åŽ»äº†ï¼Œstar ä¹Ÿæ‹¿ä¸å›žæ¥ï¼Œè¿™é‡Œä¹Ÿå±•示一下那个地址的 star.
[![GitHub Repo stars](https://img.shields.io/github/stars/codercup/unibest?style=flat&logo=github)](https://github.com/codercup/unibest)
[![GitHub forks](https://img.shields.io/github/forks/codercup/unibest?style=flat&logo=github)](https://github.com/codercup/unibest)
</div>
<div align="center">
[![GitHub Repo stars](https://img.shields.io/github/stars/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest)
[![GitHub forks](https://img.shields.io/github/forks/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest)
[![star](https://gitee.com/feige996/unibest/badge/star.svg?theme=dark)](https://gitee.com/feige996/unibest/stargazers)
[![fork](https://gitee.com/feige996/unibest/badge/fork.svg?theme=dark)](https://gitee.com/feige996/unibest/members)
![node version](https://img.shields.io/badge/node-%3E%3D18-green)
![pnpm version](https://img.shields.io/badge/pnpm-%3E%3D7.30-green)
![GitHub package.json version (subfolder of monorepo)](https://img.shields.io/github/package-json/v/feige996/unibest)
![GitHub License](https://img.shields.io/github/license/feige996/unibest)
</div>
`unibest` â€”— æœ€å¥½çš„ `uniapp` å¼€å‘模板,由 `uniapp` + `Vue3` + `Ts` + `Vite5` + `UnoCss` + `wot-ui` + `z-paging` æž„成,使用了最新的前端技术栈,无需依靠 `HBuilderX`,通过命令行方式运行 `web`、`小程序` å’Œ `App`(编辑器推荐 `VSCode`,可选 `webstorm`)。
`unibest` å†…置了 `约定式路由`、`layout布局`、`请求封装`、`请求拦截`、`登录拦截`、`UnoCSS`、`i18n多语言` ç­‰åŸºç¡€åŠŸèƒ½ï¼Œæä¾›äº† `代码提示`、`自动格式化`、`统一配置`、`代码片段` ç­‰è¾…助功能,让你编写 `uniapp` æ‹¥æœ‰ `best` ä½“验 ï¼ˆ `unibest çš„由来`)。
![](https://raw.githubusercontent.com/andreasbm/readme/master/screenshots/lines/rainbow.png)
<p align="center">
  <a href="https://unibest.tech/" target="_blank">📖 æ–‡æ¡£åœ°å€(new)</a>
  <span style="margin:0 10px;">|</span>
  <a href="https://feige996.github.io/hello-unibest/" target="_blank">📱 DEMO åœ°å€</a>
</p>
---
注意旧的地址 [codercup](https://github.com/codercup/unibest) æˆ‘进不去了,使用新的 [feige996](https://github.com/feige996/unibest)。PR和 issue ä¹Ÿè¯·ä½¿ç”¨æ–°åœ°å€ï¼Œå¦åˆ™æ— æ³•合并。
## å¹³å°å…¼å®¹æ€§
| H5  | IOS | å®‰å“ | å¾®ä¿¡å°ç¨‹åº | å­—节小程序 | å¿«æ‰‹å°ç¨‹åº | æ”¯ä»˜å®å°ç¨‹åº | é’‰é’‰å°ç¨‹åº | ç™¾åº¦å°ç¨‹åº |
| --- | --- | ---- | ---------- | ---------- | ---------- | ------------ | ---------- | ---------- |
| âˆš   | âˆš   | âˆš    | âˆš          | âˆš          | âˆš          | âˆš            | âˆš          | âˆš          |
注意每种 `UI框架` æ”¯æŒçš„平台有所不同,详情请看各 `UI框架` çš„官网,也可以看 `unibest` æ–‡æ¡£ã€‚
## âš™ï¸ çŽ¯å¢ƒ
- node>=18
- pnpm>=7.30
- Vue Official>=2.1.10
- TypeScript>=5.0
## &#x1F4C2; å¿«é€Ÿå¼€å§‹
执行 `pnpm create unibest` åˆ›å»ºé¡¹ç›®
执行 `pnpm i` å®‰è£…依赖
执行 `pnpm dev` è¿è¡Œ `H5`
## ðŸ“¦ è¿è¡Œï¼ˆæ”¯æŒçƒ­æ›´æ–°ï¼‰
- web平台: `pnpm dev:h5`, ç„¶åŽæ‰“å¼€ [http://localhost:9000/](http://localhost:9000/)。
- weixin平台:`pnpm dev:mp-weixin` ç„¶åŽæ‰“开微信开发者工具,导入本地文件夹,选择本项目的`dist/dev/mp-weixin` æ–‡ä»¶ã€‚
- APP平台:`pnpm dev:app`, ç„¶åŽæ‰“å¼€ `HBuilderX`,导入刚刚生成的`dist/dev/app` æ–‡ä»¶å¤¹ï¼Œé€‰æ‹©è¿è¡Œåˆ°æ¨¡æ‹Ÿå™¨(开发时优先使用),或者运行的安卓/ios基座。
## ðŸ”— å‘布
- web平台: `pnpm build:h5`,打包后的文件在 `dist/build/h5`,可以放到web服务器,如nginx运行。如果最终不是放在根目录,可以在 `manifest.config.ts` æ–‡ä»¶çš„ `h5.router.base` å±žæ€§è¿›è¡Œä¿®æ”¹ã€‚
- weixin平台:`pnpm build:mp-weixin`, æ‰“包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
- APP平台:`pnpm build:app`, ç„¶åŽæ‰“å¼€ `HBuilderX`,导入刚刚生成的`dist/build/app` æ–‡ä»¶å¤¹ï¼Œé€‰æ‹©å‘行 - APP云打包。
## ðŸ“„ License
[MIT](https://opensource.org/license/mit/)
Copyright (c) 2025 è²é¸½
## æèµ 
<p align='center'>
<img alt="special sponsor appwrite" src="./screenshots/pay-1.png" height="330" style="display:inline-block; height:330px;">
<img alt="special sponsor appwrite" src="./screenshots/pay-2.png" height="330" style="display:inline-block; height:330px; margin-left:10px;">
</p>
eims-ui-mobile/env/.env
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
VITE_APP_TITLE = '兰宝设备管理'
VITE_APP_PORT = 9000
VITE_UNI_APPID = 'H5F0B095D'
VITE_WX_APPID = ''
# h5部署网站的base,配置到 manifest.config.ts é‡Œçš„ h5.router.base
VITE_APP_PUBLIC_BASE=/
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
# æœ‰äº›åŒå­¦å¯èƒ½éœ€è¦åœ¨å¾®ä¿¡å°ç¨‹åºé‡Œé¢æ ¹æ® develop、trial、release åˆ†åˆ«è®¾ç½®ä¸Šä¼ åœ°å€ï¼Œå‚考代码如下。
# ä¸‹é¢çš„变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run'
VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run'
VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run'
VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run/upload'
VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run/upload'
VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run/upload'
# h5是否需要配置代理
VITE_APP_PROXY=false
VITE_APP_PROXY_PREFIX = '/api'
eims-ui-mobile/env/.env.development
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
# å˜é‡å¿…须以 VITE_ ä¸ºå‰ç¼€æ‰èƒ½æš´éœ²ç»™å¤–部读取
NODE_ENV = 'development'
# æ˜¯å¦å޻除console å’Œ debugger
VITE_DELETE_CONSOLE = false
# æ˜¯å¦å¼€å¯sourcemap
VITE_SHOW_SOURCEMAP = true
eims-ui-mobile/env/.env.production
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
# å˜é‡å¿…须以 VITE_ ä¸ºå‰ç¼€æ‰èƒ½æš´éœ²ç»™å¤–部读取
NODE_ENV = 'development'
# æ˜¯å¦å޻除console å’Œ debugger
VITE_DELETE_CONSOLE = true
# æ˜¯å¦å¼€å¯sourcemap
VITE_SHOW_SOURCEMAP = false
eims-ui-mobile/env/.env.test
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
# å˜é‡å¿…须以 VITE_ ä¸ºå‰ç¼€æ‰èƒ½æš´éœ²ç»™å¤–部读取
NODE_ENV = 'development'
# æ˜¯å¦å޻除console å’Œ debugger
VITE_DELETE_CONSOLE = false
eims-ui-mobile/favicon.ico
eims-ui-mobile/index.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
<!doctype html>
<html build-time="%BUILD_TIME%">
  <head>
    <meta charset="UTF-8" />
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
    <script>
      var coverSupport =
        'CSS' in window &&
        typeof CSS.supports === 'function' &&
        (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
      document.write(
        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
          (coverSupport ? ', viewport-fit=cover' : '') +
          '" />',
      )
    </script>
    <title>设备管理系统</title>
    <!--preload-links-->
    <!--app-context-->
  </head>
  <body>
    <div id="app"><!--app-html--></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
eims-ui-mobile/manifest.config.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,134 @@
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
import path from 'node:path'
import { loadEnv } from 'vite'
// èŽ·å–çŽ¯å¢ƒå˜é‡çš„èŒƒä¾‹
const env = loadEnv(process.env.NODE_ENV!, path.resolve(process.cwd(), 'env'))
const {
  VITE_APP_TITLE,
  VITE_UNI_APPID,
  VITE_WX_APPID,
  VITE_APP_PUBLIC_BASE,
  VITE_FALLBACK_LOCALE,
} = env
export default defineManifestConfig({
  name: VITE_APP_TITLE,
  appid: VITE_UNI_APPID,
  description: '',
  versionName: '1.0.0',
  versionCode: '100',
  transformPx: false,
  locale: VITE_FALLBACK_LOCALE, // 'zh-Hans'
  h5: {
    router: {
      base: VITE_APP_PUBLIC_BASE,
    },
  },
  /* 5+App特有相关 */
  'app-plus': {
    usingComponents: true,
    nvueStyleCompiler: 'uni-app',
    compilerVersion: 3,
    compatible: {
      ignoreVersion: true,
    },
    splashscreen: {
      alwaysShowBeforeRender: true,
      waiting: true,
      autoclose: true,
      delay: 0,
    },
    /* æ¨¡å—配置 */
    modules: {},
    /* åº”用发布信息 */
    distribute: {
      /* android打包配置 */
      android: {
        minSdkVersion: 21,
        targetSdkVersion: 30,
        abiFilters: ['armeabi-v7a', 'arm64-v8a'],
        permissions: [
          '<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
          '<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
          '<uses-permission android:name="android.permission.VIBRATE"/>',
          '<uses-permission android:name="android.permission.READ_LOGS"/>',
          '<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
          '<uses-feature android:name="android.hardware.camera.autofocus"/>',
          '<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
          '<uses-permission android:name="android.permission.CAMERA"/>',
          '<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
          '<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
          '<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
          '<uses-permission android:name="android.permission.WAKE_LOCK"/>',
          '<uses-permission android:name="android.permission.FLASHLIGHT"/>',
          '<uses-feature android:name="android.hardware.camera"/>',
          '<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
        ],
      },
      /* ios打包配置 */
      ios: {},
      /* SDK配置 */
      sdkConfigs: {},
      /* å›¾æ ‡é…ç½® */
      icons: {
        android: {
          hdpi: 'static/app/icons/72x72.png',
          xhdpi: 'static/app/icons/96x96.png',
          xxhdpi: 'static/app/icons/144x144.png',
          xxxhdpi: 'static/app/icons/192x192.png',
        },
        ios: {
          appstore: 'static/app/icons/1024x1024.png',
          ipad: {
            app: 'static/app/icons/76x76.png',
            'app@2x': 'static/app/icons/152x152.png',
            notification: 'static/app/icons/20x20.png',
            'notification@2x': 'static/app/icons/40x40.png',
            'proapp@2x': 'static/app/icons/167x167.png',
            settings: 'static/app/icons/29x29.png',
            'settings@2x': 'static/app/icons/58x58.png',
            spotlight: 'static/app/icons/40x40.png',
            'spotlight@2x': 'static/app/icons/80x80.png',
          },
          iphone: {
            'app@2x': 'static/app/icons/120x120.png',
            'app@3x': 'static/app/icons/180x180.png',
            'notification@2x': 'static/app/icons/40x40.png',
            'notification@3x': 'static/app/icons/60x60.png',
            'settings@2x': 'static/app/icons/58x58.png',
            'settings@3x': 'static/app/icons/87x87.png',
            'spotlight@2x': 'static/app/icons/80x80.png',
            'spotlight@3x': 'static/app/icons/120x120.png',
          },
        },
      },
    },
  },
  /* å¿«åº”用特有相关 */
  quickapp: {},
  /* å°ç¨‹åºç‰¹æœ‰ç›¸å…³ */
  'mp-weixin': {
    appid: VITE_WX_APPID,
    setting: {
      urlCheck: false,
    },
    usingComponents: true,
    // __usePrivacyCheck__: true,
  },
  'mp-alipay': {
    usingComponents: true,
    styleIsolation: 'shared',
  },
  'mp-baidu': {
    usingComponents: true,
  },
  'mp-toutiao': {
    usingComponents: true,
  },
  uniStatistics: {
    enable: false,
  },
  vueVersion: '3',
})
eims-ui-mobile/openapi-ts-request.config.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
import type { GenerateServiceProps } from 'openapi-ts-request'
export default [
  {
    schemaPath: 'http://petstore.swagger.io/v2/swagger.json',
    serversPath: './src/service/app',
    requestLibPath: `import request from '@/utils/request';\n import { CustomRequestOptions } from '@/interceptors/request';`,
    requestOptionsType: 'CustomRequestOptions',
    isGenReactQuery: true,
    reactQueryMode: 'vue',
    isGenJavaScript: false,
  },
] as GenerateServiceProps[]
eims-ui-mobile/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,174 @@
{
  "name": "eims-ui-mobile",
  "type": "commonjs",
  "version": "2.5.5",
  "description": "兰宝设备管理",
  "author": {
    "name": "feige996",
    "zhName": "菲鸽",
    "email": "1020103647@qq.com",
    "github": "https://github.com/feige996",
    "gitee": "https://gitee.com/feige996"
  },
  "license": "MIT",
  "repository": "https://github.com/feige996/unibest",
  "repository-gitee": "https://gitee.com/feige996/unibest",
  "repository-deprecated": "https://github.com/codercup/unibest",
  "bugs": {
    "url": "https://github.com/feige996/unibest/issues"
  },
  "homepage": "https://feige996.github.io/unibest/",
  "engines": {
    "node": ">=18",
    "pnpm": ">=7.30"
  },
  "scripts": {
    "preinstall": "npx only-allow pnpm",
    "uvm": "npx @dcloudio/uvm@latest",
    "uvm-rm": "node ./scripts/postupgrade.js",
    "postuvm": "echo upgrade uni-app success!",
    "dev:app": "uni -p app",
    "dev:app-android": "uni -p app-android",
    "dev:app-ios": "uni -p app-ios",
    "dev:custom": "uni -p",
    "dev": "uni",
    "dev:h5": "uni",
    "dev:h5:ssr": "uni --ssr",
    "dev:mp": "uni -p mp-weixin",
    "dev:mp-alipay": "uni -p mp-alipay",
    "dev:mp-baidu": "uni -p mp-baidu",
    "dev:mp-jd": "uni -p mp-jd",
    "dev:mp-kuaishou": "uni -p mp-kuaishou",
    "dev:mp-lark": "uni -p mp-lark",
    "dev:mp-qq": "uni -p mp-qq",
    "dev:mp-toutiao": "uni -p mp-toutiao",
    "dev:mp-weixin": "uni -p mp-weixin",
    "dev:mp-xhs": "uni -p mp-xhs",
    "dev:quickapp-webview": "uni -p quickapp-webview",
    "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
    "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
    "build:app": "uni build -p app",
    "build:app-android": "uni build -p app-android",
    "build:app-ios": "uni build -p app-ios",
    "build:custom": "uni build -p",
    "build:h5": "uni build",
    "build": "uni build",
    "build:h5:ssr": "uni build --ssr",
    "build:mp-alipay": "uni build -p mp-alipay",
    "build:mp": "uni build -p mp-weixin",
    "build:mp-baidu": "uni build -p mp-baidu",
    "build:mp-jd": "uni build -p mp-jd",
    "build:mp-kuaishou": "uni build -p mp-kuaishou",
    "build:mp-lark": "uni build -p mp-lark",
    "build:mp-qq": "uni build -p mp-qq",
    "build:mp-toutiao": "uni build -p mp-toutiao",
    "build:mp-weixin": "uni build -p mp-weixin",
    "build:mp-xhs": "uni build -p mp-xhs",
    "build:quickapp-webview": "uni build -p quickapp-webview",
    "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
    "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
    "prepare": "git init && husky install",
    "type-check": "vue-tsc --noEmit",
    "cz": "czg",
    "openapi-ts-request": "openapi-ts"
  },
  "lint-staged": {
    "**/*.{html,vue,ts,cjs,json,md}": [
      "prettier --write"
    ],
    "**/*.{vue,js,ts,jsx,tsx}": [
      "eslint --cache --fix"
    ],
    "**/*.{vue,css,scss,html}": [
      "stylelint --fix"
    ]
  },
  "resolutions": {
    "bin-wrapper": "npm:bin-wrapper-china"
  },
  "dependencies": {
    "@dcloudio/uni-app": "3.0.0-4050720250324001",
    "@dcloudio/uni-app-harmony": "3.0.0-4050720250324001",
    "@dcloudio/uni-app-plus": "3.0.0-4050720250324001",
    "@dcloudio/uni-components": "3.0.0-4050720250324001",
    "@dcloudio/uni-h5": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-alipay": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-baidu": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-jd": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-kuaishou": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-lark": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-qq": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-toutiao": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-weixin": "3.0.0-4050720250324001",
    "@dcloudio/uni-mp-xhs": "3.0.0-4050720250324001",
    "@dcloudio/uni-quickapp-webview": "3.0.0-4050720250324001",
    "@tanstack/vue-query": "^5.62.16",
    "abortcontroller-polyfill": "^1.7.8",
    "dayjs": "1.11.10",
    "pinia": "2.0.36",
    "pinia-plugin-persistedstate": "3.2.1",
    "qs": "6.5.3",
    "vue": "3.4.21",
    "vue-i18n": "^9.1.9",
    "wot-design-uni": "^1.4.0",
    "z-paging": "^2.8.4"
  },
  "devDependencies": {
    "@commitlint/cli": "^18.6.1",
    "@commitlint/config-conventional": "^18.6.3",
    "@dcloudio/types": "^3.4.14",
    "@dcloudio/uni-automator": "3.0.0-4050720250324001",
    "@dcloudio/uni-cli-shared": "3.0.0-4050720250324001",
    "@dcloudio/uni-stacktracey": "3.0.0-4050720250324001",
    "@dcloudio/vite-plugin-uni": "3.0.0-4050720250324001",
    "@esbuild/darwin-arm64": "0.20.2",
    "@esbuild/darwin-x64": "0.20.2",
    "@iconify-json/carbon": "^1.2.4",
    "@rollup/rollup-darwin-x64": "^4.28.0",
    "@types/node": "^20.17.9",
    "@types/wechat-miniprogram": "^3.4.8",
    "@typescript-eslint/eslint-plugin": "^6.21.0",
    "@typescript-eslint/parser": "^6.21.0",
    "@uni-helper/uni-types": "1.0.0-alpha.3",
    "@uni-helper/vite-plugin-uni-layouts": "^0.1.10",
    "@uni-helper/vite-plugin-uni-manifest": "^0.2.7",
    "@uni-helper/vite-plugin-uni-pages": "0.2.20",
    "@uni-helper/vite-plugin-uni-platform": "^0.0.4",
    "@unocss/preset-legacy-compat": "^0.59.4",
    "@vue/runtime-core": "^3.5.13",
    "@vue/tsconfig": "^0.1.3",
    "autoprefixer": "^10.4.20",
    "commitlint": "^18.6.1",
    "czg": "^1.9.4",
    "eslint": "^8.57.1",
    "eslint-config-prettier": "^9.1.0",
    "eslint-config-standard": "^17.1.0",
    "eslint-import-resolver-typescript": "^3.7.0",
    "eslint-plugin-import": "^2.31.0",
    "eslint-plugin-prettier": "^5.2.1",
    "eslint-plugin-vue": "^9.32.0",
    "husky": "^8.0.3",
    "lint-staged": "^15.2.10",
    "openapi-ts-request": "^1.1.2",
    "postcss": "^8.4.49",
    "postcss-html": "^1.7.0",
    "postcss-scss": "^4.0.9",
    "rollup-plugin-visualizer": "^5.12.0",
    "sass": "1.77.8",
    "stylelint": "^16.11.0",
    "stylelint-config-html": "^1.1.0",
    "stylelint-config-recess-order": "^4.6.0",
    "stylelint-config-recommended": "^14.0.1",
    "stylelint-config-recommended-scss": "^14.1.0",
    "stylelint-config-recommended-vue": "^1.5.0",
    "stylelint-prettier": "^5.0.2",
    "terser": "^5.36.0",
    "typescript": "^5.7.2",
    "unocss": "^0.58.9",
    "unocss-applet": "^0.7.8",
    "unplugin-auto-import": "^0.17.8",
    "vite": "5.2.8",
    "vite-plugin-restart": "^0.4.2",
    "vue-tsc": "^1.8.27"
  }
}
eims-ui-mobile/pages.config.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
export default defineUniPages({
  globalStyle: {
    navigationStyle: 'default',
    navigationBarTitleText: '兰宝设备管理',
    navigationBarBackgroundColor: '#f8f8f8',
    navigationBarTextStyle: 'black',
    backgroundColor: '#FFFFFF',
  },
  easycom: {
    autoscan: true,
    custom: {
      // '^fg-(.*)': '@/components/fg-$1.vue',
      '^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
      '^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
        'z-paging/components/z-paging$1/z-paging$1.vue',
    },
  },
  tabBar: {
    custom: true,
    color: '#999999',
    selectedColor: '#007aff',
    borderStyle: 'black',
    height: '50px',
    fontSize: '10px',
    iconWidth: '24px',
    spacing: '3px',
    list: [
      // æ³¨æ„tabbar路由需要使用 layout:tabbar å¸ƒå±€
      {
        pagePath: 'pages/home/index',
        text: '首页',
        icon: 'home',
        iconType: 'wot',
      },
      {
        pagePath: 'pages/equ/index',
        text: '设备',
        icon: 'mobile',
        iconType: 'wot',
      },
      {
        pagePath: 'pages/equ/index',
        icon: 'scan',
        iconType: 'wot',
      },
      // {
      //   pagePath: 'pages/my/index',
      //   text: '我的',
      //   icon: '/static/logo.svg',
      //   iconType: 'local',
      // },
      {
        pagePath: 'pages/spare/index',
        text: '备件',
        icon: 'setting1',
        iconType: 'wot',
      },
      {
        pagePath: 'pages/my/index',
        text: '我的',
        icon: 'user',
        iconType: 'wot',
      },
      // {
      //   pagePath: 'pages/my/index',
      //   text: '我的',
      //   icon: 'iconfont icon-my',
      //   iconType: 'iconfont',
      // },
    ],
  },
})
eims-ui-mobile/pnpm-lock.yaml
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
eims-ui-mobile/scripts/postupgrade.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
// # æ‰§è¡Œ `pnpm upgrade` åŽä¼šå‡çº§ `uniapp` ç›¸å…³ä¾èµ–
// # åœ¨å‡çº§å®ŒåŽï¼Œä¼šè‡ªåŠ¨æ·»åŠ å¾ˆå¤šæ— ç”¨ä¾èµ–ï¼Œè¿™éœ€è¦åˆ é™¤ä»¥å‡å°ä¾èµ–åŒ…ä½“ç§¯
// # åªéœ€è¦æ‰§è¡Œä¸‹é¢çš„命令即可
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { exec } = require('child_process')
// å®šä¹‰è¦æ‰§è¡Œçš„命令
const dependencies = [
  '@dcloudio/uni-app-harmony',
  // TODO: å¦‚果需要某个平台的小程序,请手动删除或注释掉
  '@dcloudio/uni-mp-alipay',
  '@dcloudio/uni-mp-baidu',
  '@dcloudio/uni-mp-jd',
  '@dcloudio/uni-mp-kuaishou',
  '@dcloudio/uni-mp-lark',
  '@dcloudio/uni-mp-qq',
  '@dcloudio/uni-mp-toutiao',
  '@dcloudio/uni-mp-xhs',
  '@dcloudio/uni-quickapp-webview',
  // i18n模板要注释掉下面的
  'vue-i18n',
]
// ä½¿ç”¨exec执行命令
exec(`pnpm un ${dependencies.join(' ')}`, (error, stdout, stderr) => {
  if (error) {
    // å¦‚果有错误,打印错误信息
    console.error(`执行出错: ${error}`)
    return
  }
  // æ‰“印正常输出
  console.log(`stdout: ${stdout}`)
  // å¦‚果有错误输出,也打印出来
  console.error(`stderr: ${stderr}`)
})
eims-ui-mobile/src/App.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
onLaunch(() => {
  console.log('App Launch')
})
onShow(() => {
  console.log('App Show')
})
onHide(() => {
  console.log('App Hide')
})
</script>
<style lang="scss">
/* stylelint-disable selector-type-no-unknown */
button::after {
  border: none;
}
swiper,
scroll-view {
  flex: 1;
  height: 100%;
  overflow: hidden;
}
image {
  width: 100%;
  height: 100%;
  vertical-align: middle;
}
// å•行省略,优先使用 unocss: text-ellipsis
.ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
// ä¸¤è¡Œçœç•¥
.ellipsis-2 {
  display: -webkit-box;
  overflow: hidden;
  text-overflow: ellipsis;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
// ä¸‰è¡Œçœç•¥
.ellipsis-3 {
  display: -webkit-box;
  overflow: hidden;
  text-overflow: ellipsis;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
</style>
eims-ui-mobile/src/components/.gitkeep
eims-ui-mobile/src/components/fg-tabbar/fg-tabbar.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
<template>
  <wd-tabbar
    fixed
    v-model="tabbarStore.curIdx"
    bordered
    safeAreaInsetBottom
    placeholder
    @change="selectTabBar"
    active-color="#007aff"
    inactive-color="#7d7e80"
  >
    <block v-for="(item, idx) in tabbarList" :key="item.path">
      <wd-tabbar-item
        v-if="idx !== 2 && item.iconType === 'wot'"
        :title="item.text"
        :icon="item.icon"
      ></wd-tabbar-item>
      <!--TODO æ‰«ç å›¾æ ‡ç‰¹æ®Šå¤„理-->
      <wd-tabbar-item
        v-if="idx === 2 && item.iconType === 'wot'"
        :title="item.text"
        :icon="item.icon"
      >
        <template #icon>
          <view class="scan-box">
            <wd-icon round name="scan" color="white" size="42rpx"></wd-icon>
          </view>
        </template>
      </wd-tabbar-item>
      <wd-tabbar-item
        v-else-if="item.iconType === 'unocss' || item.iconType === 'iconfont'"
        :title="item.text"
      >
        <template #icon>
          <view
            h-40rpx
            w-40rpx
            :class="[item.icon, idx === tabbarStore.curIdx ? 'is-active' : 'is-inactive']"
          ></view>
        </template>
      </wd-tabbar-item>
      <wd-tabbar-item v-else-if="item.iconType === 'local'" :title="item.text">
        <template #icon>
          <image :src="item.icon" h-40rpx w-40rpx />
        </template>
      </wd-tabbar-item>
    </block>
  </wd-tabbar>
</template>
<script setup lang="ts">
// unocss icon é»˜è®¤ä¸ç”Ÿæ•ˆï¼Œéœ€è¦åœ¨è¿™é‡Œå†™ä¸€éæ‰èƒ½ç”Ÿæ•ˆï¼æ³¨é‡ŠæŽ‰ä¹Ÿæ˜¯ç”Ÿæ•ˆçš„,但是必须要有!
// i-carbon-code
import { tabBar } from '@/pages.json'
import { tabbarStore } from './tabbar'
/** tabbarList é‡Œé¢çš„ path ä»Ž pages.config.ts å¾—到 */
const tabbarList = tabBar.list.map((item) => ({ ...item, path: `/${item.pagePath}` }))
function selectTabBar({ value: index }: { value: number }) {
  const url = tabbarList[index].path
  // scan特殊处理
  if (index === 2) {
    tabbarStore.setCurIdx(tabbarStore.lastIdx)
    // åªå…è®¸é€šè¿‡ç›¸æœºæ‰«ç 
    uni.scanCode({
      onlyFromCamera: true,
      success: function (res) {
        console.log('条码类型:' + res.scanType)
        console.log('条码内容:' + res.result)
      },
    })
  } else {
    tabbarStore.setCurIdx(index)
    tabbarStore.setLastIdx(index)
    uni.switchTab({ url })
  }
}
onLoad(() => {
  // è§£å†³åŽŸç”Ÿ tabBar æœªéšè—å¯¼è‡´æœ‰2个 tabBar çš„问题
  // #ifdef APP-PLUS | H5
  uni.hideTabBar({
    fail(err) {
      console.log('hideTabBar fail: ', err)
    },
    success(res) {
      console.log('hideTabBar success: ', res)
    },
  })
  // #endif
})
</script>
<style lang="scss" scoped>
.main-color {
  color: $uni-color-primary;
}
.scan-box {
  width: 100rpx;
  height: 100rpx;
  background-color: $uni-color-primary;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  margin-bottom: 30rpx;
}
</style>
eims-ui-mobile/src/components/fg-tabbar/tabbar.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
/**
 * tabbar çŠ¶æ€ï¼Œå¢žåŠ  storageSync ä¿è¯åˆ·æ–°æµè§ˆå™¨æ—¶åœ¨æ­£ç¡®çš„ tabbar é¡µé¢
 * ä½¿ç”¨reactive简单状态,而不是 pinia å…¨å±€çŠ¶æ€
 */
export const tabbarStore = reactive({
  curIdx: uni.getStorageSync('app-tabbar-index') || 0,
  lastIdx: uni.getStorageSync('app-tabbar-lastIdx-index') || 0,
  setCurIdx(idx: number) {
    this.curIdx = idx
    uni.setStorageSync('app-tabbar-index', idx)
  },
  setLastIdx(idx: number) {
    this.lastIdx = idx
    uni.setStorageSync('app-tabbar-lastIdx-index', idx)
  },
})
eims-ui-mobile/src/env.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
/// <reference types="vite/client" />
/// <reference types="vite-svg-loader" />
declare module '*.vue' {
  import { DefineComponent } from 'vue'
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
  const component: DefineComponent<{}, {}, any>
  export default component
}
interface ImportMetaEnv {
  /** ç½‘站标题,应用名称 */
  readonly VITE_APP_TITLE: string
  /** æœåŠ¡ç«¯å£å· */
  readonly VITE_SERVER_PORT: string
  /** åŽå°æŽ¥å£åœ°å€ */
  readonly VITE_SERVER_BASEURL: string
  /** H5是否需要代理 */
  readonly VITE_APP_PROXY: 'true' | 'false'
  /** H5是否需要代理,需要的话有个前缀 */
  readonly VITE_APP_PROXY_PREFIX: string // ä¸€èˆ¬æ˜¯/api
  /** ä¸Šä¼ å›¾ç‰‡åœ°å€ */
  readonly VITE_UPLOAD_BASEURL: string
  /** æ˜¯å¦æ¸…除console */
  readonly VITE_DELETE_CONSOLE: string
  // æ›´å¤šçŽ¯å¢ƒå˜é‡...
}
interface ImportMeta {
  readonly env: ImportMetaEnv
}
eims-ui-mobile/src/hooks/.gitkeep
eims-ui-mobile/src/hooks/useRequest.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
import { UnwrapRef } from 'vue'
type IUseRequestOptions<T> = {
  /** æ˜¯å¦ç«‹å³æ‰§è¡Œ */
  immediate?: boolean
  /** åˆå§‹åŒ–数据 */
  initialData?: T
}
/**
 * useRequest是一个定制化的请求钩子,用于处理异步请求和响应。
 * @param func ä¸€ä¸ªæ‰§è¡Œå¼‚步请求的函数,返回一个包含响应数据的Promise。
 * @param options åŒ…含请求选项的对象 {immediate, initialData}。
 * @param options.immediate æ˜¯å¦ç«‹å³æ‰§è¡Œè¯·æ±‚,默认为false。
 * @param options.initialData åˆå§‹åŒ–数据,默认为undefined。
 * @returns è¿”回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
 */
export default function useRequest<T>(
  func: () => Promise<IResData<T>>,
  options: IUseRequestOptions<T> = { immediate: false },
) {
  const loading = ref(false)
  const error = ref(false)
  const data = ref<T>(options.initialData)
  const run = async () => {
    loading.value = true
    return func()
      .then((res) => {
        data.value = res.data as UnwrapRef<T>
        error.value = false
        return data.value
      })
      .catch((err) => {
        error.value = err
        throw err
      })
      .finally(() => {
        loading.value = false
      })
  }
  options.immediate && run()
  return { loading, error, data, run }
}
eims-ui-mobile/src/hooks/useUpload.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
// TODO: åˆ«å¿˜åŠ æ›´æ”¹çŽ¯å¢ƒå˜é‡çš„ VITE_UPLOAD_BASEURL åœ°å€ã€‚
import { getEnvBaseUploadUrl } from '@/utils'
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
/**
 * useUpload æ˜¯ä¸€ä¸ªå®šåˆ¶åŒ–的请求钩子,用于处理上传图片。
 * @param formData é¢å¤–传递给后台的数据,如{name: '菲鸽'}。
 * @returns è¿”回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
 */
export default function useUpload<T = string>(formData: Record<string, any> = {}) {
  const loading = ref(false)
  const error = ref(false)
  const data = ref<T>()
  const run = () => {
    // #ifdef MP-WEIXIN
    // å¾®ä¿¡å°ç¨‹åºä»ŽåŸºç¡€åº“ 2.21.0 å¼€å§‹ï¼Œ wx.chooseImage åœæ­¢ç»´æŠ¤ï¼Œè¯·ä½¿ç”¨ uni.chooseMedia ä»£æ›¿ã€‚
    // å¾®ä¿¡å°ç¨‹åºåœ¨2023å¹´10月17日之后,使用本API需要配置隐私协议
    uni.chooseMedia({
      count: 1,
      mediaType: ['image'],
      success: (res) => {
        loading.value = true
        const tempFilePath = res.tempFiles[0].tempFilePath
        uploadFile<T>({ tempFilePath, formData, data, error, loading })
      },
      fail: (err) => {
        console.error('uni.chooseMedia err->', err)
        error.value = true
      },
    })
    // #endif
    // #ifndef MP-WEIXIN
    uni.chooseImage({
      count: 1,
      success: (res) => {
        loading.value = true
        const tempFilePath = res.tempFilePaths[0]
        uploadFile<T>({ tempFilePath, formData, data, error, loading })
      },
      fail: (err) => {
        console.error('uni.chooseImage err->', err)
        error.value = true
      },
    })
    // #endif
  }
  return { loading, error, data, run }
}
function uploadFile<T>({ tempFilePath, formData, data, error, loading }) {
  uni.uploadFile({
    url: VITE_UPLOAD_BASEURL,
    filePath: tempFilePath,
    name: 'file',
    formData,
    success: (uploadFileRes) => {
      data.value = uploadFileRes.data as T
    },
    fail: (err) => {
      console.error('uni.uploadFile err->', err)
      error.value = true
    },
    complete: () => {
      loading.value = false
    },
  })
}
eims-ui-mobile/src/interceptors/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
export { routeInterceptor } from './route'
export { requestInterceptor } from './request'
export { prototypeInterceptor } from './prototype'
eims-ui-mobile/src/interceptors/prototype.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
export const prototypeInterceptor = {
  install() {
    // è§£å†³ä½Žç‰ˆæœ¬æ‰‹æœºä¸è¯†åˆ« array.at() å¯¼è‡´è¿è¡ŒæŠ¥é”™çš„问题
    if (typeof Array.prototype.at !== 'function') {
      // eslint-disable-next-line no-extend-native
      Array.prototype.at = function (index: number) {
        if (index < 0) return this[this.length + index]
        if (index >= this.length) return undefined
        return this[index]
      }
    }
  },
}
eims-ui-mobile/src/interceptors/request.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
/* eslint-disable no-param-reassign */
import qs from 'qs'
import { useUserStore } from '@/store'
import { platform } from '@/utils/platform'
import { getEnvBaseUrl } from '@/utils'
export type CustomRequestOptions = UniApp.RequestOptions & {
  query?: Record<string, any>
  /** å‡ºé”™æ—¶æ˜¯å¦éšè—é”™è¯¯æç¤º */
  hideErrorToast?: boolean
} & IUniUploadFileOptions // æ·»åŠ uni.uploadFile参数类型
// è¯·æ±‚基准地址
const baseUrl = getEnvBaseUrl()
// æ‹¦æˆªå™¨é…ç½®
const httpInterceptor = {
  // æ‹¦æˆªå‰è§¦å‘
  invoke(options: CustomRequestOptions) {
    // æŽ¥å£è¯·æ±‚支持通过 query å‚数配置 queryString
    if (options.query) {
      const queryStr = qs.stringify(options.query)
      if (options.url.includes('?')) {
        options.url += `&${queryStr}`
      } else {
        options.url += `?${queryStr}`
      }
    }
    // éž http å¼€å¤´éœ€æ‹¼æŽ¥åœ°å€
    if (!options.url.startsWith('http')) {
      // #ifdef H5
      // console.log(__VITE_APP_PROXY__)
      if (JSON.parse(__VITE_APP_PROXY__)) {
        // å•¥éƒ½ä¸éœ€è¦åš
      } else {
        options.url = baseUrl + options.url
      }
      // #endif
      // éžH5正常拼接
      // #ifndef H5
      options.url = baseUrl + options.url
      // #endif
      // TIPS: å¦‚果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
    }
    // 1. è¯·æ±‚è¶…æ—¶
    options.timeout = 10000 // 10s
    // 2. ï¼ˆå¯é€‰ï¼‰æ·»åŠ å°ç¨‹åºç«¯è¯·æ±‚å¤´æ ‡è¯†
    options.header = {
      platform, // å¯é€‰ï¼Œä¸Ž uniapp å®šä¹‰çš„平台一致,告诉后台来源
      ...options.header,
    }
    // 3. æ·»åŠ  token è¯·æ±‚头标识
    const userStore = useUserStore()
    const { token } = userStore.userInfo as unknown as IUserInfo
    if (token) {
      options.header.Authorization = `Bearer ${token}`
    }
  },
}
export const requestInterceptor = {
  install() {
    // æ‹¦æˆª request è¯·æ±‚
    uni.addInterceptor('request', httpInterceptor)
    // æ‹¦æˆª uploadFile æ–‡ä»¶ä¸Šä¼ 
    uni.addInterceptor('uploadFile', httpInterceptor)
  },
}
eims-ui-mobile/src/interceptors/route.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
/**
 * by è²é¸½ on 2024-03-06
 * è·¯ç”±æ‹¦æˆªï¼Œé€šå¸¸ä¹Ÿæ˜¯ç™»å½•拦截
 * å¯ä»¥è®¾ç½®è·¯ç”±ç™½åå•,或者黑名单,看业务需要选哪一个
 * æˆ‘这里应为大部分都可以随便进入,所以使用黑名单
 */
import { useUserStore } from '@/store'
import { needLoginPages as _needLoginPages, getNeedLoginPages } from '@/utils'
// TODO Check
const loginRoute = '/pages/login/index'
const isLogined = () => {
  const userStore = useUserStore()
  return userStore.isLogined
}
const isDev = import.meta.env.DEV
// é»‘名单登录拦截器 - ï¼ˆé€‚用于大部分页面不需要登录,少部分页面需要登录)
const navigateToInterceptor = {
  // æ³¨æ„ï¼Œè¿™é‡Œçš„url是 '/' å¼€å¤´çš„,如 '/pages/index/index',跟 'pages.json' é‡Œé¢çš„ path ä¸åŒ
  invoke({ url }: { url: string }) {
    // console.log(url) // /pages/route-interceptor/index?name=feige&age=30
    const path = url.split('?')[0]
    let needLoginPages: string[] = []
    // ä¸ºäº†é˜²æ­¢å¼€å‘时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好
    if (isDev) {
      needLoginPages = getNeedLoginPages()
    } else {
      needLoginPages = _needLoginPages
    }
    const isNeedLogin = needLoginPages.includes(path)
    if (!isNeedLogin) {
      return true
    }
    const hasLogin = isLogined()
    if (hasLogin) {
      return true
    }
    const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
    uni.navigateTo({ url: redirectRoute })
    return false
  },
}
export const routeInterceptor = {
  install() {
    uni.addInterceptor('navigateTo', navigateToInterceptor)
    uni.addInterceptor('reLaunch', navigateToInterceptor)
    uni.addInterceptor('redirectTo', navigateToInterceptor)
    uni.addInterceptor('switchTab', navigateToInterceptor)
  },
}
eims-ui-mobile/src/layouts/default.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<template>
  <wd-config-provider :themeVars="themeVars">
    <slot />
    <wd-toast />
    <wd-message-box />
  </wd-config-provider>
</template>
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
const themeVars: ConfigProviderThemeVars = {
  // colorTheme: 'red',
  // buttonPrimaryBgColor: '#07c160',
  // buttonPrimaryColor: '#07c160',
}
</script>
eims-ui-mobile/src/layouts/demo.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<template>
  <wd-config-provider :themeVars="themeVars">
    <slot />
    <wd-toast />
    <wd-message-box />
  </wd-config-provider>
</template>
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
const themeVars: ConfigProviderThemeVars = {
  // colorTheme: 'red',
  // buttonPrimaryBgColor: '#07c160',
  // buttonPrimaryColor: '#07c160',
}
</script>
eims-ui-mobile/src/layouts/tabbar.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
<template>
  <wd-config-provider :themeVars="themeVars">
    <slot />
    <!-- æ³¨æ„ä¸‹é¢ï¼Œå¤šäº†ä¸€ä¸ªè‡ªå®šä¹‰tabbar -->
    <fg-tabbar />
    <wd-toast />
    <wd-message-box />
  </wd-config-provider>
</template>
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
const themeVars: ConfigProviderThemeVars = {
  // colorTheme: 'red',
  // buttonPrimaryBgColor: '#07c160',
  // buttonPrimaryColor: '#07c160',
}
</script>
eims-ui-mobile/src/main.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
import '@/style/index.scss'
import { VueQueryPlugin } from '@tanstack/vue-query'
import 'virtual:uno.css'
import { createSSRApp } from 'vue'
import App from './App.vue'
import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
import store from './store'
export function createApp() {
  const app = createSSRApp(App)
  app.use(store)
  app.use(routeInterceptor)
  app.use(requestInterceptor)
  app.use(prototypeInterceptor)
  app.use(VueQueryPlugin)
  return {
    app,
  }
}
eims-ui-mobile/src/manifest.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,111 @@
{
  "name": "兰宝设备管理",
  "appid": "H5F0B095D",
  "description": "",
  "versionName": "1.0.0",
  "versionCode": "100",
  "transformPx": false,
  "app-plus": {
    "usingComponents": true,
    "nvueStyleCompiler": "uni-app",
    "compilerVersion": 3,
    "splashscreen": {
      "alwaysShowBeforeRender": true,
      "waiting": true,
      "autoclose": true,
      "delay": 0
    },
    "modules": {},
    "distribute": {
      "android": {
        "permissions": [
          "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
          "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
          "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
          "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
          "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
          "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
          "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
          "<uses-permission android:name=\"android.permission.CAMERA\"/>",
          "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
          "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
          "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
          "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
          "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
          "<uses-feature android:name=\"android.hardware.camera\"/>",
          "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
        ],
        "minSdkVersion": 21,
        "targetSdkVersion": 30,
        "abiFilters": [
          "armeabi-v7a",
          "arm64-v8a"
        ]
      },
      "ios": {},
      "sdkConfigs": {},
      "icons": {
        "android": {
          "hdpi": "static/app/icons/72x72.png",
          "xhdpi": "static/app/icons/96x96.png",
          "xxhdpi": "static/app/icons/144x144.png",
          "xxxhdpi": "static/app/icons/192x192.png"
        },
        "ios": {
          "appstore": "static/app/icons/1024x1024.png",
          "ipad": {
            "app": "static/app/icons/76x76.png",
            "app@2x": "static/app/icons/152x152.png",
            "notification": "static/app/icons/20x20.png",
            "notification@2x": "static/app/icons/40x40.png",
            "proapp@2x": "static/app/icons/167x167.png",
            "settings": "static/app/icons/29x29.png",
            "settings@2x": "static/app/icons/58x58.png",
            "spotlight": "static/app/icons/40x40.png",
            "spotlight@2x": "static/app/icons/80x80.png"
          },
          "iphone": {
            "app@2x": "static/app/icons/120x120.png",
            "app@3x": "static/app/icons/180x180.png",
            "notification@2x": "static/app/icons/40x40.png",
            "notification@3x": "static/app/icons/60x60.png",
            "settings@2x": "static/app/icons/58x58.png",
            "settings@3x": "static/app/icons/87x87.png",
            "spotlight@2x": "static/app/icons/80x80.png",
            "spotlight@3x": "static/app/icons/120x120.png"
          }
        }
      }
    },
    "compatible": {
      "ignoreVersion": true
    }
  },
  "quickapp": {},
  "mp-weixin": {
    "appid": "",
    "setting": {
      "urlCheck": false
    },
    "usingComponents": true
  },
  "mp-alipay": {
    "usingComponents": true,
    "styleIsolation": "shared"
  },
  "mp-baidu": {
    "usingComponents": true
  },
  "mp-toutiao": {
    "usingComponents": true
  },
  "uniStatistics": {
    "enable": false
  },
  "vueVersion": "3",
  "h5": {
    "router": {
      "base": "/"
    }
  }
}
eims-ui-mobile/src/pages-sub/demo/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
<route lang="json5" type="page">
{
  style: { navigationBarTitleText: '分包页面 æ ‡é¢˜' },
}
</route>
<template>
  <view class="text-center">
    <view class="m-8">http://localhost:9000/#/pages-sub/demo/index</view>
    <view class="text-green-500">分包页面demo</view>
  </view>
</template>
<script lang="ts" setup>
// code here
</script>
<style lang="scss" scoped>
//
</style>
eims-ui-mobile/src/pages.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
{
  "globalStyle": {
    "navigationStyle": "default",
    "navigationBarTitleText": "兰宝设备管理",
    "navigationBarBackgroundColor": "#f8f8f8",
    "navigationBarTextStyle": "black",
    "backgroundColor": "#FFFFFF"
  },
  "easycom": {
    "autoscan": true,
    "custom": {
      "^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
      "^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
    }
  },
  "tabBar": {
    "custom": true,
    "color": "#999999",
    "selectedColor": "#007aff",
    "borderStyle": "black",
    "height": "50px",
    "fontSize": "10px",
    "iconWidth": "24px",
    "spacing": "3px",
    "list": [
      {
        "pagePath": "pages/home/index",
        "text": "首页",
        "icon": "home",
        "iconType": "wot"
      },
      {
        "pagePath": "pages/equ/index",
        "text": "设备",
        "icon": "mobile",
        "iconType": "wot"
      },
      {
        "pagePath": "pages/equ/index",
        "icon": "scan",
        "iconType": "wot"
      },
      {
        "pagePath": "pages/spare/index",
        "text": "备件",
        "icon": "setting1",
        "iconType": "wot"
      },
      {
        "pagePath": "pages/my/index",
        "text": "我的",
        "icon": "user",
        "iconType": "wot"
      }
    ]
  },
  "pages": [
    {
      "path": "pages/home/index",
      "type": "home",
      "layout": "tabbar",
      "style": {
        "navigationStyle": "custom",
        "navigationBarTitleText": "首页"
      }
    },
    {
      "path": "pages/equ/index",
      "type": "page",
      "layout": "tabbar",
      "style": {
        "navigationBarTitleText": "关于"
      }
    },
    {
      "path": "pages/login/index",
      "type": "page",
      "layout": "default",
      "style": {
        "navigationStyle": "custom",
        "navigationBarTitleText": "登录"
      }
    },
    {
      "path": "pages/my/index",
      "type": "page",
      "layout": "tabbar",
      "style": {
        "navigationBarTitleText": "我的"
      }
    },
    {
      "path": "pages/spare/index",
      "type": "page",
      "layout": "tabbar",
      "style": {
        "navigationBarTitleText": "spare"
      }
    }
  ],
  "subPackages": []
}
eims-ui-mobile/src/pages/equ/components/request.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<route lang="json5">
{
  layout: 'demo',
  style: {
    navigationBarTitleText: '请求',
  },
}
</route>
<template>
  <view class="p-6 text-center">
    <view class="my-2">使用的是 laf äº‘后台</view>
    <view class="text-green-400">我的推荐码,可以获得佣金</view>
    <!-- #ifdef H5 -->
    <view class="my-2">
      <a class="my-2" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
    </view>
    <!-- #endif -->
    <!-- #ifndef H5 -->
    <view class="my-2 text-left text-sm">{{ recommendUrl }}</view>
    <!-- #endif -->
    <!-- http://localhost:9000/#/pages/index/request -->
    <wd-button @click="run" class="my-6">发送请求</wd-button>
    <view class="h-16">
      <view v-if="loading">loading...</view>
      <block v-else>
        <view class="text-xl">请求数据如下</view>
        <view class="text-green leading-8">{{ JSON.stringify(data) }}</view>
      </block>
    </view>
    <wd-button type="error" @click="reset" class="my-6" :disabled="!data">重置数据</wd-button>
  </view>
</template>
<script lang="ts" setup>
import { getFooAPI, postFooAPI, IFooItem } from '@/service/index/foo'
import { findPetsByStatusQueryOptions } from '@/service/app'
import { useQuery } from '@tanstack/vue-query'
const recommendUrl = ref('http://laf.run/signup?code=ohaOgIX')
// const initialData = {
//   name: 'initialData',
//   id: '1234',
// }
const initialData = undefined
// é€‚合少部分全局性的接口————多个页面都需要的请求接口,额外编写一个 Service å±‚
const { loading, error, data, run } = useRequest<IFooItem>(() => getFooAPI('菲鸽'), {
  immediate: true,
  initialData,
})
// ä½¿ç”¨ vue-query çš„ useQuery æ¥è¯·æ±‚数据,只做参考,是否使用请根据实际情况而定
const {
  data: data2,
  error: error2,
  isLoading: isLoading2,
  refetch,
} = useQuery(findPetsByStatusQueryOptions({ params: { status: ['available'] } }))
const reset = () => {
  data.value = initialData
}
</script>
eims-ui-mobile/src/pages/equ/components/upload.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
<route lang="json5" type="page">
{
  layout: 'default',
  style: {
    navigationBarTitleText: '上传-状态一体化',
  },
}
</route>
<template>
  <view class="p-4 text-center">
    <wd-button @click="run">选择图片并上传</wd-button>
    <view v-if="loading" class="text-blue h-10">上传...</view>
    <template v-else>
      <view class="m-2">上传后返回的接口数据:</view>
      <view class="m-2">{{ data }}</view>
      <view class="h-80 w-full">
        <image v-if="data" :src="data || data" mode="scaleToFill" />
      </view>
    </template>
  </view>
</template>
<script lang="ts" setup>
const { loading, data, run } = useUpload({ user: '菲鸽' })
</script>
<style lang="scss" scoped>
//
</style>
eims-ui-mobile/src/pages/equ/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
<route lang="json5">
{
  layout: 'tabbar',
  style: {
    navigationBarTitleText: '关于',
  },
}
</route>
<template>
  <view
    class="bg-white overflow-hidden pt-2 px-4"
    :style="{ marginTop: safeAreaInsets?.top + 'px' }"
  >
    <view class="text-center text-3xl mt-8">
      é¸½å‹ä»¬å¥½ï¼Œæˆ‘是
      <text class="text-red-500">菲鸽</text>
    </view>
    <RequestComp />
    <UploadComp />
  </view>
</template>
<script lang="ts" setup>
import RequestComp from './components/request.vue'
import UploadComp from './components/upload.vue'
// èŽ·å–å±å¹•è¾¹ç•Œåˆ°å®‰å…¨åŒºåŸŸè·ç¦»
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>
<style lang="scss" scoped>
.test-css {
  // mt-4=>1rem=>16px;
  margin-top: 16px;
}
</style>
eims-ui-mobile/src/pages/home/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
<!-- ä½¿ç”¨ type="home" å±žæ€§è®¾ç½®é¦–页,其他页面不需要设置,默认为page;推荐使用json5,更强大,且允许注释 -->
<route lang="json5" type="home">
{
  layout: 'tabbar',
  style: {
    navigationStyle: 'custom',
    navigationBarTitleText: '首页',
  },
}
</route>
<template>
  <view
    class="bg-white overflow-hidden pt-2 px-4"
    :style="{ marginTop: safeAreaInsets?.top + 'px' }"
  >
    <view class="mt-12">
      <image src="/static/logo.svg" alt="" class="w-28 h-28 block mx-auto" />
    </view>
    <view class="text-center text-4xl main-title-color mt-4">unibest</view>
    <view class="text-center text-2xl mt-2 mb-8">最好用的 uniapp å¼€å‘模板</view>
    <view class="text-justify max-w-100 m-auto text-4 indent mb-2">{{ description }}</view>
    <view class="text-center mt-8">
      å½“前平台是:
      <text class="text-green-500">{{ PLATFORM.platform }}</text>
    </view>
    <view class="text-center mt-4">
      æ¨¡æ¿åˆ†æ”¯æ˜¯ï¼š
      <text class="text-green-500">tabbar</text>
    </view>
  </view>
</template>
<script lang="ts" setup>
import { TestEnum } from '@/typings'
import PLATFORM from '@/utils/platform'
defineOptions({
  name: 'Home',
})
// èŽ·å–å±å¹•è¾¹ç•Œåˆ°å®‰å…¨åŒºåŸŸè·ç¦»
const { safeAreaInsets } = uni.getSystemInfoSync()
const author = ref('菲鸽')
const description = ref(
  'unibest æ˜¯ä¸€ä¸ªé›†æˆäº†å¤šç§å·¥å…·å’ŒæŠ€æœ¯çš„ uniapp å¼€å‘模板,由 uniapp + Vue3 + Ts + Vite4 + UnoCss + UniUI + VSCode æž„建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp æ‹¥æœ‰ best ä½“验。',
)
// æµ‹è¯• uni API è‡ªåЍ引入
onLoad(() => {
  console.log(author)
  console.log(TestEnum.A)
})
</script>
<style lang="scss" scoped>
.main-title-color {
  color: $uni-color-primary;
}
</style>
eims-ui-mobile/src/pages/login/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
<route lang="json5">
{
  layout: 'default',
  style: {
    navigationStyle: 'custom',
    navigationBarTitleText: '登录',
  },
}
</route>
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>
eims-ui-mobile/src/pages/my/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
<route lang="json5" type="page">
{
  layout: 'tabbar',
  style: {
    navigationBarTitleText: '我的',
  },
}
</route>
<template>
  <view class="pt-40 text-xl text-center text-green-500">我的页面</view>
</template>
<script lang="ts" setup>
//
</script>
<style lang="scss" scoped>
//
</style>
eims-ui-mobile/src/pages/spare/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
<route lang="json5" type="page">
{
  layout: 'tabbar',
  style: {
    navigationBarTitleText: 'spare',
  },
}
</route>
<template>
  <view class="pt-40 text-xl text-center text-green-500">备件</view>
</template>
<script lang="ts" setup>
//
</script>
<style lang="scss" scoped>
//
</style>
eims-ui-mobile/src/service/app/displayEnumLabel.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
/* eslint-disable */
// @ts-ignore
import * as API from './types';
export function displayStatusEnum(field: API.IStatusEnum) {
  return { available: 'available', pending: 'pending', sold: 'sold' }[field];
}
export function displayStatusEnum2(field: API.IStatusEnum2) {
  return { placed: 'placed', approved: 'approved', delivered: 'delivered' }[
    field
  ];
}
eims-ui-mobile/src/service/app/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
/* eslint-disable */
// @ts-ignore
export * from './types';
export * from './displayEnumLabel';
export * from './pet';
export * from './pet.vuequery';
export * from './store';
export * from './store.vuequery';
export * from './user';
export * from './user.vuequery';
eims-ui-mobile/src/service/app/pet.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,193 @@
/* eslint-disable */
// @ts-ignore
import request from '@/utils/request';
import { CustomRequestOptions } from '@/interceptors/request';
import * as API from './types';
/** Update an existing pet PUT /pet */
export async function updatePet({
  body,
  options,
}: {
  body: API.Pet;
  options?: CustomRequestOptions;
}) {
  return request<unknown>('/pet', {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** Add a new pet to the store POST /pet */
export async function addPet({
  body,
  options,
}: {
  body: API.Pet;
  options?: CustomRequestOptions;
}) {
  return request<unknown>('/pet', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** Find pet by ID Returns a single pet GET /pet/${param0} */
export async function getPetById({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.getPetByIdParams;
  options?: CustomRequestOptions;
}) {
  const { petId: param0, ...queryParams } = params;
  return request<API.Pet>(`/pet/${param0}`, {
    method: 'GET',
    params: { ...queryParams },
    ...(options || {}),
  });
}
/** Updates a pet in the store with form data POST /pet/${param0} */
export async function updatePetWithForm({
  params,
  body,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.updatePetWithFormParams;
  body: {
    /** Updated name of the pet */
    name?: string;
    /** Updated status of the pet */
    status?: string;
  };
  options?: CustomRequestOptions;
}) {
  const { petId: param0, ...queryParams } = params;
  return request<unknown>(`/pet/${param0}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    params: { ...queryParams },
    data: body,
    ...(options || {}),
  });
}
/** Deletes a pet DELETE /pet/${param0} */
export async function deletePet({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.deletePetParams;
  options?: CustomRequestOptions;
}) {
  const { petId: param0, ...queryParams } = params;
  return request<unknown>(`/pet/${param0}`, {
    method: 'DELETE',
    params: { ...queryParams },
    ...(options || {}),
  });
}
/** uploads an image POST /pet/${param0}/uploadImage */
export async function uploadFile({
  params,
  body,
  file,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.uploadFileParams;
  body: {
    /** Additional data to pass to server */
    additionalMetadata?: string;
  };
  file?: File;
  options?: CustomRequestOptions;
}) {
  const { petId: param0, ...queryParams } = params;
  const formData = new FormData();
  if (file) {
    formData.append('file', file);
  }
  Object.keys(body).forEach((ele) => {
    const item = (body as { [key: string]: any })[ele];
    if (item !== undefined && item !== null) {
      if (typeof item === 'object' && !(item instanceof File)) {
        if (item instanceof Array) {
          item.forEach((f) => formData.append(ele, f || ''));
        } else {
          formData.append(ele, JSON.stringify(item));
        }
      } else {
        formData.append(ele, item);
      }
    }
  });
  return request<API.ApiResponse>(`/pet/${param0}/uploadImage`, {
    method: 'POST',
    headers: {
      'Content-Type': 'multipart/form-data',
    },
    params: { ...queryParams },
    data: formData,
    ...(options || {}),
  });
}
/** Finds Pets by status Multiple status values can be provided with comma separated strings GET /pet/findByStatus */
export async function findPetsByStatus({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.findPetsByStatusParams;
  options?: CustomRequestOptions;
}) {
  return request<API.Pet[]>('/pet/findByStatus', {
    method: 'GET',
    params: {
      ...params,
    },
    ...(options || {}),
  });
}
/** Finds Pets by tags Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. GET /pet/findByTags */
export async function findPetsByTags({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.findPetsByTagsParams;
  options?: CustomRequestOptions;
}) {
  return request<API.Pet[]>('/pet/findByTags', {
    method: 'GET',
    params: {
      ...params,
    },
    ...(options || {}),
  });
}
eims-ui-mobile/src/service/app/pet.vuequery.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,151 @@
/* eslint-disable */
// @ts-ignore
import { queryOptions, useMutation } from '@tanstack/vue-query';
import type { DefaultError } from '@tanstack/vue-query';
import request from '@/utils/request';
import { CustomRequestOptions } from '@/interceptors/request';
import * as apis from './pet';
import * as API from './types';
/** Update an existing pet PUT /pet */
export function useUpdatePetMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.updatePet,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Add a new pet to the store POST /pet */
export function useAddPetMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.addPet,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Find pet by ID Returns a single pet GET /pet/${param0} */
export function getPetByIdQueryOptions(options: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.getPetByIdParams;
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.getPetById(queryKey[1] as typeof options);
    },
    queryKey: ['getPetById', options],
  });
}
/** Updates a pet in the store with form data POST /pet/${param0} */
export function useUpdatePetWithFormMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.updatePetWithForm,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Deletes a pet DELETE /pet/${param0} */
export function useDeletePetMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.deletePet,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** uploads an image POST /pet/${param0}/uploadImage */
export function useUploadFileMutation(options?: {
  onSuccess?: (value?: API.ApiResponse) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.uploadFile,
    onSuccess(data: API.ApiResponse) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Finds Pets by status Multiple status values can be provided with comma separated strings GET /pet/findByStatus */
export function findPetsByStatusQueryOptions(options: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.findPetsByStatusParams;
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.findPetsByStatus(queryKey[1] as typeof options);
    },
    queryKey: ['findPetsByStatus', options],
  });
}
/** Finds Pets by tags Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. GET /pet/findByTags */
export function findPetsByTagsQueryOptions(options: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.findPetsByTagsParams;
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.findPetsByTags(queryKey[1] as typeof options);
    },
    queryKey: ['findPetsByTags', options],
  });
}
eims-ui-mobile/src/service/app/store.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
/* eslint-disable */
// @ts-ignore
import request from '@/utils/request';
import { CustomRequestOptions } from '@/interceptors/request';
import * as API from './types';
/** Returns pet inventories by status Returns a map of status codes to quantities GET /store/inventory */
export async function getInventory({
  options,
}: {
  options?: CustomRequestOptions;
}) {
  return request<Record<string, unknown>>('/store/inventory', {
    method: 'GET',
    ...(options || {}),
  });
}
/** Place an order for a pet POST /store/order */
export async function placeOrder({
  body,
  options,
}: {
  body: API.Order;
  options?: CustomRequestOptions;
}) {
  return request<API.Order>('/store/order', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** Find purchase order by ID For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions GET /store/order/${param0} */
export async function getOrderById({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.getOrderByIdParams;
  options?: CustomRequestOptions;
}) {
  const { orderId: param0, ...queryParams } = params;
  return request<API.Order>(`/store/order/${param0}`, {
    method: 'GET',
    params: { ...queryParams },
    ...(options || {}),
  });
}
/** Delete purchase order by ID For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors DELETE /store/order/${param0} */
export async function deleteOrder({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.deleteOrderParams;
  options?: CustomRequestOptions;
}) {
  const { orderId: param0, ...queryParams } = params;
  return request<unknown>(`/store/order/${param0}`, {
    method: 'DELETE',
    params: { ...queryParams },
    ...(options || {}),
  });
}
eims-ui-mobile/src/service/app/store.vuequery.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
/* eslint-disable */
// @ts-ignore
import { queryOptions, useMutation } from '@tanstack/vue-query';
import type { DefaultError } from '@tanstack/vue-query';
import request from '@/utils/request';
import { CustomRequestOptions } from '@/interceptors/request';
import * as apis from './store';
import * as API from './types';
/** Returns pet inventories by status Returns a map of status codes to quantities GET /store/inventory */
export function getInventoryQueryOptions(options: {
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.getInventory(queryKey[1] as typeof options);
    },
    queryKey: ['getInventory', options],
  });
}
/** Place an order for a pet POST /store/order */
export function usePlaceOrderMutation(options?: {
  onSuccess?: (value?: API.Order) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.placeOrder,
    onSuccess(data: API.Order) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Find purchase order by ID For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions GET /store/order/${param0} */
export function getOrderByIdQueryOptions(options: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.getOrderByIdParams;
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.getOrderById(queryKey[1] as typeof options);
    },
    queryKey: ['getOrderById', options],
  });
}
/** Delete purchase order by ID For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors DELETE /store/order/${param0} */
export function useDeleteOrderMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.deleteOrder,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
eims-ui-mobile/src/service/app/types.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,128 @@
/* eslint-disable */
// @ts-ignore
export type ApiResponse = {
  code?: number;
  type?: string;
  message?: string;
};
export type Category = {
  id?: number;
  name?: string;
};
export type deleteOrderParams = {
  /** ID of the order that needs to be deleted */
  orderId: number;
};
export type deletePetParams = {
  /** Pet id to delete */
  petId: number;
};
export type deleteUserParams = {
  /** The name that needs to be deleted */
  username: string;
};
export type findPetsByStatusParams = {
  /** Status values that need to be considered for filter */
  status: ('available' | 'pending' | 'sold')[];
};
export type findPetsByTagsParams = {
  /** Tags to filter by */
  tags: string[];
};
export type getOrderByIdParams = {
  /** ID of pet that needs to be fetched */
  orderId: number;
};
export type getPetByIdParams = {
  /** ID of pet to return */
  petId: number;
};
export type getUserByNameParams = {
  /** The name that needs to be fetched. Use user1 for testing.  */
  username: string;
};
export type loginUserParams = {
  /** The user name for login */
  username: string;
  /** The password for login in clear text */
  password: string;
};
export type Order = {
  id?: number;
  petId?: number;
  quantity?: number;
  shipDate?: string;
  /** Order Status */
  status?: 'placed' | 'approved' | 'delivered';
  complete?: boolean;
};
export type Pet = {
  id?: number;
  category?: Category;
  name: string;
  photoUrls: string[];
  tags?: Tag[];
  /** pet status in the store */
  status?: 'available' | 'pending' | 'sold';
};
export enum StatusEnum {
  available = 'available',
  pending = 'pending',
  sold = 'sold',
}
export type IStatusEnum = keyof typeof StatusEnum;
export enum StatusEnum2 {
  placed = 'placed',
  approved = 'approved',
  delivered = 'delivered',
}
export type IStatusEnum2 = keyof typeof StatusEnum2;
export type Tag = {
  id?: number;
  name?: string;
};
export type updatePetWithFormParams = {
  /** ID of pet that needs to be updated */
  petId: number;
};
export type updateUserParams = {
  /** name that need to be updated */
  username: string;
};
export type uploadFileParams = {
  /** ID of pet to update */
  petId: number;
};
export type User = {
  id?: number;
  username?: string;
  firstName?: string;
  lastName?: string;
  email?: string;
  password?: string;
  phone?: string;
  /** User Status */
  userStatus?: number;
};
eims-ui-mobile/src/service/app/user.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,150 @@
/* eslint-disable */
// @ts-ignore
import request from '@/utils/request';
import { CustomRequestOptions } from '@/interceptors/request';
import * as API from './types';
/** Create user This can only be done by the logged in user. è¿”回值: successful operation POST /user */
export async function createUser({
  body,
  options,
}: {
  body: API.User;
  options?: CustomRequestOptions;
}) {
  return request<unknown>('/user', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** Get user by user name GET /user/${param0} */
export async function getUserByName({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.getUserByNameParams;
  options?: CustomRequestOptions;
}) {
  const { username: param0, ...queryParams } = params;
  return request<API.User>(`/user/${param0}`, {
    method: 'GET',
    params: { ...queryParams },
    ...(options || {}),
  });
}
/** Updated user This can only be done by the logged in user. PUT /user/${param0} */
export async function updateUser({
  params,
  body,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.updateUserParams;
  body: API.User;
  options?: CustomRequestOptions;
}) {
  const { username: param0, ...queryParams } = params;
  return request<unknown>(`/user/${param0}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
    },
    params: { ...queryParams },
    data: body,
    ...(options || {}),
  });
}
/** Delete user This can only be done by the logged in user. DELETE /user/${param0} */
export async function deleteUser({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.deleteUserParams;
  options?: CustomRequestOptions;
}) {
  const { username: param0, ...queryParams } = params;
  return request<unknown>(`/user/${param0}`, {
    method: 'DELETE',
    params: { ...queryParams },
    ...(options || {}),
  });
}
/** Creates list of users with given input array è¿”回值: successful operation POST /user/createWithArray */
export async function createUsersWithArrayInput({
  body,
  options,
}: {
  body: API.User[];
  options?: CustomRequestOptions;
}) {
  return request<unknown>('/user/createWithArray', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** Creates list of users with given input array è¿”回值: successful operation POST /user/createWithList */
export async function createUsersWithListInput({
  body,
  options,
}: {
  body: API.User[];
  options?: CustomRequestOptions;
}) {
  return request<unknown>('/user/createWithList', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** Logs user into the system GET /user/login */
export async function loginUser({
  params,
  options,
}: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.loginUserParams;
  options?: CustomRequestOptions;
}) {
  return request<string>('/user/login', {
    method: 'GET',
    params: {
      ...params,
    },
    ...(options || {}),
  });
}
/** Logs out current logged in user session è¿”回值: successful operation GET /user/logout */
export async function logoutUser({
  options,
}: {
  options?: CustomRequestOptions;
}) {
  return request<unknown>('/user/logout', {
    method: 'GET',
    ...(options || {}),
  });
}
eims-ui-mobile/src/service/app/user.vuequery.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,149 @@
/* eslint-disable */
// @ts-ignore
import { queryOptions, useMutation } from '@tanstack/vue-query';
import type { DefaultError } from '@tanstack/vue-query';
import request from '@/utils/request';
import { CustomRequestOptions } from '@/interceptors/request';
import * as apis from './user';
import * as API from './types';
/** Create user This can only be done by the logged in user. è¿”回值: successful operation POST /user */
export function useCreateUserMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.createUser,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Get user by user name GET /user/${param0} */
export function getUserByNameQueryOptions(options: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.getUserByNameParams;
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.getUserByName(queryKey[1] as typeof options);
    },
    queryKey: ['getUserByName', options],
  });
}
/** Updated user This can only be done by the logged in user. PUT /user/${param0} */
export function useUpdateUserMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.updateUser,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Delete user This can only be done by the logged in user. DELETE /user/${param0} */
export function useDeleteUserMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.deleteUser,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Creates list of users with given input array è¿”回值: successful operation POST /user/createWithArray */
export function useCreateUsersWithArrayInputMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.createUsersWithArrayInput,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Creates list of users with given input array è¿”回值: successful operation POST /user/createWithList */
export function useCreateUsersWithListInputMutation(options?: {
  onSuccess?: (value?: unknown) => void;
  onError?: (error?: DefaultError) => void;
}) {
  const { onSuccess, onError } = options || {};
  const response = useMutation({
    mutationFn: apis.createUsersWithListInput,
    onSuccess(data: unknown) {
      onSuccess?.(data);
    },
    onError(error) {
      onError?.(error);
    },
  });
  return response;
}
/** Logs user into the system GET /user/login */
export function loginUserQueryOptions(options: {
  // å åŠ ç”Ÿæˆçš„Param类型 (非body参数openapi默认没有生成对象)
  params: API.loginUserParams;
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.loginUser(queryKey[1] as typeof options);
    },
    queryKey: ['loginUser', options],
  });
}
/** Logs out current logged in user session è¿”回值: successful operation GET /user/logout */
export function logoutUserQueryOptions(options: {
  options?: CustomRequestOptions;
}) {
  return queryOptions({
    queryFn: async ({ queryKey }) => {
      return apis.logoutUser(queryKey[1] as typeof options);
    },
    queryKey: ['logoutUser', options],
  });
}
eims-ui-mobile/src/service/index/foo.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
import { http } from '@/utils/http'
export interface IFooItem {
  id: string
  name: string
}
/** GET è¯·æ±‚ */
export const getFooAPI = (name: string) => {
  return http.get<IFooItem>('/foo', { name })
}
/** POST è¯·æ±‚ */
export const postFooAPI = (name: string) => {
  return http.post<IFooItem>('/foo', { name }, { name })
}
eims-ui-mobile/src/static/.DS_Store
Binary files differ
eims-ui-mobile/src/static/app/.DS_Store
Binary files differ
eims-ui-mobile/src/static/app/icons/.DS_Store
Binary files differ
eims-ui-mobile/src/static/app/icons/1024x1024.png
eims-ui-mobile/src/static/app/icons/120x120.png
eims-ui-mobile/src/static/app/icons/144x144.png
eims-ui-mobile/src/static/app/icons/152x152.png
eims-ui-mobile/src/static/app/icons/167x167.png
eims-ui-mobile/src/static/app/icons/180x180.png
eims-ui-mobile/src/static/app/icons/192x192.png
eims-ui-mobile/src/static/app/icons/20x20.png
eims-ui-mobile/src/static/app/icons/29x29.png
eims-ui-mobile/src/static/app/icons/40x40.png
eims-ui-mobile/src/static/app/icons/58x58.png
eims-ui-mobile/src/static/app/icons/60x60.png
eims-ui-mobile/src/static/app/icons/72x72.png
eims-ui-mobile/src/static/app/icons/76x76.png
eims-ui-mobile/src/static/app/icons/80x80.png
eims-ui-mobile/src/static/app/icons/87x87.png
eims-ui-mobile/src/static/app/icons/96x96.png
eims-ui-mobile/src/static/images/.gitkeep
eims-ui-mobile/src/static/logo.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1744441358272" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1641" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M327.33 160.57a1993.38 1993.38 0 0 1 369.34 0C777 168.94 855.11 247 863.48 327.3a2005.94 2005.94 0 0 1 0 369.37C855.11 777 777 855.13 696.67 863.5a2010.9 2010.9 0 0 1-369.34 0C247 855.13 168.89 777 160.51 696.67a2007.7 2007.7 0 0 1 0-369.37C168.89 247 247 168.94 327.33 160.57z" fill="#2A2AFB" p-id="1642"></path><path d="M696.67 863.5a2010.9 2010.9 0 0 1-369.34 0C247 855.13 168.89 777 160.51 696.67c-3.46-37.52-5.74-75.13-7.09-112.65 1.35-37.61 3.6-75.14 7.09-112.75C168.89 391 247 312.91 327.33 304.54a1993.38 1993.38 0 0 1 369.34 0C777 312.91 855.11 391 863.48 471.27c3.47 37.61 5.74 75.14 7.09 112.75-1.35 37.52-3.59 75.13-7.09 112.65C855.11 777 777 855.13 696.67 863.5z" fill="#4444FB" p-id="1643"></path><path d="M696.67 863.5a2010.9 2010.9 0 0 1-369.34 0C247 855.13 168.89 777 160.51 696.67c-1.25-13.49-2.3-27.08-3.28-40.67 1-13.59 2-27.17 3.28-40.76C168.89 535 247 456.88 327.33 448.51a1993.38 1993.38 0 0 1 369.34 0C777 456.88 855.11 535 863.48 615.24c1.25 13.59 2.32 27.17 3.29 40.76-1 13.59-2 27.18-3.29 40.67C855.11 777 777 855.13 696.67 863.5z" fill="#5D5DFD" p-id="1644"></path><path d="M696.67 863.5a2010.9 2010.9 0 0 1-369.34 0c-69.83-7.29-137.89-67.22-160-135.51 22.15-68.3 90.21-128.32 160-135.51a1993.38 1993.38 0 0 1 369.34 0c69.83 7.19 137.9 67.21 160 135.51-22.1 68.29-90.17 128.22-160 135.51z" fill="#7A7AFD" p-id="1645"></path><path d="M327.33 863.5c-45.09-4.68-89.47-31.32-121.21-68 31.74-36.71 76.12-63.35 121.21-68a1993.38 1993.38 0 0 1 369.34 0c45.09 4.67 89.46 31.31 121.2 68-31.74 36.71-76.11 63.35-121.2 68a2010.9 2010.9 0 0 1-369.34 0z" fill="#8E8FFE" p-id="1646"></path><path d="M690.06 415.76a81.08 81.08 0 0 1-11.52 36.84 70.73 70.73 0 0 1-52.74 32.66 139.71 139.71 0 0 1-17.12 0.09 15.64 15.64 0 0 0-11.57 4.71q-38 37.77-75.93 75.63a7290.78 7290.78 0 0 1-34.12 34 21.15 21.15 0 0 0-6.39 14.86A71.8 71.8 0 0 1 457 665.88a75 75 0 0 1-52.65 19.67c-3-0.45-8.77-1.43-14.6-2.32a5.48 5.48 0 0 1-4.94-4.8 5.63 5.63 0 0 1 3.3-5.61c9.53-6 19.05-12 28.59-18a17.8 17.8 0 0 0 7.68-24 18.37 18.37 0 0 0-1.8-2.81c-6.76-10.41-13.24-21-20-31.41a16.8 16.8 0 0 0-21.69-6 115.85 115.85 0 0 0-11.27 6.76c-8.72 5.51-17.42 11-26.1 16.64a5.71 5.71 0 0 1-9.38-6.14 82.6 82.6 0 0 1 9.93-33.73 70.65 70.65 0 0 1 53.77-35.32c5.76-0.71 11.61-0.71 17.42-0.89a15.55 15.55 0 0 0 10.17-4.63q30.46-28.95 60.95-57.74c17.53-16.64 35-33.37 52.5-50a15.23 15.23 0 0 0 4.64-11.92 70.28 70.28 0 0 1 15.84-47 71.14 71.14 0 0 1 59.92-27.85 133 133 0 0 1 15.53 2 5.14 5.14 0 0 1 4.56 4.81 5.54 5.54 0 0 1-3.07 5.43c-9.61 6.05-19.21 12.19-28.81 18.24a17.71 17.71 0 0 0-7.72 23.81 18.07 18.07 0 0 0 1.78 2.79c6.61 10.32 13.11 20.82 19.83 31.14a17.31 17.31 0 0 0 23.81 5.69l0.54-0.35q17.58-11.07 35.06-22.33a6.06 6.06 0 0 1 6.92-0.8 5.88 5.88 0 0 1 2.35 6.55z m-259.23 75.46a17.57 17.57 0 0 0-15.12-5.61A72.46 72.46 0 0 1 337 420v-0.4c-0.23-1.69-0.36-3.38-0.53-5.16a5.27 5.27 0 0 1 2.59-5.34 5.55 5.55 0 0 1 6.15 0.45c11.08 7.11 22.24 14.14 33.32 21.26a20 20 0 0 0 15.05 4.09 17.65 17.65 0 0 0 12-8.09c6.84-10.86 13.75-21.63 20.47-32.48a17.24 17.24 0 0 0-5-23.87c-0.27-0.18-0.55-0.35-0.83-0.51-9.94-6.32-19.84-12.55-29.79-18.86a5.44 5.44 0 0 1 1.53-10.24 70.58 70.58 0 0 1 57.84 9.7 72.14 72.14 0 0 1 33.52 62.38A18.44 18.44 0 0 0 490 427.7c4.19 3.56 8.14 7.47 12.45 11.48q-31.85 30.24-63.06 60c-3-2.85-5.91-5.16-8.45-7.92z m191.08 59.08A60.53 60.53 0 0 0 644 563.11a61 61 0 0 1 42.93 49.83 62.57 62.57 0 0 1-121.78 27.67A61.23 61.23 0 0 0 549 614c-8.73-8.9-17.26-17.89-25.86-26.87a5.63 5.63 0 0 1-0.57-0.72c22.13-22.15 44.18-44.31 66.43-66.64 11.08 10.32 22 20.46 32.9 30.52z m-28.29 78.48a4.66 4.66 0 0 1 3.73 5.45 4.71 4.71 0 0 1-1.06 2.2 4.57 4.57 0 0 0-0.64 3.56 6.46 6.46 0 0 0 3.12 1.69c4.19-0.89 6.09 1.42 5.48 5.52a4.52 4.52 0 0 0 1 3.47 4.18 4.18 0 0 0 3.19-0.36 4.7 4.7 0 0 1 6.58 0.92 4.63 4.63 0 0 1 0.89 2.1 5.09 5.09 0 0 0 2.38 3 4.26 4.26 0 0 0 2.79-1.69 4.62 4.62 0 0 1 6.28-1.79 4.53 4.53 0 0 1 1.79 1.79 4.38 4.38 0 0 0 2.87 1.69 4.06 4.06 0 0 0 2.31-2.76 4.63 4.63 0 0 1 7.48-3.29 4.3 4.3 0 0 0 3.22 0.36 4.49 4.49 0 0 0 1-3.39 4.7 4.7 0 0 1 5.48-6.22 3.93 3.93 0 0 0 3.06-1.07 4.52 4.52 0 0 0-0.61-3.56 4.7 4.7 0 0 1 2.67-7.65 3.78 3.78 0 0 0 2.42-2.23 4.07 4.07 0 0 0-1.9-2.93 4.65 4.65 0 0 1-0.75-8.19 3.82 3.82 0 0 0 1.33-2.94 4.51 4.51 0 0 0-3-2 4.6 4.6 0 0 1-4-7.12 3.83 3.83 0 0 0-0.14-3.29 4 4 0 0 0-3.49-0.54 4.68 4.68 0 0 1-6.61-4.62 4.15 4.15 0 0 0-1.44-3.21 4.06 4.06 0 0 0-3.43 1 4.64 4.64 0 0 1-8-2 2.58 2.58 0 0 0-5.08 0 4.67 4.67 0 0 1-8 1.87 4.29 4.29 0 0 0-3.4-0.89 4.47 4.47 0 0 0-1.44 3.29 4.54 4.54 0 0 1-4.12 4.93 4.47 4.47 0 0 1-2.28-0.39 4.65 4.65 0 0 0-3.56 0.36 4.23 4.23 0 0 0-0.21 3.56 4.62 4.62 0 0 1-3.9 7 4.25 4.25 0 0 0-3 1.87 4.1 4.1 0 0 0 1.34 3.2 4.59 4.59 0 0 1 1.13 6.39 4.71 4.71 0 0 1-2 1.62 4.47 4.47 0 0 0-1.91 3 3.7 3.7 0 0 0 2.4 2.23z" fill="#FFFFFF" p-id="1647"></path></svg>
eims-ui-mobile/src/static/tabbar/scan.png
eims-ui-mobile/src/store/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate' // æ•°æ®æŒä¹…化
const store = createPinia()
store.use(
  createPersistedState({
    storage: {
      getItem: uni.getStorageSync,
      setItem: uni.setStorageSync,
    },
  }),
)
export default store
// æ¨¡å—统一导出
export * from './user'
eims-ui-mobile/src/store/user.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
const initState = { nickname: '', avatar: '' }
export const useUserStore = defineStore(
  'user',
  () => {
    const userInfo = ref<IUserInfo>({ ...initState })
    const setUserInfo = (val: IUserInfo) => {
      userInfo.value = val
    }
    const clearUserInfo = () => {
      userInfo.value = { ...initState }
    }
    // ä¸€èˆ¬æ²¡æœ‰reset需求,不需要的可以删除
    const reset = () => {
      userInfo.value = { ...initState }
    }
    const isLogined = computed(() => !!userInfo.value.token)
    return {
      userInfo,
      setUserInfo,
      clearUserInfo,
      isLogined,
      reset,
    }
  },
  {
    persist: true,
  },
)
eims-ui-mobile/src/style/iconfont.css
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
@font-face {
  font-family: 'iconfont'; /* Project id 4543091 */
  src:
    url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAOwAAsAAAAAB9AAAANjAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDHAqDBIJqATYCJAMQCwoABCAFhGcHPRvnBsgusG3kMyE15/44PsBX09waBHv0REDt97oHAQDFrOIyPirRiULQ+TJcXV0hCYTuVFcBC915/2vX/32Q80hkZ5PZGZ9snvwruVLloidKqYN6iKC53bOtbKwVLSIi3W6zCWZbs3VbER3j9JpGX3ySYcc94IQRTK5s4epS/jSqIgvg37qlY2/jwQN7D9ADpfRCmIknQByTscVZPTBr+hnnCKg2o4bjakvXEPjuY65DJGeJNtBUhn1JxOBuB2UZmUpBOXdsFp4oxOv4GHgs3h/+wRDcicqSZJG1q9kK1z/Af9NpqxjpC2QaAdpHlCFh4spcYXs5sMWpSk5wUj31G2dLQKVKkZ/w7f/8/i/A3JVUSZK9f7xIKJeU14IFpBI/Qfkkz46GT/CuaGREfCtKJUougWeQWHvVC5Lcz2BGS+SePR99vj3yjJx7h574tp7uWcOh4yfaTjS/245TT/vkQrN+a7RLkK8+Vd+bz+FSGh+9srDQKPeJ2s29z7ah4+efdoxefRbbGwfy7ht+SuIWukzsu1b6ePP+6kN1aamb47qsPim1Ia3xdEpDcl1dckPKGYnneI23+57r2W1Mmkqs6ajrChRCs5qyQ66rTVWhgZaG7toOeHm5cxn0sSQuNDEgcUTdNTSupKI1JRZih/JssAUKezPeOJJzbNozF6zWJuuVavVU5Tgtkop/SDzHa7ytvnCTq0PhkEfi4xLLtb0PuwyOAYqmrYQApFJyoJjTnfz+ve94vvv2f/yWgxl8Jd8Di2DRDPuob59mU/+VfDCROQyR8xSnmP9fXm7liagmN39OlmbvjqG0sMsJKrU0EFXogaRSH5bNY1CmxhyUq7QC1cY1T67RwuQk5CoM2RUQNLoEUb03kDS6h2XzcyjT7iOUa/QXqq1Hn6/GUBAaGcGcWJFlGUmCoVOp8kLvABHnVczGYiOE2SVEUH5OXj/TSnTCDjHAviAWcE4RZYaGWszNiKoayGSGTASeY+PcrMjNpVMvyREMDRoxBMYRVojFMkQiMOhohubdzxtAiOapMMbERpKMnQT9SL4ceQysVdJZVa9kEbsFogIcRyEUE2kN0mL7CDVIGhBzupWMEHA5bDvipgq5hKJcKef8ivbx1kC15KgcYkghhzLxYNntxoKCReJ82jAHAAA=')
      format('woff2'),
    url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.woff?t=1715485842402') format('woff'),
    url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.ttf?t=1715485842402') format('truetype');
}
.iconfont {
  font-family: 'iconfont' !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.icon-my:before {
  content: '\e78c';
}
.icon-package:before {
  content: '\e9c2';
}
.icon-chat:before {
  content: '\e600';
}
eims-ui-mobile/src/style/index.scss
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
@import './iconfont.css';
.test {
  // å¯ä»¥é€šè¿‡ @apply å¤šä¸ªæ ·å¼å°è£…整体样式
  @apply mt-4 ml-4;
  padding-top: 4px;
  color: red;
}
:root,
page {
  // ä¿®æ”¹æŒ‰ä¸»é¢˜è‰²
  // --wot-color-theme: #37c2bc;
  // ä¿®æ”¹æŒ‰é’®èƒŒæ™¯è‰²
  // --wot-button-primary-bg-color: green;
}
eims-ui-mobile/src/types/auto-import.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,187 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
  const EffectScope: typeof import('vue')['EffectScope']
  const computed: typeof import('vue')['computed']
  const createApp: typeof import('vue')['createApp']
  const customRef: typeof import('vue')['customRef']
  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
  const defineComponent: typeof import('vue')['defineComponent']
  const effectScope: typeof import('vue')['effectScope']
  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
  const getCurrentScope: typeof import('vue')['getCurrentScope']
  const h: typeof import('vue')['h']
  const inject: typeof import('vue')['inject']
  const isProxy: typeof import('vue')['isProxy']
  const isReactive: typeof import('vue')['isReactive']
  const isReadonly: typeof import('vue')['isReadonly']
  const isRef: typeof import('vue')['isRef']
  const markRaw: typeof import('vue')['markRaw']
  const nextTick: typeof import('vue')['nextTick']
  const onActivated: typeof import('vue')['onActivated']
  const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
  const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
  const onBeforeMount: typeof import('vue')['onBeforeMount']
  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
  const onDeactivated: typeof import('vue')['onDeactivated']
  const onError: typeof import('@dcloudio/uni-app')['onError']
  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
  const onHide: typeof import('@dcloudio/uni-app')['onHide']
  const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
  const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
  const onMounted: typeof import('vue')['onMounted']
  const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
  const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
  const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
  const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
  const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
  const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
  const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
  const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
  const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
  const onReady: typeof import('@dcloudio/uni-app')['onReady']
  const onRenderTracked: typeof import('vue')['onRenderTracked']
  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
  const onResize: typeof import('@dcloudio/uni-app')['onResize']
  const onScopeDispose: typeof import('vue')['onScopeDispose']
  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
  const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
  const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
  const onShow: typeof import('@dcloudio/uni-app')['onShow']
  const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
  const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
  const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
  const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
  const onUnmounted: typeof import('vue')['onUnmounted']
  const onUpdated: typeof import('vue')['onUpdated']
  const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
  const provide: typeof import('vue')['provide']
  const reactive: typeof import('vue')['reactive']
  const readonly: typeof import('vue')['readonly']
  const ref: typeof import('vue')['ref']
  const resolveComponent: typeof import('vue')['resolveComponent']
  const shallowReactive: typeof import('vue')['shallowReactive']
  const shallowReadonly: typeof import('vue')['shallowReadonly']
  const shallowRef: typeof import('vue')['shallowRef']
  const toRaw: typeof import('vue')['toRaw']
  const toRef: typeof import('vue')['toRef']
  const toRefs: typeof import('vue')['toRefs']
  const toValue: typeof import('vue')['toValue']
  const triggerRef: typeof import('vue')['triggerRef']
  const unref: typeof import('vue')['unref']
  const useAttrs: typeof import('vue')['useAttrs']
  const useCssModule: typeof import('vue')['useCssModule']
  const useCssVars: typeof import('vue')['useCssVars']
  const useId: typeof import('vue')['useId']
  const useModel: typeof import('vue')['useModel']
  const useNavbarWeixin: (typeof import('../hooks/useNavbarWeixin'))['default']
  const useRequest: typeof import('../hooks/useRequest')['default']
  const useSlots: typeof import('vue')['useSlots']
  const useTemplateRef: typeof import('vue')['useTemplateRef']
  const useUpload: typeof import('../hooks/useUpload')['default']
  const useUpload2: typeof import('../hooks/useUpload2')['default']
  const watch: typeof import('vue')['watch']
  const watchEffect: typeof import('vue')['watchEffect']
  const watchPostEffect: typeof import('vue')['watchPostEffect']
  const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
  // @ts-ignore
  export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
  import('vue')
}
// for vue template auto import
import { UnwrapRef } from 'vue'
declare module 'vue' {
  interface GlobalComponents {}
  interface ComponentCustomProperties {
    readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
    readonly computed: UnwrapRef<typeof import('vue')['computed']>
    readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
    readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
    readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
    readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
    readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
    readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
    readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
    readonly h: UnwrapRef<typeof import('vue')['h']>
    readonly inject: UnwrapRef<typeof import('vue')['inject']>
    readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
    readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
    readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
    readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
    readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
    readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
    readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
    readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
    readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']>
    readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
    readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
    readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
    readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
    readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']>
    readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
    readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']>
    readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']>
    readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']>
    readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
    readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']>
    readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']>
    readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']>
    readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']>
    readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']>
    readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']>
    readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']>
    readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']>
    readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']>
    readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']>
    readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
    readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
    readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']>
    readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
    readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
    readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']>
    readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']>
    readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']>
    readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']>
    readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']>
    readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']>
    readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
    readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
    readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
    readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
    readonly provide: UnwrapRef<typeof import('vue')['provide']>
    readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
    readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
    readonly ref: UnwrapRef<typeof import('vue')['ref']>
    readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
    readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
    readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
    readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
    readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
    readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
    readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
    readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
    readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
    readonly unref: UnwrapRef<typeof import('vue')['unref']>
    readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
    readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
    readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
    readonly useId: UnwrapRef<typeof import('vue')['useId']>
    readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
    readonly useRequest: UnwrapRef<typeof import('../hooks/useRequest')['default']>
    readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
    readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
    readonly useUpload: UnwrapRef<typeof import('../hooks/useUpload')['default']>
    readonly watch: UnwrapRef<typeof import('vue')['watch']>
    readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
    readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
    readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
  }
}
eims-ui-mobile/src/types/global.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
declare const __UNI_PLATFORM__:
  | 'h5'
  | 'app'
  | 'mp-alipay'
  | 'mp-baidu'
  | 'mp-jd'
  | 'mp-kuaishou'
  | 'mp-lark'
  | 'mp-qq'
  | 'mp-toutiao'
  | 'mp-weixin'
  | 'quickapp-webview'
  | 'quickapp-webview-huawei'
  | 'quickapp-webview-union'
declare const __VITE_APP_PROXY__: 'true' | 'false'
declare namespace JSX {
  interface IntrinsicElements {
    template: any
    block: any
  }
}
eims-ui-mobile/src/types/shims-uni.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
/// <reference types='@dcloudio/types' />
import 'vue'
declare module '@vue/runtime-core' {
  type Hooks = App.AppInstance & Page.PageInstance
  interface ComponentCustomOptions extends Hooks {}
}
eims-ui-mobile/src/types/uni-pages.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-pages
interface NavigateToOptions {
  url: "/pages/home/index" |
       "/pages/equ/index" |
       "/pages/login/index" |
       "/pages/my/index" |
       "/pages/spare/index";
}
interface RedirectToOptions extends NavigateToOptions {}
interface SwitchTabOptions {
  url: "/pages/home/index" | "/pages/equ/index" | "/pages/equ/index" | "/pages/spare/index" | "/pages/my/index"
}
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
declare interface Uni {
  navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
  redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
  switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
  reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
}
eims-ui-mobile/src/typings.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
// å…¨å±€è¦ç”¨çš„类型放到这里
declare global {
  type IResData<T> = {
    code: number
    msg: string
    data: T
  }
  // uni.uploadFile文件上传参数
  type IUniUploadFileOptions = {
    file?: File
    files?: UniApp.UploadFileOptionFiles[]
    filePath?: string
    name?: string
    formData?: any
  }
  type IUserInfo = {
    nickname?: string
    avatar?: string
    /** å¾®ä¿¡çš„ openid,非微信没有这个字段 */
    openid?: string
    token?: string
  }
}
export {} // é˜²æ­¢æ¨¡å—污染
eims-ui-mobile/src/typings.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
// æžšä¸¾å®šä¹‰
export enum TestEnum {
  A = '1',
  B = '2',
}
eims-ui-mobile/src/uni.scss
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
/* stylelint-disable comment-empty-line-before */
/**
 * è¿™é‡Œæ˜¯uni-app内置的常用样式变量
 *
 * uni-app å®˜æ–¹æ‰©å±•插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
 * å¦‚果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import è¿™ä¸ªæ–‡ä»¶ï¼‰ï¼Œæ–¹ä¾¿ç”¨æˆ·é€šè¿‡æ­ç§¯æœ¨çš„æ–¹å¼å¼€å‘整体风格一致的App
 *
 */
/**
 * å¦‚果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
 *
 * å¦‚果你的项目同样使用了scss预处理,你也可以直接在你的 scss ä»£ç ä¸­ä½¿ç”¨å¦‚下变量,同时无需 import è¿™ä¸ªæ–‡ä»¶
 */
/* é¢œè‰²å˜é‡ */
/* è¡Œä¸ºç›¸å…³é¢œè‰² */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* æ–‡å­—基本颜色 */
$uni-text-color: #333; // åŸºæœ¬è‰²
$uni-text-color-inverse: #fff; // åè‰²
$uni-text-color-grey: #999; // è¾…助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* èƒŒæ™¯é¢œè‰² */
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; // ç‚¹å‡»çŠ¶æ€é¢œè‰²
$uni-bg-color-mask: rgb(0 0 0 / 40%); // é®ç½©é¢œè‰²
/* è¾¹æ¡†é¢œè‰² */
$uni-border-color: #c8c7cc;
/* å°ºå¯¸å˜é‡ */
/* æ–‡å­—尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* å›¾ç‰‡å°ºå¯¸ */
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* æ°´å¹³é—´è· */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* åž‚直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* é€æ˜Žåº¦ */
$uni-opacity-disabled: 0.3; // ç»„件禁用态的透明度
/* æ–‡ç« åœºæ™¯ç›¸å…³ */
$uni-color-title: #2c405a; // æ–‡ç« æ ‡é¢˜é¢œè‰²
$uni-font-size-title: 20px;
$uni-color-subtitle: #555; // äºŒçº§æ ‡é¢˜é¢œè‰²
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // æ–‡ç« æ®µè½é¢œè‰²
$uni-font-size-paragraph: 15px;
eims-ui-mobile/src/uni_modules/.gitkeep
eims-ui-mobile/src/utils/http.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,80 @@
import { CustomRequestOptions } from '@/interceptors/request'
export const http = <T>(options: CustomRequestOptions) => {
  // 1. è¿”回 Promise å¯¹è±¡
  return new Promise<IResData<T>>((resolve, reject) => {
    uni.request({
      ...options,
      dataType: 'json',
      // #ifndef MP-WEIXIN
      responseType: 'json',
      // #endif
      // å“åº”成功
      success(res) {
        // çŠ¶æ€ç  2xx,参考 axios çš„设计
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 2.1 æå–核心数据 res.data
          resolve(res.data as IResData<T>)
        } else if (res.statusCode === 401) {
          // 401错误  -> æ¸…理用户信息,跳转到登录页
          // userStore.clearUserInfo()
          // uni.navigateTo({ url: '/pages/login/login' })
          reject(res)
        } else {
          // å…¶ä»–错误 -> æ ¹æ®åŽç«¯é”™è¯¯ä¿¡æ¯è½»æç¤º
          !options.hideErrorToast &&
            uni.showToast({
              icon: 'none',
              title: (res.data as IResData<T>).msg || '请求错误',
            })
          reject(res)
        }
      },
      // å“åº”失败
      fail(err) {
        uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试',
        })
        reject(err)
      },
    })
  })
}
/**
 * GET è¯·æ±‚
 * @param url åŽå°åœ°å€
 * @param query è¯·æ±‚query参数
 * @returns
 */
export const httpGet = <T>(url: string, query?: Record<string, any>) => {
  return http<T>({
    url,
    query,
    method: 'GET',
  })
}
/**
 * POST è¯·æ±‚
 * @param url åŽå°åœ°å€
 * @param data è¯·æ±‚body参数
 * @param query è¯·æ±‚query参数,post请求也支持query,很多微信接口都需要
 * @returns
 */
export const httpPost = <T>(
  url: string,
  data?: Record<string, any>,
  query?: Record<string, any>,
) => {
  return http<T>({
    url,
    query,
    data,
    method: 'POST',
  })
}
http.get = httpGet
http.post = httpPost
eims-ui-mobile/src/utils/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,178 @@
import { pages, subPackages, tabBar } from '@/pages.json'
import { isMpWeixin } from './platform'
const getLastPage = () => {
  // getCurrentPages() è‡³å°‘有1个元素,所以不再额外判断
  // const lastPage = getCurrentPages().at(-1)
  // ä¸Šé¢é‚£ä¸ªåœ¨ä½Žç‰ˆæœ¬å®‰å“中打包会报错,所以改用下面这个【虽然我加了 src/interceptions/prototype.ts,但依然报错】
  const pages = getCurrentPages()
  return pages[pages.length - 1]
}
/** åˆ¤æ–­å½“前页面是否是 tabbar é¡µ  */
export const getIsTabbar = () => {
  if (!tabBar) {
    return false
  }
  if (!tabBar.list.length) {
    // é€šå¸¸æœ‰ tabBar çš„话,list ä¸èƒ½æœ‰ç©ºï¼Œä¸”至少有2个元素,这里其实不用处理
    return false
  }
  const lastPage = getLastPage()
  const currPath = lastPage.route
  return !!tabBar.list.find((e) => e.pagePath === currPath)
}
/**
 * èŽ·å–å½“å‰é¡µé¢è·¯ç”±çš„ path è·¯å¾„å’Œ redirectPath è·¯å¾„
 * path å¦‚ '/pages/login/index'
 * redirectPath å¦‚ '/pages/demo/base/route-interceptor'
 */
export const currRoute = () => {
  const lastPage = getLastPage()
  const currRoute = (lastPage as any).$page
  // console.log('lastPage.$page:', currRoute)
  // console.log('lastPage.$page.fullpath:', currRoute.fullPath)
  // console.log('lastPage.$page.options:', currRoute.options)
  // console.log('lastPage.options:', (lastPage as any).options)
  // ç»è¿‡å¤šç«¯æµ‹è¯•,只有 fullPath é è°±ï¼Œå…¶ä»–都不靠谱
  const { fullPath } = currRoute as { fullPath: string }
  // console.log(fullPath)
  // eg: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor (小程序)
  // eg: /pages/login/index?redirect=%2Fpages%2Froute-interceptor%2Findex%3Fname%3Dfeige%26age%3D30(h5)
  return getUrlObj(fullPath)
}
const ensureDecodeURIComponent = (url: string) => {
  if (url.startsWith('%')) {
    return ensureDecodeURIComponent(decodeURIComponent(url))
  }
  return url
}
/**
 * è§£æž url å¾—到 path å’Œ query
 * æ¯”如输入url: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
 * è¾“出: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
 */
export const getUrlObj = (url: string) => {
  const [path, queryStr] = url.split('?')
  // console.log(path, queryStr)
  if (!queryStr) {
    return {
      path,
      query: {},
    }
  }
  const query: Record<string, string> = {}
  queryStr.split('&').forEach((item) => {
    const [key, value] = item.split('=')
    // console.log(key, value)
    query[key] = ensureDecodeURIComponent(value) // è¿™é‡Œéœ€è¦ç»Ÿä¸€ decodeURIComponent ä¸€ä¸‹ï¼Œå¯ä»¥å…¼å®¹h5和微信y
  })
  return { path, query }
}
/**
 * å¾—到所有的需要登录的 pages,包括主包和分包的
 * è¿™é‡Œè®¾è®¡å¾—通用一点,可以传递 key ä½œä¸ºåˆ¤æ–­ä¾æ®ï¼Œé»˜è®¤æ˜¯ needLogin, ä¸Ž route-block é…å¯¹ä½¿ç”¨
 * å¦‚果没有传 key,则表示所有的 pages,如果传递了 key, åˆ™è¡¨ç¤ºé€šè¿‡ key è¿‡æ»¤
 */
export const getAllPages = (key = 'needLogin') => {
  // è¿™é‡Œå¤„理主包
  const mainPages = [
    ...pages
      .filter((page) => !key || page[key])
      .map((page) => ({
        ...page,
        path: `/${page.path}`,
      })),
  ]
  // è¿™é‡Œå¤„理分包
  const subPages: any[] = []
  subPackages.forEach((subPageObj) => {
    // console.log(subPageObj)
    const { root } = subPageObj
    subPageObj.pages
      .filter((page) => !key || page[key])
      .forEach((page: { path: string } & Record<string, any>) => {
        subPages.push({
          ...page,
          path: `/${root}/${page.path}`,
        })
      })
  })
  const result = [...mainPages, ...subPages]
  // console.log(`getAllPages by ${key} result: `, result)
  return result
}
/**
 * å¾—到所有的需要登录的 pages,包括主包和分包的
 * åªå¾—到 path æ•°ç»„
 */
export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map((page) => page.path)
/**
 * å¾—到所有的需要登录的 pages,包括主包和分包的
 * åªå¾—到 path æ•°ç»„
 */
export const needLoginPages: string[] = getAllPages('needLogin').map((page) => page.path)
/**
 * æ ¹æ®å¾®ä¿¡å°ç¨‹åºå½“前环境,判断应该获取的 baseUrl
 */
export const getEnvBaseUrl = () => {
  // è¯·æ±‚基准地址
  let baseUrl = import.meta.env.VITE_SERVER_BASEURL
  // å¾®ä¿¡å°ç¨‹åºç«¯çŽ¯å¢ƒåŒºåˆ†
  if (isMpWeixin) {
    const {
      miniProgram: { envVersion },
    } = uni.getAccountInfoSync()
    switch (envVersion) {
      case 'develop':
        baseUrl = import.meta.env.VITE_SERVER_BASEURL__WEIXIN_DEVELOP || baseUrl
        break
      case 'trial':
        baseUrl = import.meta.env.VITE_SERVER_BASEURL__WEIXIN_TRIAL || baseUrl
        break
      case 'release':
        baseUrl = import.meta.env.VITE_SERVER_BASEURL__WEIXIN_RELEASE || baseUrl
        break
    }
  }
  return baseUrl
}
/**
 * æ ¹æ®å¾®ä¿¡å°ç¨‹åºå½“前环境,判断应该获取的 UPLOAD_BASEURL
 */
export const getEnvBaseUploadUrl = () => {
  // è¯·æ±‚基准地址
  let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL
  // å¾®ä¿¡å°ç¨‹åºç«¯çŽ¯å¢ƒåŒºåˆ†
  if (isMpWeixin) {
    const {
      miniProgram: { envVersion },
    } = uni.getAccountInfoSync()
    switch (envVersion) {
      case 'develop':
        baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP || baseUploadUrl
        break
      case 'trial':
        baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL__WEIXIN_TRIAL || baseUploadUrl
        break
      case 'release':
        baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL__WEIXIN_RELEASE || baseUploadUrl
        break
    }
  }
  return baseUploadUrl
}
eims-ui-mobile/src/utils/platform.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
/*
 * @Author: è²é¸½
 * @Date: 2024-03-28 19:13:55
 * @Last Modified by: è²é¸½
 * @Last Modified time: 2024-03-28 19:24:55
 */
export const platform = __UNI_PLATFORM__
export const isH5 = __UNI_PLATFORM__ === 'h5'
export const isApp = __UNI_PLATFORM__ === 'app'
export const isMp = __UNI_PLATFORM__.startsWith('mp-')
export const isMpWeixin = __UNI_PLATFORM__.startsWith('mp-weixin')
export const isMpAplipay = __UNI_PLATFORM__.startsWith('mp-alipay')
export const isMpToutiao = __UNI_PLATFORM__.startsWith('mp-toutiao')
const PLATFORM = {
  platform,
  isH5,
  isApp,
  isMp,
  isMpWeixin,
  isMpAplipay,
  isMpToutiao,
}
export default PLATFORM
eims-ui-mobile/src/utils/request.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
import { CustomRequestOptions } from '@/interceptors/request'
/**
 * è¯·æ±‚方法: ä¸»è¦æ˜¯å¯¹ uni.request çš„封装,去适配 openapi-ts-request çš„ request æ–¹æ³•
 * @param options è¯·æ±‚参数
 * @returns è¿”回 Promise å¯¹è±¡
 */
const http = <T>(options: CustomRequestOptions) => {
  // 1. è¿”回 Promise å¯¹è±¡
  return new Promise<T>((resolve, reject) => {
    uni.request({
      ...options,
      dataType: 'json',
      // #ifndef MP-WEIXIN
      responseType: 'json',
      // #endif
      // å“åº”成功
      success(res) {
        // çŠ¶æ€ç  2xx,参考 axios çš„设计
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 2.1 æå–核心数据 res.data
          resolve(res.data as T)
        } else if (res.statusCode === 401) {
          // 401错误  -> æ¸…理用户信息,跳转到登录页
          // userStore.clearUserInfo()
          // uni.navigateTo({ url: '/pages/login/login' })
          reject(res)
        } else {
          // å…¶ä»–错误 -> æ ¹æ®åŽç«¯é”™è¯¯ä¿¡æ¯è½»æç¤º
          !options.hideErrorToast &&
            uni.showToast({
              icon: 'none',
              title: (res.data as T & { msg?: string })?.msg || '请求错误',
            })
          reject(res)
        }
      },
      // å“åº”失败
      fail(err) {
        uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试',
        })
        reject(err)
      },
    })
  })
}
/*
 * openapi-ts-request å·¥å…·çš„ request è·¨å®¢æˆ·ç«¯é€‚配方法
 */
export default function request<T = unknown>(
  url: string,
  options: Omit<CustomRequestOptions, 'url'> & {
    params?: Record<string, unknown>
    headers?: Record<string, unknown>
  },
) {
  const requestOptions = {
    url,
    ...options,
  }
  if (options.params) {
    requestOptions.query = requestOptions.params
    delete requestOptions.params
  }
  if (options.headers) {
    requestOptions.header = options.headers
    delete requestOptions.headers
  }
  return http<T>(requestOptions)
}
eims-ui-mobile/tsconfig.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "noImplicitThis": true,
    "allowSyntheticDefaultImports": true,
    "allowJs": true,
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "outDir": "dist",
    "lib": ["esnext", "dom"],
    "types": [
      "@dcloudio/types",
      "@uni-helper/uni-types",
      "@types/wechat-miniprogram",
      "wot-design-uni/global.d.ts",
      "z-paging/types",
      "./src/typings.d.ts"
    ]
  },
  "vueCompilerOptions": {
    "plugins": ["@uni-helper/uni-types/volar-plugin"]
  },
  "exclude": ["node_modules"],
  "include": [
    "src/**/*.ts",
    "src/**/*.js",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.jsx",
    "src/**/*.vue",
    "src/**/*.json"
  ]
}
eims-ui-mobile/uno.config.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
// uno.config.ts
import {
  type Preset,
  defineConfig,
  presetUno,
  presetAttributify,
  presetIcons,
  transformerDirectives,
  transformerVariantGroup,
} from 'unocss'
import { presetApplet, presetRemRpx, transformerAttributify } from 'unocss-applet'
// @see https://unocss.dev/presets/legacy-compat
// import { presetLegacyCompat } from '@unocss/preset-legacy-compat'
const isMp = process.env?.UNI_PLATFORM?.startsWith('mp') ?? false
const presets: Preset[] = []
if (isMp) {
  // ä½¿ç”¨å°ç¨‹åºé¢„设
  presets.push(presetApplet(), presetRemRpx())
} else {
  presets.push(
    // éžå°ç¨‹åºç”¨å®˜æ–¹é¢„设
    presetUno(),
    // æ”¯æŒcss class属性化
    presetAttributify(),
  )
}
export default defineConfig({
  presets: [
    ...presets,
    // æ”¯æŒå›¾æ ‡ï¼Œéœ€è¦æ­é…å›¾æ ‡åº“,eg: @iconify-json/carbon, ä½¿ç”¨ `<button class="i-carbon-sun dark:i-carbon-moon" />`
    presetIcons({
      scale: 1.2,
      warn: true,
      extraProperties: {
        display: 'inline-block',
        'vertical-align': 'middle',
      },
    }),
    // å°†é¢œè‰²å‡½æ•° (rgb()和hsl()) ä»Žç©ºæ ¼åˆ†éš”转换为逗号分隔,更好的兼容性app端,example:
    // `rgb(255 0 0)` -> `rgb(255, 0, 0)`
    // `rgba(255 0 0 / 0.5)` -> `rgba(255, 0, 0, 0.5)`
    // ä¸Žç¾¤å‹çš„æ­£å¸¸å†™æ³•冲突,先去掉!(2024-05-25)
    // presetLegacyCompat({
    //   commaStyleColorFunction: true,
    // }) as Preset,
  ],
  /**
   * è‡ªå®šä¹‰å¿«æ·è¯­å¥
   * @see https://github.com/unocss/unocss#shortcuts
   */
  shortcuts: [['center', 'flex justify-center items-center']],
  transformers: [
    // å¯ç”¨ @apply åŠŸèƒ½
    transformerDirectives(),
    // å¯ç”¨ () åˆ†ç»„功能
    // æ”¯æŒcss class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
    transformerVariantGroup(),
    // Don't change the following order
    transformerAttributify({
      // è§£å†³ä¸Žç¬¬ä¸‰æ–¹æ¡†æž¶æ ·å¼å†²çªé—®é¢˜
      prefixedOnly: true,
      prefix: 'fg',
    }),
  ],
  rules: [
    [
      'p-safe',
      {
        padding:
          'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
      },
    ],
    ['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
    ['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
  ],
})
/**
 * æœ€ç»ˆè¿™ä¸€å¥—组合下来会得到:
 * mp é‡Œé¢ï¼šmt-4 => margin-top: 32rpx  == 16px
 * h5 é‡Œé¢ï¼šmt-4 => margin-top: 1rem == 16px
 *
 * å¦‚果是传统方式写样式,则推荐设计稿设置为 750,这样设计稿1px,代码写1rpx。
 * rpx是响应式的,可以让不同设备的屏幕显示效果保持一致。
 */
eims-ui-mobile/vite-plugins/copyNativeRes.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
import fs from 'fs-extra'
import path from 'path'
export function copyNativeRes() {
  const waitPath = path.resolve(__dirname, '../src/nativeResources')
  const buildPath = path.resolve(
    __dirname,
    '../dist',
    process.env.NODE_ENV === 'production' ? 'build' : 'dev',
    process.env.UNI_PLATFORM!,
    'nativeResources',
  )
  return {
    enforce: 'post',
    async writeBundle() {
      try {
        // æ£€æŸ¥æºç›®å½•是否存在
        const sourceExists = await fs.pathExists(waitPath)
        if (!sourceExists) {
          console.warn(`[copyNativeRes] è­¦å‘Šï¼šæºç›®å½• "${waitPath}" ä¸å­˜åœ¨ï¼Œè·³è¿‡å¤åˆ¶æ“ä½œã€‚`)
          return
        }
        // ç¡®ä¿ç›®æ ‡ç›®å½•及中间目录存在
        await fs.ensureDir(buildPath)
        console.log(`[copyNativeRes] ç¡®ä¿ç›®æ ‡ç›®å½•存在:${buildPath}`)
        // æ‰§è¡Œæ–‡ä»¶å¤¹å¤åˆ¶
        await fs.copy(waitPath, buildPath)
        console.log(
          `[copyNativeRes] æˆåŠŸå°† nativeResources ç›®å½•中的资源移动到构建目录:${buildPath}`,
        )
      } catch (error) {
        console.error(`[copyNativeRes] å¤åˆ¶èµ„源失败:`, error)
      }
    },
  }
}
eims-ui-mobile/vite.config.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
import Uni from '@dcloudio/vite-plugin-uni'
import dayjs from 'dayjs'
import path from 'node:path'
import { defineConfig, loadEnv } from 'vite'
// @see https://uni-helper.js.org/vite-plugin-uni-pages
import UniPages from '@uni-helper/vite-plugin-uni-pages'
// @see https://uni-helper.js.org/vite-plugin-uni-layouts
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
// @see https://github.com/uni-helper/vite-plugin-uni-platform
// éœ€è¦ä¸Ž @uni-helper/vite-plugin-uni-pages æ’件一起使用
import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
// @see https://github.com/uni-helper/vite-plugin-uni-manifest
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
// @see https://unocss.dev/
import { visualizer } from 'rollup-plugin-visualizer'
import UnoCSS from 'unocss/vite'
import AutoImport from 'unplugin-auto-import/vite'
import ViteRestart from 'vite-plugin-restart'
import { copyNativeRes } from './vite-plugins/copyNativeRes'
// https://vitejs.dev/config/
export default ({ command, mode }) => {
  // console.log(mode === process.env.NODE_ENV) // true
  // mode: åŒºåˆ†ç”Ÿäº§çŽ¯å¢ƒè¿˜æ˜¯å¼€å‘çŽ¯å¢ƒ
  console.log('command, mode -> ', command, mode)
  // pnpm dev:h5 æ—¶å¾—到 => serve development
  // pnpm build:h5 æ—¶å¾—到 => build production
  // pnpm dev:mp-weixin æ—¶å¾—到 => build development (注意区别,command为build)
  // pnpm build:mp-weixin æ—¶å¾—到 => build production
  // pnpm dev:app æ—¶å¾—到 => build development (注意区别,command为build)
  // pnpm build:app æ—¶å¾—到 => build production
  // dev å’Œ build å‘½ä»¤å¯ä»¥åˆ†åˆ«ä½¿ç”¨ .env.development å’Œ .env.production çš„环境变量
  const { UNI_PLATFORM } = process.env
  console.log('UNI_PLATFORM -> ', UNI_PLATFORM) // å¾—到 mp-weixin, h5, app ç­‰
  const env = loadEnv(mode, path.resolve(process.cwd(), 'env'))
  const {
    VITE_APP_PORT,
    VITE_SERVER_BASEURL,
    VITE_DELETE_CONSOLE,
    VITE_SHOW_SOURCEMAP,
    VITE_APP_PROXY,
    VITE_APP_PROXY_PREFIX,
  } = env
  console.log('环境变量 env -> ', env)
  return defineConfig({
    envDir: './env', // è‡ªå®šä¹‰env目录
    plugins: [
      UniPages({
        exclude: ['**/components/**/**.*'],
        routeBlockLang: 'json5', // è™½ç„¶è®¾äº†é»˜è®¤å€¼ï¼Œä½†æ˜¯vue文件还是要加上 lang="json5", è¿™æ ·æ‰èƒ½å¾ˆå¥½åœ°æ ¼å¼åŒ–
        // homePage é€šè¿‡ vue æ–‡ä»¶çš„ route-block çš„type="home"来设定
        // pages ç›®å½•为 src/pages,分包目录不能配置在pages目录下
        // subPackages: ['src/pages-sub'], // æ˜¯ä¸ªæ•°ç»„,可以配置多个,但是不能为pages里面的目录
        dts: 'src/types/uni-pages.d.ts',
      }),
      UniLayouts(),
      UniPlatform(),
      UniManifest(),
      // UniXXX éœ€è¦åœ¨ Uni ä¹‹å‰å¼•å…¥
      Uni(),
      {
        // ä¸´æ—¶è§£å†³ dcloudio å®˜æ–¹çš„ @dcloudio/uni-mp-compiler å‡ºçŽ°çš„ç¼–è¯‘ BUG
        // å‚考 github issue: https://github.com/dcloudio/uni-app/issues/4952
        // è‡ªå®šä¹‰æ’件禁用 vite:vue æ’ä»¶çš„ devToolsEnabled,强制编译 vue æ¨¡æ¿æ—¶ inline ä¸º true
        name: 'fix-vite-plugin-vue',
        configResolved(config) {
          const plugin = config.plugins.find((p) => p.name === 'vite:vue')
          if (plugin && plugin.api && plugin.api.options) {
            plugin.api.options.devToolsEnabled = false
          }
        },
      },
      UnoCSS(),
      AutoImport({
        imports: ['vue', 'uni-app'],
        dts: 'src/types/auto-import.d.ts',
        dirs: ['src/hooks'], // è‡ªåЍ坼入 hooks
        eslintrc: { enabled: true },
        vueTemplate: true, // default false
      }),
      ViteRestart({
        // é€šè¿‡è¿™ä¸ªæ’件,在修改vite.config.js文件则不需要重新运行也生效配置
        restart: ['vite.config.js'],
      }),
      // h5环境增加 BUILD_TIME å’Œ BUILD_BRANCH
      UNI_PLATFORM === 'h5' && {
        name: 'html-transform',
        transformIndexHtml(html) {
          return html.replace('%BUILD_TIME%', dayjs().format('YYYY-MM-DD HH:mm:ss'))
        },
      },
      // æ‰“包分析插件,h5 + ç”Ÿäº§çŽ¯å¢ƒæ‰å¼¹å‡º
      UNI_PLATFORM === 'h5' &&
        mode === 'production' &&
        visualizer({
          filename: './node_modules/.cache/visualizer/stats.html',
          open: true,
          gzipSize: true,
          brotliSize: true,
        }),
      // åªæœ‰åœ¨ app å¹³å°æ—¶æ‰å¯ç”¨ copyNativeRes æ’ä»¶
      UNI_PLATFORM === 'app' && copyNativeRes(),
    ],
    define: {
      __UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
      __VITE_APP_PROXY__: JSON.stringify(VITE_APP_PROXY),
    },
    css: {
      postcss: {
        plugins: [
          // autoprefixer({
          //   // æŒ‡å®šç›®æ ‡æµè§ˆå™¨
          //   overrideBrowserslist: ['> 1%', 'last 2 versions'],
          // }),
        ],
      },
    },
    resolve: {
      alias: {
        '@': path.join(process.cwd(), './src'),
        '@img': path.join(process.cwd(), './src/static/images'),
      },
    },
    server: {
      host: '0.0.0.0',
      hmr: true,
      port: Number.parseInt(VITE_APP_PORT, 10),
      // ä»… H5 ç«¯ç”Ÿæ•ˆï¼Œå…¶ä»–端不生效(其他端走build,不走devServer)
      proxy: JSON.parse(VITE_APP_PROXY)
        ? {
            [VITE_APP_PROXY_PREFIX]: {
              target: VITE_SERVER_BASEURL,
              changeOrigin: true,
              rewrite: (path) => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
            },
          }
        : undefined,
    },
    build: {
      // æ–¹ä¾¿éžh5端调试
      sourcemap: VITE_SHOW_SOURCEMAP === 'true', // é»˜è®¤æ˜¯false
      target: 'es6',
      // å¼€å‘环境不用压缩
      minify: mode === 'development' ? false : 'terser',
      terserOptions: {
        compress: {
          drop_console: VITE_DELETE_CONSOLE === 'true',
          drop_debugger: true,
        },
      },
    },
  })
}
eims-ui/apps/web-antd/.env.production
@@ -19,7 +19,7 @@
VITE_GLOB_API_URL=/prod-api
# å…¨å±€åР坆开关(即开启了加解密功能才会生效 ä¸æ˜¯å…¨éƒ¨æŽ¥å£åР坆 éœ€è¦å’ŒåŽç«¯å¯¹åº”)
VITE_GLOB_ENABLE_ENCRYPT=true
VITE_GLOB_ENABLE_ENCRYPT=false
# RSA公钥 è¯·æ±‚加密使用 æ³¨æ„è¿™ä¸¤ä¸ªæ˜¯ä¸¤å¯¹RSA公私钥 è¯·æ±‚加密-后端解密是一对 å“åº”解密-后端加密是一对
VITE_GLOB_RSA_PUBLIC_KEY=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
# RSA私钥 å“åº”解密使用 æ³¨æ„è¿™ä¸¤ä¸ªæ˜¯ä¸¤å¯¹RSA公私钥 è¯·æ±‚加密-后端解密是一对 å“åº”解密-后端加密是一对
@@ -28,5 +28,5 @@
VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
# å¼€å¯SSE
VITE_GLOB_SSE_ENABLE=true
VITE_GLOB_SSE_ENABLE=false
eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts
@@ -20,6 +20,11 @@
  chargeUser: number;
  /**
   * ç»åŠžéƒ¨é—¨
   */
  chargeDept: number;
  /**
   * å·¥å•类型(1-入库单  2-出库单) å­—å…¸
   */
  type: string;
@@ -38,4 +43,8 @@
   * å¤‡æ³¨
   */
  remark: string;
  /**
   * å‡ºå…¥åº“选择的备件列表
   */
  spareList: any;
}
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
import type { IDS, PageQuery, PageResult } from '#/api/common';
import type { SpareInoutdtVO } from '#/api/eims/spare-inoutdt/model';
import { commonExport } from '#/api/helper';
import { requestClient } from '#/api/request';
enum Api {
  root = '/eims/spareInoutdt',
  spareInoutdtExport = '/eims/spareInoutdt/export',
  spareInoutdtList = '/eims/spareInoutdt/list'
}
/**
 * æŸ¥è¯¢ã€å¤‡ä»¶å‡ºå…¥åº“明细】列表
 * @param query
 * @returns {*}
 */
export function listSpareInoutdt(params?: PageQuery) {
  return requestClient.get<PageResult<SpareInoutdtVO>>(Api.spareInoutdtList, { params });
}
/**
 * æŸ¥è¯¢ã€å¤‡ä»¶å‡ºå…¥åº“明细】详细
 * @param spareInoutdtId
 */
export function getSpareInoutdt(spareInoutdtId: any) {
  return requestClient.get<SpareInoutdtVO>(`${Api.root}/${spareInoutdtId}`);
}
/**
 * æ–°å¢žã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param data
 */
export function addSpareInoutdt(data: any) {
  return requestClient.postWithMsg<void>(Api.root, data);
}
/**
 * ä¿®æ”¹ã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param data
 */
export function updateSpareInoutdt(data: any) {
  return requestClient.putWithMsg<void>(Api.root, data);
}
/**
 * åˆ é™¤ã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param spareInoutdtIds
 */
export function delSpareInoutdt(spareInoutdtIds: IDS) {
  return requestClient.deleteWithMsg<void>(`${Api.root}/${spareInoutdtIds}`);
}
/**
 * å¯¼å‡ºã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param data
 */
export function spareInoutdtExport(data: any) {
  return commonExport(Api.spareInoutdtExport, data);
}
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
export interface SpareInoutdtVO {
  /**
   *
   */
  id: number | string;
  /**
   * å‡ºåº“单或入库单id
   */
  inoutId: number | string;
  /**
   * å¤‡ä»¶id
   */
  spareId: number | string;
  /**
   * ä¹‹å‰åº“å­˜
   */
  beforeStock: number;
  /**
   * å®žé™…库存
   */
  actualStock: number;
  /**
   * æ•°é‡
   */
  quantity: number;
  /**
   * å•ä»·
   */
  unitPrice: number;
  /**
   * é‡‘额
   */
  amount: number;
  /**
   * å¤‡æ³¨
   */
  remark: string;
}
eims-ui/apps/web-antd/src/api/eims/spare/index.ts
@@ -5,6 +5,7 @@
import { requestClient } from '#/api/request';
enum Api {
  inoutList = '/eims/spare/listInout',
  root = '/eims/spare',
  spareExport = '/eims/spare/export',
  spareList = '/eims/spare/list'
@@ -20,6 +21,10 @@
  return requestClient.get<PageResult<SpareVO>>(Api.spareList, { params });
}
export function listInout(params?: PageQuery) {
  return requestClient.get<PageResult<any>>(Api.inoutList, { params });
}
/**
 * æŸ¥è¯¢ã€å¤‡ä»¶å°è´¦ã€‘详细
 * @param spareId
eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue
@@ -16,6 +16,7 @@
const columns = props?.columns?.filter((i) => i.field !== 'action');
const gridOptions: VxeGridProps = {
  size: 'mini',
  checkboxConfig: {
    // é«˜äº®
    highlight: true,
eims-ui/apps/web-antd/src/views/eims/components/spare-modal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
<script setup lang="ts">
import type { VxeGridProps } from '#/adapter/vxe-table';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { message } from 'ant-design-vue';
import { renderDict } from '#/utils/render';
import InnerView from '#/views/eims/spare/index.vue';
const emit = defineEmits<{ updateSelect: [any] }>();
const [BasicModal, modalApi] = useVbenModal({
  fullscreenButton: false,
  draggable: true,
  onCancel: handleCancel,
  onConfirm: handleConfirm
});
const innerView = ref();
async function handleConfirm() {
  try {
    modalApi.modalLoading(true);
    const tableSelect = innerView.value.tableSelect();
    const eList = tableSelect.filter((item: any) => !item.actualStock && item.actualStock <= 0);
    // æ£€æµ‹é€‰æ‹©çš„备件库存是否正常
    if (eList.length > 0) {
      message.error('存在库存不足备件,请重新选择');
      return false;
    }
    emit('updateSelect', tableSelect);
    await handleCancel();
  } catch (error) {
    console.error(error);
  } finally {
    modalApi.modalLoading(false);
  }
}
async function handleCancel() {
  modalApi.close();
}
</script>
<template>
  <BasicModal :fullscreen-button="true" class="w-[800px]">
    <InnerView ref="innerView" />
  </BasicModal>
</template>
<style scoped></style>
eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx
@@ -118,6 +118,17 @@
    label: '供应商'
  },
  {
    component: 'Input',
    fieldName: 'openSpare',
    label: '选择备件',
    formItemClass: 'col-span-1 w-[80px]'
  },
  {
    component: 'Input',
    fieldName: 'outSpareList',
    label: ''
  },
  {
    component: 'TreeSelect',
    // åœ¨drawer里更新 è¿™é‡Œä¸éœ€è¦é»˜è®¤çš„componentProps
    defaultValue: undefined,
eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import { onMounted } from 'vue';
import { onMounted, ref } from 'vue';
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
@@ -16,6 +16,9 @@
import { columns, querySchema } from './data';
import drawer from './spare-in-drawer.vue';
import { columns as inoutCol } from '#/views/eims/spare-inoutdt/data';
import { listSpareInoutdt } from '#/api/eims/spare-inoutdt';
import BasisSubTable from '#/views/eims/components/basis-sub-table.vue';
const formOptions: VbenFormProps = {
  commonConfig: {
@@ -69,12 +72,16 @@
  },
  id: 'spre-inout-index'
};
const inoutId = ref<string>();
const [BasicTable, tableApi] = useVbenVxeGrid({
  formOptions,
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
    cellClick: (e: any) => {
      const { row } = e;
      inoutId.value = row.id;
    }
  }
});
@@ -190,8 +197,8 @@
<template>
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px]">
      <BasicTable class="flex-1 overflow-hidden" table-title="备件入库单列表">
    <div class="flex h-full gap-[8px] flex-col">
      <BasicTable class="h-2/3" table-title="备件入库单列表">
        <template #toolbar-tools>
          <Space>
            <a-button v-access:code="['eims:spareInout:export']" @click="handleDownloadExcel">
@@ -231,6 +238,14 @@
          </Space>
        </template>
      </BasicTable>
      <BasisSubTable
        :columns="inoutCol"
        :list-api="listSpareInoutdt"
        :req-value="inoutId"
        class="h-1/3"
        req-key="inoutId"
        title="入库明细"
      />
    </div>
    <Drawer @reload="tableApi.query()" />
  </Page>
eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { addFullName, cloneDeep, getPopupContainer } from '@vben/utils';
@@ -11,7 +11,72 @@
import { drawerSchema } from './data';
import CodeInput from '#/views/eims/components/code-input.vue';
import spareModal from '#/views/eims/components/spare-modal.vue';
import SelectSpareTable from '#/views/eims/spare-out/select-spare-table.vue';
import { message } from 'ant-design-vue';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { renderDict } from '#/utils/render';
import { DictEnum } from '@vben/constants';
/**
 * å‡ºåº“单选择的备件数据
 */
const outSpareList = ref([]);
const selectSpareTable = ref();
const outCol: VxeGridProps['columns'] = [
  {
    field: 'action',
    slots: { default: 'action' },
    title: '删除',
    width: 60
  },
  {
    title: '备件名称',
    field: 'name',
    width: 180
  },
  {
    title: '备件编码',
    field: 'code',
    width: 120
  },
  {
    title: '备件型号',
    field: 'modelNo',
    width: 100
  },
  {
    title: '计量单位',
    field: 'unit',
    slots: {
      default: ({ row }) => {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
      }
    },
    width: 80
  },
  {
    title: '实际库存',
    field: 'actualStock',
    width: 100
  },
  {
    title: '数量',
    field: 'quantity',
    editRender: {
      name: 'input'
    },
    width: 80
  },
  {
    title: '参考价',
    field: 'referPrice',
    width: 90
  }
];
const emit = defineEmits<{ reload: [] }>();
const isUpdate = ref(false);
@@ -39,6 +104,7 @@
    if (!isOpen) {
      return null;
    }
    outSpareList.value = [];
    drawerApi.drawerLoading(true);
    const { id } = drawerApi.getData() as { id?: number | string };
    isUpdate.value = !!id;
@@ -48,6 +114,10 @@
    if (isUpdate.value && id) {
      const record = await getSpareInout(id);
      await formApi.setValues(record);
      outSpareList.value = record?.spareList;
      if (isUpdate.value && record.chargeDept) {
        await setupUserOptions(record.chargeDept);
      }
    }
    drawerApi.drawerLoading(false);
@@ -127,7 +197,15 @@
    if (!valid) {
      return;
    }
    const selectSpareList = selectSpareTable.value.tableData();
    // æ£€æµ‹æ˜¯å¦è¾“入出库数量
    const eList = selectSpareList.filter((item: any) => !item.quantity || item.quantity <= 0);
    if (selectSpareList.length<= 0 || eList.length > 0) {
      message.error('入库数量为空,请检查!');
      return false;
    }
    const data = cloneDeep(await formApi.getValues());
    data.spareList = selectSpareList;
    await (isUpdate.value ? updateSpareInout(data) : addSpareInout(data));
    emit('reload');
    await handleCancel();
@@ -142,14 +220,42 @@
  drawerApi.close();
  await formApi.resetForm();
}
// å¤‡ä»¶modal
const [SpareModal, spareModalApi] = useVbenModal({
  connectedComponent: spareModal,
  draggable: true,
  title: '选择备件'
});
function handleSpareModal() {
  spareModalApi.setData({});
  spareModalApi.open();
}
/**
 * é€‰æ‹©çš„备件
 * @param spareList
 */
function selectSpare(spareList: any) {
  outSpareList.value = spareList;
}
</script>
<template>
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[1000px]">
    <BasicForm>
      <template #orderCode="slotProps">
        <CodeInput v-bind="slotProps" :disabled="isUpdate" prefix="RK" />
      </template>
      <template #openSpare="slotProps">
        <a-button type="primary" v-bind="slotProps" :disabled="isUpdate" @click.stop="handleSpareModal">添加备件</a-button>
      </template>
      <template #outSpareList>
        <SelectSpareTable ref="selectSpareTable" :columns="outCol" :data="outSpareList" :is-update="isUpdate" />
      </template>
    </BasicForm>
    <SpareModal class="w-[1200px]" @update-select="selectSpare" />
  </BasicDrawer>
</template>
eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/data.tsx
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
import type { VxeGridProps } from '#/adapter/vxe-table';
import { type FormSchemaGetter } from '#/adapter/form';
import { renderDict } from '#/utils/render';
import { DictEnum } from '@vben/constants';
export const querySchema: FormSchemaGetter = () => [];
export const columns: VxeGridProps['columns'] = [
  { type: 'checkbox', width: 60, fixed: 'left' },
  {
    title: '备件名称',
    field: 'spareName',
    minWidth: 120
  },
  {
    title: '备件编号',
    field: 'spareCode',
    minWidth: 120
  },
  {
    title: '规格型号',
    field: 'modelNo',
    minWidth: 100
  },
  {
    title: '计量单位',
    field: 'unit',
    sortable: true,
    slots: {
      default: ({ row }) => {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
      }
    },
    width: 100
  },
  {
    title: '之前库存',
    field: 'beforeStock',
    minWidth: 100
  },
  {
    title: '当前库存',
    field: 'actualStock',
    minWidth: 100
  },
  {
    title: '数量',
    field: 'quantity',
    minWidth: 80
  },
  {
    title: '单价',
    field: 'unitPrice',
    minWidth: 80
  },
  {
    title: '金额',
    field: 'amount',
    minWidth: 80
  }
];
export const drawerSchema: FormSchemaGetter = () => [];
eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx
@@ -84,6 +84,8 @@
  }
];
export const drawerSchema: FormSchemaGetter = () => [
  {
    component: 'Input',
@@ -118,6 +120,17 @@
    label: '客户'
  },
  {
    component: 'Input',
    fieldName: 'openSpare',
    label: '选择备件',
    formItemClass: 'col-span-1 w-[80px]'
  },
  {
    component: 'Input',
    fieldName: 'outSpareList',
    label: ''
  },
  {
    component: 'TreeSelect',
    // åœ¨drawer里更新 è¿™é‡Œä¸éœ€è¦é»˜è®¤çš„componentProps
    defaultValue: undefined,
eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import { onMounted } from 'vue';
import { onMounted, ref } from 'vue';
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
@@ -16,6 +16,9 @@
import { columns, querySchema } from './data';
import drawer from './spare-out-drawer.vue';
import { columns as inoutCol } from '#/views/eims/spare-inoutdt/data';
import { listSpareInoutdt } from '#/api/eims/spare-inoutdt';
import BasisSubTable from '#/views/eims/components/basis-sub-table.vue';
const formOptions: VbenFormProps = {
  commonConfig: {
@@ -69,12 +72,16 @@
  },
  id: 'spre-inout-index'
};
const inoutId = ref<string>();
const [BasicTable, tableApi] = useVbenVxeGrid({
  formOptions,
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
    cellClick: (e: any) => {
      const { row } = e;
      inoutId.value = row.id;
    }
  }
});
@@ -190,8 +197,8 @@
<template>
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px]">
      <BasicTable class="flex-1 overflow-hidden" table-title="备件出库单列表">
    <div class="flex h-full gap-[8px] flex-col">
      <BasicTable class="h-2/3" table-title="备件出库单列表">
        <template #toolbar-tools>
          <Space>
            <a-button v-access:code="['eims:spareInout:export']" @click="handleDownloadExcel">
@@ -231,6 +238,14 @@
          </Space>
        </template>
      </BasicTable>
      <BasisSubTable
        :columns="inoutCol"
        :list-api="listSpareInoutdt"
        :req-value="inoutId"
        class="h-1/3"
        req-key="inoutId"
        title="出库明细"
      />
    </div>
    <Drawer @reload="tableApi.query()" />
  </Page>
eims-ui/apps/web-antd/src/views/eims/spare-out/select-spare-table.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,121 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import { reactive, ref, watch } from 'vue';
import { $t } from '@vben/locales';
import { getVxePopupContainer } from '@vben/utils';
import { Popconfirm, Space } from 'ant-design-vue';
import { useVbenVxeGrid, type VxeGridProps, vxeSortEvent } from '#/adapter/vxe-table';
interface Props {
  title?: string;
  columns?: VxeGridProps['columns'];
  data: any;
  isUpdate?: boolean;
}
const props = defineProps<Props>();
const responsiveData = reactive(props.data);
defineExpose({
  tableData
});
watch(
  () => props.data,
  (data) => {
    responsiveData.splice(0, responsiveData.length, ...data);
  }
);
const gridOptions: VxeGridProps = {
  checkboxConfig: {
    // é«˜äº®
    highlight: true,
    // ç¿»é¡µæ—¶ä¿ç•™é€‰ä¸­çŠ¶æ€
    reserve: true
    // ç‚¹å‡»è¡Œé€‰ä¸­
    // trigger: 'row'
  },
  columns: props.columns,
  height: 'auto',
  keepSource: true,
  data: responsiveData,
  pagerConfig: {
    enabled: false
  },
  toolbarConfig: {
    enabled: false
  },
  rowConfig: {
    isHover: true,
    keyField: 'id'
  },
  sortConfig: {
    // è¿œç¨‹æŽ’序
    remote: true,
    // æ”¯æŒå¤šå­—段排序 é»˜è®¤å…³é—­
    multiple: true
  },
  editConfig: {
    mode: 'cell',
    trigger: 'click'
  },
  id: 'local-table'
};
const [BasicTable, tableApi] = useVbenVxeGrid({
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
  }
});
function handleDelete(row: Recordable<any>) {
  const index = responsiveData.findIndex((item: any) => item.id === row.id);
  if (index !== -1) {
    responsiveData.splice(index, 1);
  }
}
// é€‰ä¸­æ•°æ®
function tableData() {
  return tableApi.grid.getData();
}
/**
 * TODO åŽç»­æ‰©å±•点击事件
 */
const slotName = ref<string>('equName');
</script>
<template>
  <div class="w-full h-min">
    <BasicTable :table-title="title" size="small">
      <template #[slotName]="{ row }">
        <Space>
          <span>{{ row[slotName] }}</span>
        </Space>
      </template>
      <template #action="{ row }">
        <Space>
          <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="确认删除?" @confirm="handleDelete(row)">
            <ghost-button :disabled="isUpdate" danger @click.stop="">
              {{ $t('pages.common.delete') }}
            </ghost-button>
          </Popconfirm>
        </Space>
      </template>
    </BasicTable>
  </div>
</template>
<style lang="scss" scoped>
:deep(.p-2) {
  padding: 0;
}
</style>
eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue
@@ -1,18 +1,88 @@
<script setup lang="ts">
import type { VxeGridProps } from '#/adapter/vxe-table';
import { computed, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { $t } from '@vben/locales';
import { addFullName, cloneDeep, getPopupContainer } from '@vben/utils';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { addSpareInout, getSpareInout, updateSpareInout } from '#/api/eims/spare-inout';
import { getDeptTree, userList } from '#/api/system/user';
import { renderDict } from '#/utils/render';
import CodeInput from '#/views/eims/components/code-input.vue';
import spareModal from '#/views/eims/components/spare-modal.vue';
import { drawerSchema } from './data';
import CodeInput from '#/views/eims/components/code-input.vue';
import SelectSpareTable from './select-spare-table.vue';
const emit = defineEmits<{ reload: [] }>();
/**
 * å‡ºåº“单选择的备件数据
 */
const outSpareList = ref([]);
const selectSpareTable = ref();
const outCol: VxeGridProps['columns'] = [
  {
    field: 'action',
    slots: { default: 'action' },
    title: '删除',
    width: 60
  },
  {
    title: '备件名称',
    field: 'name',
    width: 180
  },
  {
    title: '备件编码',
    field: 'code',
    width: 120
  },
  {
    title: '备件型号',
    field: 'modelNo',
    width: 100
  },
  {
    title: '计量单位',
    field: 'unit',
    slots: {
      default: ({ row }) => {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
      }
    },
    width: 80
  },
  {
    title: '实际库存',
    field: 'actualStock',
    width: 100
  },
  {
    title: '数量',
    field: 'quantity',
    editRender: {
      name: 'input'
    },
    width: 80
  },
  {
    title: '参考价',
    field: 'referPrice',
    width: 90
  }
];
const isUpdate = ref(false);
const title = computed(() => {
@@ -42,12 +112,18 @@
    drawerApi.drawerLoading(true);
    const { id } = drawerApi.getData() as { id?: number | string };
    isUpdate.value = !!id;
    outSpareList.value = [];
    // åˆå§‹åŒ–
    await setupDeptSelect();
    // æ›´æ–° && èµ‹å€¼
    if (isUpdate.value && id) {
      const record = await getSpareInout(id);
      await formApi.setValues(record);
      // æ›´æ–°å‡ºåº“单的备件明细
      outSpareList.value = record?.spareList;
      if (isUpdate.value && record.chargeDept) {
        await setupUserOptions(record.chargeDept);
      }
    }
    drawerApi.drawerLoading(false);
@@ -104,7 +180,7 @@
          /** æ ¹æ®éƒ¨é—¨ID加载用户 */
          await setupUserOptions(deptId);
          /** å˜åŒ–后需要重新选择用户 */
          formModel.operatorId = undefined;
          formModel.chargeUser = undefined;
        },
        placeholder: '请选择',
        showSearch: true,
@@ -120,6 +196,7 @@
    }
  ]);
}
async function handleConfirm() {
  try {
    drawerApi.drawerLoading(true);
@@ -127,7 +204,15 @@
    if (!valid) {
      return;
    }
    const selectSpareList = selectSpareTable.value.tableData();
    // æ£€æµ‹æ˜¯å¦è¾“入出库数量
    const eList = selectSpareList.filter((item: any) => !item.quantity || item.quantity <= 0 || item.quantity > item.actualStock);
    if (selectSpareList.length<= 0 ||eList.length > 0) {
      message.error('出库数量为空或大于库存,请检查!');
      return false;
    }
    const data = cloneDeep(await formApi.getValues());
    data.spareList = selectSpareList;
    await (isUpdate.value ? updateSpareInout(data) : addSpareInout(data));
    emit('reload');
    await handleCancel();
@@ -142,14 +227,43 @@
  drawerApi.close();
  await formApi.resetForm();
}
// å¤‡ä»¶modal
const [SpareModal, spareModalApi] = useVbenModal({
  connectedComponent: spareModal,
  draggable: true,
  title: '选择备件'
});
function handleSpareModal() {
  spareModalApi.setData({});
  spareModalApi.open();
}
/**
 * é€‰æ‹©çš„备件
 * @param spareList
 */
function selectSpare(spareList: any) {
  outSpareList.value = spareList;
}
</script>
<template>
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[1000px]">
    <BasicForm>
      <template #orderCode="slotProps">
        <CodeInput v-bind="slotProps" :disabled="isUpdate" prefix="CK" />
      </template>
      <template #openSpare="slotProps">
        <a-button type="primary" v-bind="slotProps" :disabled="isUpdate" @click.stop="handleSpareModal">添加备件</a-button>
      </template>
      <template #outSpareList>
        <SelectSpareTable ref="selectSpareTable" :columns="outCol" :data="outSpareList" :is-update="isUpdate" />
      </template>
    </BasicForm>
    <SpareModal class="w-[1200px]" @update-select="selectSpare" />
  </BasicDrawer>
</template>
eims-ui/apps/web-antd/src/views/eims/spare/data.tsx
@@ -1,11 +1,13 @@
import type { VxeGridProps } from '#/adapter/vxe-table';
import { DictEnum } from '@vben/constants';
import { getPopupContainer } from '@vben/utils';
import { Tag } from 'ant-design-vue';
import { type FormSchemaGetter } from '#/adapter/form';
import { getDictOptions } from '#/utils/dict';
import { renderDict } from '#/utils/render';
import { getPopupContainer } from '@vben/utils';
export const querySchema: FormSchemaGetter = () => [
  {
@@ -84,7 +86,7 @@
    sortable: true,
    slots: {
      default: ({ row }) => {
        if (row.unit === null || row.unit === '') {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
@@ -138,6 +140,87 @@
  }
];
export const inoutCol: VxeGridProps['columns'] = [
  {
    title: '出入库单号',
    field: 'orderCode',
    width: 180
  },
  {
    title: '日期',
    field: 'orderTime',
    width: 180
  },
  {
    title: '方向',
    field: 'type1',
    width: 80,
    slots: {
      default: ({ row }) => {
        const type = row.type;
        switch (type) {
          case '1': {
            return <Tag color="green"> å…¥åº“ </Tag>;
          }
          case '2': {
            return <Tag color="blue"> å‡ºåº“ </Tag>;
          }
          // No default
        }
        return '';
      }
    }
  },
  {
    title: '类型',
    field: 'type',
    width: 100,
    slots: {
      default: ({ row }) => {
        if (!row.type || row.type === '') {
          return '';
        }
        return renderDict(row.type, DictEnum.SPARE_INOUT_TYPE);
      }
    }
  },
  {
    title: '入库',
    field: 'inQuantity',
    width: 120,
    slots: {
      default: ({ row }) => {
        return row.type && row.type === '1' ? row.quantity : '';
      }
    }
  },
  {
    title: '出库',
    field: 'outQuantity',
    width: 120,
    slots: {
      default: ({ row }) => {
        return row.type && row.type === '2' ? row.quantity : '';
      }
    }
  },
  {
    title: '库存',
    field: 'actualStock',
    width: 80
  },
  {
    title: '单价',
    field: 'unitPrice',
    width: 80
  },
  {
    title: '金额',
    field: 'amount',
    width: 80
  }
];
export const drawerSchema: FormSchemaGetter = () => [
  {
    component: 'Input',
@@ -173,7 +256,7 @@
      show: () => false,
      triggerFields: ['imgUrl']
    },
    label: '备件预览',
    label: '备件预览'
  },
  {
    component: 'Input',
eims-ui/apps/web-antd/src/views/eims/spare/index.vue
@@ -10,11 +10,12 @@
import { Image, Modal, Popconfirm, Space } from 'ant-design-vue';
import { useVbenVxeGrid, vxeCheckboxChecked, type VxeGridProps, vxeSortEvent } from '#/adapter/vxe-table';
import { delSpare, listSpare, spareExport } from '#/api/eims/spare';
import { delSpare, listInout, listSpare, spareExport } from '#/api/eims/spare';
import { configInfoByKey } from '#/api/system/config';
import { commonDownloadExcel } from '#/utils/file/download';
import BasisSubTable from '#/views/eims/components/basis-sub-table.vue';
import { columns, querySchema } from './data';
import { columns, inoutCol, querySchema } from './data';
import spareDrawer from './spare-drawer.vue';
import SpareTypeTree from './spare-type-tree.vue';
@@ -59,7 +60,7 @@
  height: 'auto',
  keepSource: true,
  pagerConfig: {
    enabled: false,
    enabled: false
  },
  proxyConfig: {
    enabled: true,
@@ -84,7 +85,7 @@
    keyField: 'id'
  },
  columnConfig: {
    resizable: true,
    resizable: true
  },
  sortConfig: {
    // è¿œç¨‹æŽ’序
@@ -94,12 +95,16 @@
  },
  id: 'eims-spare-index'
};
const id = ref<string>();
const [BasicTable, tableApi] = useVbenVxeGrid({
  formOptions,
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
    cellClick: (e: any) => {
      const { row } = e;
      id.value = row.id;
    }
  }
});
@@ -163,7 +168,9 @@
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px]">
      <SpareTypeTree v-model:select-type-id="selectTypeId" class="w-[260px]" @reload="() => tableApi.reload()" @select="() => tableApi.reload()" />
      <BasicTable class="flex-1 overflow-hidden" table-title="备件台账">
      <div class="flex-1 overflow-hidden">
        <div class="flex h-full gap-[8px] flex-col">
          <BasicTable class="h-2/3" table-title="备件台账">
        <template #toolbar-tools>
          <Space>
            <a-button v-access:code="['eims:spare:export']" @click="handleDownloadExcel">
@@ -202,12 +209,15 @@
          </Space>
        </template>
      </BasicTable>
          <BasisSubTable :columns="inoutCol" :list-api="listInout" :req-value="id" class="h-1/3" req-key="id" title="出入库明细" />
        </div>
      </div>
    </div>
    <SpareDrawer @reload="tableApi.query()" />
  </Page>
</template>
<style>
<style lang="scss" scoped>
/* ç»Ÿä¸€æ‰€æœ‰åˆ—的边框和行高 */
.vxe-table--body .vxe-body--row .vxe-body--column {
  height: 56px !important;
eims/ruoyi-admin/src/main/resources/application-prod.yml
@@ -48,9 +48,9 @@
          driverClassName: com.mysql.cj.jdbc.Driver
          # jdbc æ‰€æœ‰å‚数配置参考 https://lionli.blog.csdn.net/article/details/122018562
          # rewriteBatchedStatements=true æ‰¹å¤„理优化 å¤§å¹…提升批量插入更新删除性能(对数据库有性能损耗 ä½¿ç”¨æ‰¹é‡æ“ä½œåº”考虑性能问题)
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          url: jdbc:mysql://localhost:3306/eims?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          username: root
          password: root
          password: 123456
        # ä»Žåº“数据源
        slave:
          lazy: true
@@ -103,7 +103,7 @@
    # æ•°æ®åº“索引
    database: 0
    # redis å¯†ç å¿…须配置
    password: ruoyi123
    #password: ruoyi123
    # è¿žæŽ¥è¶…æ—¶æ—¶é—´
    timeout: 10s
    # æ˜¯å¦å¼€å¯ssl
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java
@@ -161,4 +161,16 @@
    }
    /**
     *备件出入库类型
     */
    String SPARE_INOUT_TYPE = "spare_inout_type";
    interface SPARE_INOUT_TYPE_DETAIL {
        String RK = "1";// é‡‡è´­å…¥åº“
        String CK = "2"; // é¢†ç”¨å‡ºåº“
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareController.java
@@ -6,6 +6,8 @@
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -45,6 +47,12 @@
        return eimsSpareService.queryPageList(bo, pageQuery);
    }
    @SaCheckPermission("eims:spare:list")
    @GetMapping("/listInout")
    public TableDataInfo<EimsSpareInoutdtVo> listInout(EimsSpareBo bo, PageQuery pageQuery) {
        return eimsSpareService.querySpareInoutList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºå¤‡ä»¶å°è´¦åˆ—表
     */
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutController.java
@@ -45,6 +45,7 @@
        return eimsSpareInoutService.queryPageList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºå¤‡ä»¶å‡ºå…¥åº“列表
     */
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutdtController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package org.dromara.eims.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.bo.EimsSpareInoutdtBo;
import org.dromara.eims.service.IEimsSpareInoutdtService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/eims/spareInoutdt")
public class EimsSpareInoutdtController extends BaseController {
    private final IEimsSpareInoutdtService eimsSpareInoutdtService;
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    @SaCheckPermission("eims:spareInoutdt:list")
    @GetMapping("/list")
    public TableDataInfo<EimsSpareInoutdtVo> list(EimsSpareInoutdtBo bo, PageQuery pageQuery) {
        return eimsSpareInoutdtService.queryPageList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºå¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    @SaCheckPermission("eims:spareInoutdt:export")
    @Log(title = "备件出入库明细", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(EimsSpareInoutdtBo bo, HttpServletResponse response) {
        List<EimsSpareInoutdtVo> list = eimsSpareInoutdtService.queryList(bo);
        ExcelUtil.exportExcel(list, "备件出入库明细", EimsSpareInoutdtVo.class, response);
    }
    /**
     * èŽ·å–å¤‡ä»¶å‡ºå…¥åº“æ˜Žç»†è¯¦ç»†ä¿¡æ¯
     *
     * @param id ä¸»é”®
     */
    @SaCheckPermission("eims:spareInoutdt:query")
    @GetMapping("/{id}")
    public R<EimsSpareInoutdtVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable Long id) {
        return R.ok(eimsSpareInoutdtService.queryById(id));
    }
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“明细
     */
    @SaCheckPermission("eims:spareInoutdt:add")
    @Log(title = "备件出入库明细", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody EimsSpareInoutdtBo bo) {
        return toAjax(eimsSpareInoutdtService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹å¤‡ä»¶å‡ºå…¥åº“明细
     */
    @SaCheckPermission("eims:spareInoutdt:edit")
    @Log(title = "备件出入库明细", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody EimsSpareInoutdtBo bo) {
        return toAjax(eimsSpareInoutdtService.updateByBo(bo));
    }
    /**
     * åˆ é™¤å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param ids ä¸»é”®ä¸²
     */
    @SaCheckPermission("eims:spareInoutdt:remove")
    @Log(title = "备件出入库明细", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(eimsSpareInoutdtService.deleteWithValidByIds(List.of(ids), true));
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsSpareInoutdt.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package org.dromara.eims.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细对象 eims_spare_inoutdt
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("eims_spare_inoutdt")
public class EimsSpareInoutdt extends BaseEntity {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    @TableId(value = "id")
    private Long id;
    /**
     * å‡ºåº“单或入库单id
     */
    private Long inoutId;
    /**
     * å¤‡ä»¶id
     */
    private Long spareId;
    /**
     * ä¹‹å‰åº“å­˜
     */
    private Long beforeStock;
    /**
     * å®žé™…库存
     */
    private Long actualStock;
    /**
     * æ•°é‡
     */
    private Long quantity;
    /**
     * å•ä»·
     */
    private BigDecimal unitPrice;
    /**
     * é‡‘额
     */
    private BigDecimal amount;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareBo.java
@@ -114,4 +114,11 @@
    private String remark;
    /**
     * å‡ºåº“入库数量
     */
    private Long quantity;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutBo.java
@@ -9,7 +9,10 @@
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.eims.domain.vo.EimsSpareVo;
/**
 * å¤‡ä»¶å‡ºå…¥åº“业务对象 eims_spare_inout
@@ -69,5 +72,9 @@
     */
    private String remark;
    //出入库选择的备件明细
    private List<EimsSpareBo> spareList;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutdtBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
package org.dromara.eims.domain.bo;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细业务对象 eims_spare_inoutdt
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = EimsSpareInoutdt.class, reverseConvertGenerate = false)
public class EimsSpareInoutdtBo extends BaseEntity {
    /**
     *
     */
    @NotNull(message = "不能为空", groups = { EditGroup.class })
    private Long id;
    /**
     * å‡ºåº“单或入库单id
     */
    @NotNull(message = "出库单或入库单id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long inoutId;
    /**
     * å¤‡ä»¶id
     */
    @NotNull(message = "备件id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long spareId;
    /**
     * ä¹‹å‰åº“å­˜
     */
    @NotNull(message = "之前库存不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long beforeStock;
    /**
     * å®žé™…库存
     */
    @NotNull(message = "实际库存不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long actualStock;
    /**
     * æ•°é‡
     */
    @NotNull(message = "数量不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long quantity;
    /**
     * å•ä»·
     */
    @NotNull(message = "单价不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long unitPrice;
    /**
     * é‡‘额
     */
    @NotNull(message = "金额不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long amount;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutVo.java
@@ -11,11 +11,12 @@
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.eims.domain.bo.EimsSpareBo;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
@@ -87,5 +88,6 @@
    @ExcelProperty(value = "备注")
    private String remark;
    //出入库选择的备件明细
    private List<EimsSpareVo> spareList;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutdtVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package org.dromara.eims.domain.vo;
import org.dromara.eims.domain.EimsSpareInoutdt;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细视图对象 eims_spare_inoutdt
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = EimsSpareInoutdt.class)
public class EimsSpareInoutdtVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    @ExcelProperty(value = "")
    private Long id;
    /**
     * å‡ºåº“单或入库单id
     */
    @ExcelProperty(value = "出库单或入库单id")
    private Long inoutId;
    /**
     * å¤‡ä»¶id
     */
    @ExcelProperty(value = "备件id")
    private Long spareId;
    /**
     * ä¹‹å‰åº“å­˜
     */
    @ExcelProperty(value = "之前库存")
    private Long beforeStock;
    /**
     * å®žé™…库存
     */
    @ExcelProperty(value = "实际库存")
    private Long actualStock;
    /**
     * æ•°é‡
     */
    @ExcelProperty(value = "数量")
    private Long quantity;
    /**
     * å•ä»·
     */
    @ExcelProperty(value = "单价")
    private BigDecimal unitPrice;
    /**
     * é‡‘额
     */
    @ExcelProperty(value = "金额")
    private BigDecimal amount;
    /**
     * å¤‡æ³¨
     */
    @ExcelProperty(value = "备注")
    private String remark;
    //备件
    private String spareName;
    private String spareCode;
    private String modelNo;
    //出入库单
    private String orderCode;
    private Date orderTime;
    private String type;
    private String unit;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareVo.java
@@ -142,6 +142,9 @@
     */
    @ExcelProperty(value = "备注")
    private String remark;
    /**
     * å‡ºåº“入库数量
     */
    private Long quantity;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutMapper.java
@@ -1,8 +1,15 @@
package org.dromara.eims.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
/**
 * å¤‡ä»¶å‡ºå…¥åº“Mapper接口
@@ -12,4 +19,5 @@
 */
public interface EimsSpareInoutMapper extends BaseMapperPlus<EimsSpareInout, EimsSpareInoutVo> {
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutdtMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.eims.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.eims.domain.EimsMaintSt;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.domain.vo.EimsMaintStVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细Mapper接口
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
public interface EimsSpareInoutdtMapper extends BaseMapperPlus<EimsSpareInoutdt, EimsSpareInoutdtVo> {
    Page<EimsSpareInoutdtVo> selectSpareInoutdtList(@Param("page") Page<EimsSpareInoutdtVo> page, @Param(Constants.WRAPPER) Wrapper<EimsSpareInoutdt> queryWrapper);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareMapper.java
@@ -1,6 +1,13 @@
package org.dromara.eims.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.eims.domain.EimsSpare;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.vo.EimsSpareVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@@ -11,5 +18,5 @@
 * @date 2025-03-20
 */
public interface EimsSpareMapper extends BaseMapperPlus<EimsSpare, EimsSpareVo> {
    Page<EimsSpareInoutdtVo> selectSpareInoutList(@Param("page") Page<EimsSpareInoutdtVo> page, @Param(Constants.WRAPPER) Wrapper<EimsSpare> queryWrapper);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutService.java
@@ -65,4 +65,8 @@
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutdtService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package org.dromara.eims.service;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.bo.EimsSpareInoutdtBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细Service接口
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
public interface IEimsSpareInoutdtService {
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param id ä¸»é”®
     * @return å¤‡ä»¶å‡ºå…¥åº“明细
     */
    EimsSpareInoutdtVo queryById(Long id);
    /**
     * åˆ†é¡µæŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细列表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return å¤‡ä»¶å‡ºå…¥åº“明细分页列表
     */
    TableDataInfo<EimsSpareInoutdtVo> queryPageList(EimsSpareInoutdtBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件出入库明细列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return å¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    List<EimsSpareInoutdtVo> queryList(EimsSpareInoutdtBo bo);
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    Boolean insertByBo(EimsSpareInoutdtBo bo);
    /**
     * ä¿®æ”¹å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    Boolean updateByBo(EimsSpareInoutdtBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤å¤‡ä»¶å‡ºå…¥åº“明细信息
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareService.java
@@ -1,5 +1,8 @@
package org.dromara.eims.service;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.vo.EimsSpareVo;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -65,4 +68,13 @@
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
    /**
     * æ ¹æ®å¤‡ä»¶æŸ¥è¯¢å‡ºå…¥åº“明细
     * @param bo
     * @param pageQuery
     * @return
     */
    TableDataInfo<EimsSpareInoutdtVo> querySpareInoutList(EimsSpareBo bo, PageQuery pageQuery);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutServiceImpl.java
@@ -1,5 +1,7 @@
package org.dromara.eims.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -8,16 +10,24 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.EimsSpare;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.vo.EimsSpareVo;
import org.dromara.eims.mapper.EimsSpareInoutdtMapper;
import org.dromara.eims.mapper.EimsSpareMapper;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.mapper.EimsSpareInoutMapper;
import org.dromara.eims.service.IEimsSpareInoutService;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
 * å¤‡ä»¶å‡ºå…¥åº“Service业务层处理
@@ -30,6 +40,8 @@
public class EimsSpareInoutServiceImpl implements IEimsSpareInoutService {
    private final EimsSpareInoutMapper baseMapper;
    private final EimsSpareInoutdtMapper inoutdtMapper;
    private final EimsSpareMapper spareMapper;
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“
@@ -39,7 +51,33 @@
     */
    @Override
    public EimsSpareInoutVo queryById(Long id){
        return baseMapper.selectVoById(id);
        EimsSpareInoutVo eimsSpareInoutVo = baseMapper.selectVoById(id);
        QueryWrapper<EimsSpareInoutdt> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EimsSpareInoutdt::getInoutId, id);
        List<EimsSpareInoutdtVo> dtVos = inoutdtMapper.selectVoList(queryWrapper);
        if(!dtVos.isEmpty()){
            Map<Long, Long> map = dtVos.stream()
                .collect(Collectors.toMap(
                    EimsSpareInoutdtVo::getSpareId,
                    EimsSpareInoutdtVo::getQuantity
                ));
            List<Long> spareIdList = dtVos.stream()
                .map(EimsSpareInoutdtVo::getSpareId) // èŽ·å– spareId å­—段
                .toList();
            List<EimsSpareVo> eimsSpareListVos = spareMapper.selectVoBatchIds(spareIdList);
            for (EimsSpareVo spareVo : eimsSpareListVos) {
                // å‡è®¾ quantity çš„值是一个固定值(例如 10)
                spareVo.setQuantity(map.get(spareVo.getId()));
            }
            eimsSpareInoutVo.setSpareList(eimsSpareListVos);
        }
        return eimsSpareInoutVo;
    }
    /**
@@ -55,6 +93,7 @@
        Page<EimsSpareInoutVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件出入库列表
@@ -81,19 +120,73 @@
        return lqw;
    }
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean insertByBo(EimsSpareInoutBo bo) {
    public synchronized Boolean insertByBo(EimsSpareInoutBo bo) {
        EimsSpareInout add = MapstructUtils.convert(bo, EimsSpareInout.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        //入库+库存  å‡ºåº“-库存  ï¼ˆé»˜è®¤å…¥åº“)
        int  OperationType;
         //出库
         if(bo.getType().equals(DictConstants.SPARE_INOUT_TYPE_DETAIL.CK)){
            OperationType =  -1;
        } else {
             OperationType = 1;
         }
        List<EimsSpareBo> spareList = Optional.ofNullable(bo.getSpareList()).orElse(new ArrayList<>());
        // æ’入出库明细
        List<EimsSpareInoutdt> dtList = spareList.stream()
            .map(eimsSpareBo -> {
                EimsSpareInoutdt dt = new EimsSpareInoutdt();
                dt.setInoutId(add.getId());
                dt.setSpareId(eimsSpareBo.getId());
                dt.setBeforeStock(eimsSpareBo.getActualStock());
                // æ³¨æ„å…¥åº“出库
                dt.setActualStock(eimsSpareBo.getActualStock() + (eimsSpareBo.getQuantity() * OperationType));
                dt.setQuantity(eimsSpareBo.getQuantity());
                // è®¾ç½®å•价和金额
                Optional.ofNullable(eimsSpareBo.getReferPrice()).ifPresent(referPrice -> {
                    dt.setUnitPrice(referPrice);
                    dt.setAmount(referPrice.multiply(BigDecimal.valueOf(eimsSpareBo.getQuantity())));
                });
                return dt;
            })
            .toList();
        // æ‰¹é‡æ’入数据
        if (!dtList.isEmpty()) {
            inoutdtMapper.insertBatch(dtList);
        }
        // æ›´æ–°å¤‡ä»¶çš„库存
        List<EimsSpare> updateSpareList = spareList.stream().map(spareBo -> {
            EimsSpare spare = new EimsSpare();
            spare.setId(spareBo.getId());
            spare.setActualStock(spareBo.getActualStock() + (spareBo.getQuantity() * OperationType));
            // è®¾ç½®å•价和金额
            Optional.ofNullable(spareBo.getReferPrice()).ifPresent(referPrice -> {
                spare.setStockAmount(referPrice.multiply(BigDecimal.valueOf(spare.getActualStock())));
            });
            return spare;
        }).toList();
        if (!updateSpareList.isEmpty()) {
            spareMapper.updateBatchById(updateSpareList);
        }
        return flag;
    }
@@ -132,4 +225,6 @@
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutdtServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,138 @@
package org.dromara.eims.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.vo.EimsMaintStVo;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsSpareInoutdtBo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.mapper.EimsSpareInoutdtMapper;
import org.dromara.eims.service.IEimsSpareInoutdtService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细Service业务层处理
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@RequiredArgsConstructor
@Service
public class EimsSpareInoutdtServiceImpl implements IEimsSpareInoutdtService {
    private final EimsSpareInoutdtMapper baseMapper;
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param id ä¸»é”®
     * @return å¤‡ä»¶å‡ºå…¥åº“明细
     */
    @Override
    public EimsSpareInoutdtVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细列表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return å¤‡ä»¶å‡ºå…¥åº“明细分页列表
     */
    @Override
    public TableDataInfo<EimsSpareInoutdtVo> queryPageList(EimsSpareInoutdtBo bo, PageQuery pageQuery) {
        Page<EimsSpareInoutdtVo> result = baseMapper.selectSpareInoutdtList(pageQuery.build(), buildWrapper(bo));
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件出入库明细列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return å¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    @Override
    public List<EimsSpareInoutdtVo> queryList(EimsSpareInoutdtBo bo) {
        LambdaQueryWrapper<EimsSpareInoutdt> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<EimsSpareInoutdt> buildQueryWrapper(EimsSpareInoutdtBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<EimsSpareInoutdt> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getInoutId() != null, EimsSpareInoutdt::getInoutId, bo.getInoutId());
        lqw.eq(bo.getSpareId() != null, EimsSpareInoutdt::getSpareId, bo.getSpareId());
        return lqw;
    }
    private QueryWrapper<EimsSpareInoutdt> buildWrapper(EimsSpareInoutdtBo bo) {
        Map<String, Object> params = bo.getParams();
        QueryWrapper<EimsSpareInoutdt> qw = Wrappers.query();
        qw.eq("io.id",bo.getInoutId());
        return qw;
    }
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Override
    public Boolean insertByBo(EimsSpareInoutdtBo bo) {
        EimsSpareInoutdt add = MapstructUtils.convert(bo, EimsSpareInoutdt.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    /**
     * ä¿®æ”¹å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    @Override
    public Boolean updateByBo(EimsSpareInoutdtBo bo) {
        EimsSpareInoutdt update = MapstructUtils.convert(bo, EimsSpareInoutdt.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(EimsSpareInoutdt entity){
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤å¤‡ä»¶å‡ºå…¥åº“明细信息
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareServiceImpl.java
@@ -1,5 +1,6 @@
package org.dromara.eims.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -8,6 +9,10 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.eims.domain.vo.EimsSpareVo;
@@ -56,6 +61,13 @@
        return TableDataInfo.build(result);
    }
    @Override
    public TableDataInfo<EimsSpareInoutdtVo> querySpareInoutList(EimsSpareBo bo, PageQuery pageQuery) {
        Page<EimsSpareInoutdtVo> result = baseMapper.selectSpareInoutList(pageQuery.build(), buildWrapper(bo));
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件台账列表
     *
@@ -79,6 +91,13 @@
        lqw.like(StringUtils.isNotBlank(bo.getSupplier()), EimsSpare::getSupplier, bo.getSupplier());
        lqw.eq(StringUtils.isNotBlank(bo.getUnit()), EimsSpare::getUnit, bo.getUnit());
        return lqw;
    }
    private QueryWrapper<EimsSpare> buildWrapper(EimsSpareBo bo) {
        Map<String, Object> params = bo.getParams();
        QueryWrapper<EimsSpare> qw = Wrappers.query();
        qw.eq( "sp.id", bo.getId());
        return qw;
    }
    /**
@@ -132,4 +151,6 @@
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareInoutdtMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.eims.mapper.EimsSpareInoutdtMapper">
    <resultMap type="org.dromara.eims.domain.vo.EimsSpareInoutdtVo" id="SpareInoutdtVoResult">
    </resultMap>
    <select id="selectSpareInoutdtList" resultMap="SpareInoutdtVoResult">
        SELECT dt.*, sp.name spareName, sp.code spareCode, sp.model_no modelNo, sp.unit unit, io.order_code orderCode
        FROM eims_spare_inoutdt dt
                 LEFT JOIN eims_spare_inout io on dt.inout_id = io.id
                 LEFT JOIN eims_spare sp on dt.spare_id = sp.id
            ${ew.getCustomSqlSegment}
    </select>
</mapper>
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareMapper.xml
@@ -3,5 +3,13 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.eims.mapper.EimsSpareMapper">
    <resultMap type="org.dromara.eims.domain.vo.EimsSpareInoutdtVo" id="SpareInoutdtVoResult">
    </resultMap>
    <select id="selectSpareInoutList" resultMap="SpareInoutdtVoResult">
        SELECT io.*, dt.quantity, dt.actual_stock, dt.unit_price, dt.amount
        FROM eims_spare_inout io
                 JOIN eims_spare_inoutdt dt on io.id = dt.inout_id
                 JOIN eims_spare sp on dt.spare_id = sp.id
            ${ew.getCustomSqlSegment}
    </select>
</mapper>