干燥机配套车间生产管理系统/云平台服务端
baoshiwei
2023-05-24 beca65f4d01ca07c358102a35b949c2a4f277afe
增加车间监控界面
已添加18个文件
已修改44个文件
41112 ■■■■ 文件已修改
.eslintrc.js 150 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LICENSE 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
commitlint.config.js 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
npm 补丁 | 查看 | 原始文档 | blame | 历史
package.json 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pnpm-lock.yaml 16838 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
prettier.config.js 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/sys/menu.ts 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/sys/model/menuModel.ts 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/ganzaoji-x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/ganzaoji-z.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/ganzaoji.gif 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/ganzaoji.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Button/src/BasicButton.vue 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/enums/pageEnum.ts 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/default/header/components/user-dropdown/index.vue 396 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/lang/en/layout.ts 210 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/lang/zh-CN/layout.ts 210 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.ts 98 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/constant.ts 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.ts 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/menus/index.ts 170 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/basic.ts 132 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/index.ts 92 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/mainOut.ts 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/modules/dashboard.ts 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/types.ts 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/permission.ts 533 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/aes.js 505 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/http/axios/index.ts 462 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/libgif/libgif.js 990 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/public.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/form/index.vue 1218 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/page/list/basic/index.vue 282 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/DryEquipmentList.vue 320 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/DryHerbFormulaList.vue 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/DryHerbList.vue 320 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/DryHerbTypeList.vue 595 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/DryOrderList.vue 320 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/DryOrderTrendList.vue 320 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/api/DryEquipment.api.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/api/DryHerbFormula.api.ts 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryEquipmentForm.vue 118 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryEquipmentModal.vue 110 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryHerbForm.vue 118 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryHerbFormulaForm.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryHerbFormulaModal.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryHerbModal.vue 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryOrderForm.vue 118 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryOrderModal.vue 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/dataDefine/DryEquipment.data.ts 271 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/dataDefine/DryHerbFormula.data.ts 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/dataDefine/DryOrder.data.ts 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/monitor/WorkShop.vue 513 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/sql/DryHerbFormula_menu_insert.sql 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/departUser/components/DepartRoleAuthDrawer.vue 266 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/loginmini/MiniLogin.vue 1042 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
stylelint.config.js 129 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 190 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
windi.config.ts 130 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yarn.lock 12304 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintrc.js
@@ -1,78 +1,78 @@
// @ts-check
const { defineConfig } = require('eslint-define-config');
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
  root: true,
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser',
    ecmaVersion: 2020,
    sourceType: 'module',
    jsxPragma: 'React',
    ecmaFeatures: {
      jsx: true,
    },
  },
  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
    'plugin:prettier/recommended',
    'plugin:jest/recommended',
  ],
  rules: {
    'vue/script-setup-uses-vars': 'error',
    '@typescript-eslint/ban-ts-ignore': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-var-requires': 'off',
    '@typescript-eslint/no-empty-function': 'off',
    'vue/custom-event-name-casing': 'off',
    'no-use-before-define': 'off',
    '@typescript-eslint/no-use-before-define': 'off',
    '@typescript-eslint/ban-ts-comment': 'off',
    '@typescript-eslint/ban-types': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^_',
        varsIgnorePattern: '^_',
      },
    ],
    'no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^_',
        varsIgnorePattern: '^_',
      },
    ],
    'space-before-function-paren': 'off',
    root: true,
    env: {
        browser: true,
        node: true,
        es6: true,
    },
    parser: 'vue-eslint-parser',
    parserOptions: {
        parser: '@typescript-eslint/parser',
        ecmaVersion: 2020,
        sourceType: 'module',
        jsxPragma: 'React',
        ecmaFeatures: {
            jsx: true,
        },
    },
    extends: [
        'plugin:vue/vue3-recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier',
        'plugin:prettier/recommended',
        'plugin:jest/recommended',
    ],
    rules: {
        'vue/script-setup-uses-vars': 'error',
        '@typescript-eslint/ban-ts-ignore': 'off',
        '@typescript-eslint/explicit-function-return-type': 'off',
        '@typescript-eslint/no-explicit-any': 'off',
        '@typescript-eslint/no-var-requires': 'off',
        '@typescript-eslint/no-empty-function': 'off',
        'vue/custom-event-name-casing': 'off',
        'no-use-before-define': 'off',
        '@typescript-eslint/no-use-before-define': 'off',
        '@typescript-eslint/ban-ts-comment': 'off',
        '@typescript-eslint/ban-types': 'off',
        '@typescript-eslint/no-non-null-assertion': 'off',
        '@typescript-eslint/explicit-module-boundary-types': 'off',
        '@typescript-eslint/no-unused-vars': [
            'error',
            {
                argsIgnorePattern: '^_',
                varsIgnorePattern: '^_',
            },
        ],
        'no-unused-vars': [
            'error',
            {
                argsIgnorePattern: '^_',
                varsIgnorePattern: '^_',
            },
        ],
        'space-before-function-paren': 'off',
    'vue/attributes-order': 'off',
    'vue/one-component-per-file': 'off',
    'vue/html-closing-bracket-newline': 'off',
    'vue/max-attributes-per-line': 'off',
    'vue/multiline-html-element-content-newline': 'off',
    'vue/singleline-html-element-content-newline': 'off',
    'vue/attribute-hyphenation': 'off',
    'vue/require-default-prop': 'off',
    'vue/html-self-closing': [
      'error',
      {
        html: {
          void: 'always',
          normal: 'never',
          component: 'always',
        },
        svg: 'always',
        math: 'always',
      },
    ],
  },
});
        'vue/attributes-order': 'off',
        'vue/one-component-per-file': 'off',
        'vue/html-closing-bracket-newline': 'off',
        'vue/max-attributes-per-line': 'off',
        'vue/multiline-html-element-content-newline': 'off',
        'vue/singleline-html-element-content-newline': 'off',
        'vue/attribute-hyphenation': 'off',
        'vue/require-default-prop': 'off',
        'vue/html-self-closing': [
            'error',
            {
                html: {
                    void: 'always',
                    normal: 'never',
                    component: 'always',
                },
                svg: 'always',
                math: 'always',
            },
        ],
    },
})
LICENSE
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
MIT License
Copyright (c) 2020-present, Jeecg
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.
<developers>
  <developer>
    <name>北京敲敲云科技有限公司</name>
    <email>jeecgos@163.com</email>
  </developer>
</developers>
<scm>
  <connection>http://www.jeecg.com</connection>
  <developerConnection>https://qiaoqiaoyun.com</developerConnection>
  <url>http://www.jeecg.com/vip</url>
</scm>
commitlint.config.js
@@ -1,32 +1,16 @@
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'],
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'perf',
        'style',
        'docs',
        'test',
        'refactor',
        'build',
        'ci',
        'chore',
        'revert',
        'wip',
        'workflow',
        'types',
        'release',
      ],
    ],
  },
};
    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'],
        'type-enum': [
            2,
            'always',
            ['feat', 'fix', 'perf', 'style', 'docs', 'test', 'refactor', 'build', 'ci', 'chore', 'revert', 'wip', 'workflow', 'types', 'release'],
        ],
    },
}
npm
package.json
@@ -34,16 +34,16 @@
    "gen:icon": "esno ./build/generate/icon/index.ts"
  },
  "dependencies": {
    "@jeecg/online": "3.4.4-RC",
    "@iconify/iconify": "^2.2.1",
    "@ant-design/colors": "^6.0.0",
    "@ant-design/icons-vue": "^6.1.0",
    "@iconify/iconify": "^2.2.1",
    "@jeecg/online": "3.4.4-RC",
    "@logicflow/core": "^1.1.13",
    "@logicflow/extension": "^1.1.13",
    "@vue/shared": "^3.2.33",
    "@vue/runtime-core": "^3.2.33",
    "@vueuse/shared": "^8.3.0",
    "@vue/shared": "^3.2.33",
    "@vueuse/core": "^8.3.0",
    "@vueuse/shared": "^8.3.0",
    "@zxcvbn-ts/core": "^2.0.1",
    "ant-design-vue": "^3.2.12",
    "axios": "^0.26.1",
@@ -56,6 +56,7 @@
    "dayjs": "^1.11.1",
    "dom-align": "^1.12.2",
    "echarts": "^5.3.2",
    "echarts-liquidfill": "^3.1.0",
    "emoji-mart-vue-fast": "^11.1.1",
    "enquire.js": "^2.1.6",
    "intro.js": "^5.1.0",
@@ -66,11 +67,11 @@
    "nprogress": "^0.2.0",
    "path-to-regexp": "^6.2.0",
    "pinia": "2.0.12",
    "print-js": "^1.6.0",
    "pinyin-pro": "^3.11.0",
    "qs": "^6.10.3",
    "print-js": "^1.6.0",
    "qrcode": "^1.5.0",
    "qrcodejs2": "0.0.2",
    "qs": "^6.10.3",
    "resize-observer-polyfill": "^1.5.1",
    "showdown": "^2.1.0",
    "sortablejs": "^1.15.0",
@@ -81,6 +82,7 @@
    "vue-cropperjs": "^5.0.0",
    "vue-i18n": "^9.1.9",
    "vue-infinite-scroll": "^2.0.2",
    "vue-json-pretty": "^2.0.6",
    "vue-print-nb-jeecg": "^1.0.10",
    "vue-router": "^4.0.14",
    "vue-types": "^4.1.1",
@@ -88,7 +90,6 @@
    "vxe-table": "4.1.0",
    "vxe-table-plugin-antd": "3.0.5",
    "xe-utils": "^3.3.1",
    "vue-json-pretty": "^2.0.6",
    "xss": "^1.0.13"
  },
  "devDependencies": {
pnpm-lock.yaml
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
prettier.config.js
@@ -1,20 +1,20 @@
module.exports = {
  printWidth: 150,
  tabWidth: 2,
  useTabs: true,
  semi: false, //语句末尾使用分号
  vueIndentScriptAndStyle: true,
  singleQuote: true, // ä½¿ç”¨å•引号
  quoteProps: 'as-needed',
  bracketSpacing: true,
  trailingComma: 'es5',
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  arrowParens: 'always',
  insertPragma: false,
  requirePragma: false,
  proseWrap: 'never',
  htmlWhitespaceSensitivity: 'strict',
  endOfLine: 'auto',
  rangeStart: 0,
    printWidth: 150,
    tabWidth: 2,
    useTabs: true,
    semi: false, //语句末尾使用分号
    vueIndentScriptAndStyle: true,
    singleQuote: true, // ä½¿ç”¨å•引号
    quoteProps: 'as-needed',
    bracketSpacing: true,
    trailingComma: 'es5',
    jsxBracketSameLine: false,
    jsxSingleQuote: false,
    arrowParens: 'always',
    insertPragma: false,
    requirePragma: false,
    proseWrap: 'never',
    htmlWhitespaceSensitivity: 'strict',
    endOfLine: 'auto',
    rangeStart: 0,
}
src/App.vue
@@ -1,21 +1,21 @@
<template>
  <ConfigProvider :locale="getAntdLocale">
    <AppProvider>
      <RouterView />
    </AppProvider>
  </ConfigProvider>
    <ConfigProvider :locale="getAntdLocale">
        <AppProvider>
            <RouterView />
        </AppProvider>
    </ConfigProvider>
</template>
<script lang="ts" setup>
  import { ConfigProvider } from 'ant-design-vue';
  import { AppProvider } from '/@/components/Application';
  import { useTitle } from '/@/hooks/web/useTitle';
  import { useLocale } from '/@/locales/useLocale';
    import { ConfigProvider } from 'ant-design-vue'
    import { AppProvider } from '/@/components/Application'
    import { useTitle } from '/@/hooks/web/useTitle'
    import { useLocale } from '/@/locales/useLocale'
  // è§£å†³æ—¥æœŸæ—¶é—´å›½é™…化问题
  import 'dayjs/locale/zh-cn';
  // support Multi-language
  const { getAntdLocale } = useLocale();
    // è§£å†³æ—¥æœŸæ—¶é—´å›½é™…化问题
    import 'dayjs/locale/zh-cn'
    // support Multi-language
    const { getAntdLocale } = useLocale()
  useTitle();
    useTitle()
</script>
src/api/sys/menu.ts
@@ -1,9 +1,9 @@
import { defHttp } from '/@/utils/http/axios';
import { getMenuListResultModel } from './model/menuModel';
import { getMenuListResultModel } from './model/menuModel'
import { defHttp } from '/@/utils/http/axios'
enum Api {
  GetMenuList = '/sys/permission/getUserPermissionByToken',
  SwitchVue3Menu = '/sys/switchVue3Menu',
    GetMenuList = '/sys/permission/getUserPermissionByToken',
    SwitchVue3Menu = '/sys/switchVue3Menu',
}
/**
@@ -11,23 +11,23 @@
 */
export const getMenuList = () => {
  return new Promise((resolve) => {
    //为了兼容mock和接口数据
    defHttp.get<getMenuListResultModel>({ url: Api.GetMenuList }).then((res) => {
      if (Array.isArray(res)) {
        resolve(res);
      } else {
        resolve(res['menu']);
      }
    });
  });
};
    return new Promise((resolve) => {
        //为了兼容mock和接口数据
        defHttp.get<getMenuListResultModel>({ url: Api.GetMenuList }).then((res) => {
            if (Array.isArray(res)) {
                resolve(res)
            } else {
                resolve(res['menu'])
            }
        })
    })
}
/**
 * åˆ‡æ¢æˆvue3菜单
 */
export const switchVue3Menu = () => {
  return new Promise((resolve) => {
    defHttp.get({ url: Api.SwitchVue3Menu });
  });
};
    return new Promise((resolve) => {
        defHttp.get({ url: Api.SwitchVue3Menu })
    })
}
src/api/sys/model/menuModel.ts
@@ -1,16 +1,16 @@
import type { RouteMeta } from 'vue-router';
import type { RouteMeta } from 'vue-router'
export interface RouteItem {
  path: string;
  component: any;
  meta: RouteMeta;
  name?: string;
  alias?: string | string[];
  redirect?: string;
  caseSensitive?: boolean;
  children?: RouteItem[];
    path: string
    component: any
    meta: RouteMeta
    name?: string
    alias?: string | string[]
    redirect?: string
    caseSensitive?: boolean
    children?: RouteItem[]
}
/**
 * @description: Get menu return value
 */
export type getMenuListResultModel = RouteItem[];
export type getMenuListResultModel = RouteItem[]
src/assets/images/dry/ganzaoji-x.png
src/assets/images/dry/ganzaoji-z.png
src/assets/images/dry/ganzaoji.gif
src/assets/images/dry/ganzaoji.png
src/components/Button/src/BasicButton.vue
@@ -1,41 +1,41 @@
<template>
  <Button v-bind="getBindValue" :class="getButtonClass" @click="onClick">
    <template v-if="preIcon" #icon>
      <Icon :icon="preIcon" :size="iconSize" />
    </template>
    <template #default="data">
      <slot v-bind="data || {}"></slot>
      <Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
    </template>
  </Button>
    <Button v-bind="getBindValue" :class="getButtonClass" @click="onClick">
        <template v-if="preIcon" #icon>
            <Icon :icon="preIcon" :size="iconSize" />
        </template>
        <template #default="data">
            <slot v-bind="data || {}"></slot>
            <Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
        </template>
    </Button>
</template>
<script lang="ts">
  import { defineComponent } from 'vue';
  export default defineComponent({
    name: 'AButton',
    inheritAttrs: false,
  });
    import { defineComponent } from 'vue'
    export default defineComponent({
        name: 'AButton',
        inheritAttrs: false,
    })
</script>
<script lang="ts" setup>
  import { computed, unref } from 'vue';
  import { Button } from 'ant-design-vue';
  import Icon from '/@/components/Icon/src/Icon.vue';
  import { buttonProps } from './props';
  import { useAttrs } from '/@/hooks/core/useAttrs';
  const props = defineProps(buttonProps);
  // get component class
  const attrs = useAttrs({ excludeDefaultKeys: false });
  const getButtonClass = computed(() => {
    const { color, disabled } = props;
    return [
      {
        [`ant-btn-${color}`]: !!color,
        [`is-disabled`]: disabled,
      },
    ];
  });
    import { computed, unref } from 'vue'
    import { Button } from 'ant-design-vue'
    import Icon from '/@/components/Icon/src/Icon.vue'
    import { buttonProps } from './props'
    import { useAttrs } from '/@/hooks/core/useAttrs'
    const props = defineProps(buttonProps)
    // get component class
    const attrs = useAttrs({ excludeDefaultKeys: false })
    const getButtonClass = computed(() => {
        const { color, disabled } = props
        return [
            {
                [`ant-btn-${color}`]: !!color,
                [`is-disabled`]: disabled,
            },
        ]
    })
  // get inherit binding value
  const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
    // get inherit binding value
    const getBindValue = computed(() => ({ ...unref(attrs), ...props }))
</script>
src/enums/pageEnum.ts
@@ -1,14 +1,14 @@
export enum PageEnum {
  // basic login path
  BASE_LOGIN = '/login',
  // basic home path
  BASE_HOME = '/dashboard/analysis',
  // error page path
  ERROR_PAGE = '/exception',
  // error log page path
  ERROR_LOG_PAGE = '/error-log/list',
  // auth2登录路由路径
  OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login',
  //文件路由
  SYS_FILES_PATH = '/file/share',
    // basic login path
    BASE_LOGIN = '/login',
    // basic home path
    BASE_HOME = '/dashboard/workshop',
    // error page path
    ERROR_PAGE = '/exception',
    // error log page path
    ERROR_LOG_PAGE = '/error-log/list',
    // auth2登录路由路径
    OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login',
    //文件路由
    SYS_FILES_PATH = '/file/share',
}
src/layouts/default/header/components/user-dropdown/index.vue
@@ -1,233 +1,233 @@
<template>
  <Dropdown placement="bottomLeft" :overlayClassName="`${prefixCls}-dropdown-overlay`">
    <span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
      <img :class="`${prefixCls}__header`" :src="getAvatarUrl" />
      <span :class="`${prefixCls}__info hidden md:block`">
        <span :class="`${prefixCls}__name  `" class="truncate">
          {{ getUserInfo.realname }}
        </span>
      </span>
    </span>
    <Dropdown placement="bottomLeft" :overlayClassName="`${prefixCls}-dropdown-overlay`">
        <span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
            <img :class="`${prefixCls}__header`" :src="getAvatarUrl" />
            <span :class="`${prefixCls}__info hidden md:block`">
                <span :class="`${prefixCls}__name  `" class="truncate">
                    {{ getUserInfo.realname }}
                </span>
            </span>
        </span>
    <template #overlay>
      <Menu @click="handleMenuClick">
        <MenuItem key="doc" :text="t('layout.header.dropdownItemDoc')" icon="ion:document-text-outline" v-if="getShowDoc" />
        <MenuDivider v-if="getShowDoc" />
        <MenuItem key="account" :text="t('layout.header.dropdownItemSwitchAccount')" icon="ant-design:setting-outlined" />
        <MenuItem key="password" :text="t('layout.header.dropdownItemSwitchPassword')" icon="ant-design:edit-outlined" />
        <MenuItem key="depart" :text="t('layout.header.dropdownItemSwitchDepart')" icon="ant-design:cluster-outlined" />
        <MenuItem key="cache" :text="t('layout.header.dropdownItemRefreshCache')" icon="ion:sync-outline" />
        <!-- <MenuItem
        <template #overlay>
            <Menu @click="handleMenuClick">
                <!-- <MenuItem key="doc" :text="t('layout.header.dropdownItemDoc')" icon="ion:document-text-outline" v-if="getShowDoc" /> -->
                <MenuDivider v-if="getShowDoc" />
                <MenuItem key="account" :text="t('layout.header.dropdownItemSwitchAccount')" icon="ant-design:setting-outlined" />
                <MenuItem key="password" :text="t('layout.header.dropdownItemSwitchPassword')" icon="ant-design:edit-outlined" />
                <MenuItem key="depart" :text="t('layout.header.dropdownItemSwitchDepart')" icon="ant-design:cluster-outlined" />
                <MenuItem key="cache" :text="t('layout.header.dropdownItemRefreshCache')" icon="ion:sync-outline" />
                <!-- <MenuItem
            v-if="getUseLockPage"
            key="lock"
            :text="t('layout.header.tooltipLock')"
            icon="ion:lock-closed-outline"
        />-->
        <MenuItem key="logout" :text="t('layout.header.dropdownItemLoginOut')" icon="ion:power-outline" />
      </Menu>
    </template>
  </Dropdown>
  <LockAction @register="register" />
  <DepartSelect ref="loginSelectRef" />
  <UpdatePassword ref="updatePasswordRef" />
                <MenuItem key="logout" :text="t('layout.header.dropdownItemLoginOut')" icon="ion:power-outline" />
            </Menu>
        </template>
    </Dropdown>
    <LockAction @register="register" />
    <DepartSelect ref="loginSelectRef" />
    <UpdatePassword ref="updatePasswordRef" />
</template>
<script lang="ts">
  // components
  import { Dropdown, Menu } from 'ant-design-vue';
    // components
    import { Dropdown, Menu } from 'ant-design-vue'
  import { defineComponent, computed, ref } from 'vue';
    import { defineComponent, computed, ref } from 'vue'
  import { SITE_URL } from '/@/settings/siteSetting';
    import { SITE_URL } from '/@/settings/siteSetting'
  import { useUserStore } from '/@/store/modules/user';
  import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { useModal } from '/@/components/Modal';
  import { useMessage } from '/src/hooks/web/useMessage';
  import { useGo } from '/@/hooks/web/usePage';
  import headerImg from '/@/assets/images/header.jpg';
  import { propTypes } from '/@/utils/propTypes';
  import { openWindow } from '/@/utils';
    import { useUserStore } from '/@/store/modules/user'
    import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'
    import { useI18n } from '/@/hooks/web/useI18n'
    import { useDesign } from '/@/hooks/web/useDesign'
    import { useModal } from '/@/components/Modal'
    import { useMessage } from '/src/hooks/web/useMessage'
    import { useGo } from '/@/hooks/web/usePage'
    import headerImg from '/@/assets/images/header.jpg'
    import { propTypes } from '/@/utils/propTypes'
    import { openWindow } from '/@/utils'
  import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
    import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'
  import { refreshCache, queryAllDictItems } from '/@/views/system/dict/dict.api';
  import { DB_DICT_DATA_KEY } from '/src/enums/cacheEnum';
  import { removeAuthCache, setAuthCache } from '/src/utils/auth';
  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
    import { refreshCache, queryAllDictItems } from '/@/views/system/dict/dict.api'
    import { DB_DICT_DATA_KEY } from '/src/enums/cacheEnum'
    import { removeAuthCache, setAuthCache } from '/src/utils/auth'
    import { getFileAccessHttpUrl } from '/@/utils/common/compUtils'
  type MenuEvent = 'logout' | 'doc' | 'lock' | 'cache' | 'depart';
  const { createMessage } = useMessage();
  export default defineComponent({
    name: 'UserDropdown',
    components: {
      Dropdown,
      Menu,
      MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
      MenuDivider: Menu.Divider,
      LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')),
      DepartSelect: createAsyncComponent(() => import('./DepartSelect.vue')),
      UpdatePassword: createAsyncComponent(() => import('./UpdatePassword.vue')),
    },
    props: {
      theme: propTypes.oneOf(['dark', 'light']),
    },
    setup() {
      const { prefixCls } = useDesign('header-user-dropdown');
      const { t } = useI18n();
      const { getShowDoc, getUseLockPage } = useHeaderSetting();
      const userStore = useUserStore();
      const go = useGo();
    type MenuEvent = 'logout' | 'doc' | 'lock' | 'cache' | 'depart'
    const { createMessage } = useMessage()
    export default defineComponent({
        name: 'UserDropdown',
        components: {
            Dropdown,
            Menu,
            MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
            MenuDivider: Menu.Divider,
            LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')),
            DepartSelect: createAsyncComponent(() => import('./DepartSelect.vue')),
            UpdatePassword: createAsyncComponent(() => import('./UpdatePassword.vue')),
        },
        props: {
            theme: propTypes.oneOf(['dark', 'light']),
        },
        setup() {
            const { prefixCls } = useDesign('header-user-dropdown')
            const { t } = useI18n()
            const { getShowDoc, getUseLockPage } = useHeaderSetting()
            const userStore = useUserStore()
            const go = useGo()
      const getUserInfo = computed(() => {
        const { realname = '', avatar, desc } = userStore.getUserInfo || {};
        return { realname, avatar: avatar || headerImg, desc };
      });
            const getUserInfo = computed(() => {
                const { realname = '', avatar, desc } = userStore.getUserInfo || {}
                return { realname, avatar: avatar || headerImg, desc }
            })
      const getAvatarUrl = computed(() => {
        let { avatar } = getUserInfo.value;
        if (avatar == headerImg) {
          return avatar;
        } else {
          return getFileAccessHttpUrl(avatar);
        }
      });
            const getAvatarUrl = computed(() => {
                let { avatar } = getUserInfo.value
                if (avatar == headerImg) {
                    return avatar
                } else {
                    return getFileAccessHttpUrl(avatar)
                }
            })
      const [register, { openModal }] = useModal();
      /**
       * å¤šéƒ¨é—¨å¼¹çª—逻辑
       */
      const loginSelectRef = ref();
      function handleLock() {
        openModal(true);
      }
            const [register, { openModal }] = useModal()
            /**
             * å¤šéƒ¨é—¨å¼¹çª—逻辑
             */
            const loginSelectRef = ref()
            function handleLock() {
                openModal(true)
            }
      //  login out
      function handleLoginOut() {
        userStore.confirmLoginOut();
      }
            //  login out
            function handleLoginOut() {
                userStore.confirmLoginOut()
            }
      // open doc
      function openDoc() {
        openWindow(SITE_URL);
      }
            // open doc
            function openDoc() {
                openWindow(SITE_URL)
            }
      // æ¸…除缓存
      async function clearCache() {
        const result = await refreshCache();
        if (result.success) {
          const res = await queryAllDictItems();
          removeAuthCache(DB_DICT_DATA_KEY);
          setAuthCache(DB_DICT_DATA_KEY, res.result);
          createMessage.success('刷新缓存完成!');
        } else {
          createMessage.error('刷新缓存失败!');
        }
      }
      // åˆ‡æ¢éƒ¨é—¨
      function updateCurrentDepart() {
        loginSelectRef.value.show();
      }
      // ä¿®æ”¹å¯†ç 
      const updatePasswordRef = ref();
      function updatePassword() {
        updatePasswordRef.value.show(userStore.getUserInfo.username);
      }
            // æ¸…除缓存
            async function clearCache() {
                const result = await refreshCache()
                if (result.success) {
                    const res = await queryAllDictItems()
                    removeAuthCache(DB_DICT_DATA_KEY)
                    setAuthCache(DB_DICT_DATA_KEY, res.result)
                    createMessage.success('刷新缓存完成!')
                } else {
                    createMessage.error('刷新缓存失败!')
                }
            }
            // åˆ‡æ¢éƒ¨é—¨
            function updateCurrentDepart() {
                loginSelectRef.value.show()
            }
            // ä¿®æ”¹å¯†ç 
            const updatePasswordRef = ref()
            function updatePassword() {
                updatePasswordRef.value.show(userStore.getUserInfo.username)
            }
      function handleMenuClick(e: { key: MenuEvent }) {
        switch (e.key) {
          case 'logout':
            handleLoginOut();
            break;
          case 'doc':
            openDoc();
            break;
          case 'lock':
            handleLock();
            break;
          case 'cache':
            clearCache();
            break;
          case 'depart':
            updateCurrentDepart();
            break;
          case 'password':
            updatePassword();
            break;
          case 'account':
            //update-begin---author:wangshuai ---date:20221125  for:进入用户设置页面------------
            go(`/system/usersetting`);
            //update-end---author:wangshuai ---date:20221125  for:进入用户设置页面--------------
            break;
        }
      }
            function handleMenuClick(e: { key: MenuEvent }) {
                switch (e.key) {
                    case 'logout':
                        handleLoginOut()
                        break
                    case 'doc':
                        openDoc()
                        break
                    case 'lock':
                        handleLock()
                        break
                    case 'cache':
                        clearCache()
                        break
                    case 'depart':
                        updateCurrentDepart()
                        break
                    case 'password':
                        updatePassword()
                        break
                    case 'account':
                        //update-begin---author:wangshuai ---date:20221125  for:进入用户设置页面------------
                        go(`/system/usersetting`)
                        //update-end---author:wangshuai ---date:20221125  for:进入用户设置页面--------------
                        break
                }
            }
      return {
        prefixCls,
        t,
        getUserInfo,
        getAvatarUrl,
        handleMenuClick,
        getShowDoc,
        register,
        getUseLockPage,
        loginSelectRef,
        updatePasswordRef,
      };
    },
  });
            return {
                prefixCls,
                t,
                getUserInfo,
                getAvatarUrl,
                handleMenuClick,
                getShowDoc,
                register,
                getUseLockPage,
                loginSelectRef,
                updatePasswordRef,
            }
        },
    })
</script>
<style lang="less">
  @prefix-cls: ~'@{namespace}-header-user-dropdown';
    @prefix-cls: ~'@{namespace}-header-user-dropdown';
  .@{prefix-cls} {
    height: @header-height;
    padding: 0 0 0 10px;
    padding-right: 10px;
    overflow: hidden;
    font-size: 12px;
    cursor: pointer;
    align-items: center;
    .@{prefix-cls} {
        height: @header-height;
        padding: 0 0 0 10px;
        padding-right: 10px;
        overflow: hidden;
        font-size: 12px;
        cursor: pointer;
        align-items: center;
    img {
      width: 24px;
      height: 24px;
      margin-right: 12px;
    }
        img {
            width: 24px;
            height: 24px;
            margin-right: 12px;
        }
    &__header {
      border-radius: 50%;
    }
        &__header {
            border-radius: 50%;
        }
    &__name {
      font-size: 14px;
    }
        &__name {
            font-size: 14px;
        }
    &--dark {
      &:hover {
        background-color: @header-dark-bg-hover-color;
      }
    }
        &--dark {
            &:hover {
                background-color: @header-dark-bg-hover-color;
            }
        }
    &--light {
      &:hover {
        background-color: @header-light-bg-hover-color;
      }
        &--light {
            &:hover {
                background-color: @header-light-bg-hover-color;
            }
      .@{prefix-cls}__name {
        color: @text-color-base;
      }
            .@{prefix-cls}__name {
                color: @text-color-base;
            }
      .@{prefix-cls}__desc {
        color: @header-light-desc-color;
      }
    }
            .@{prefix-cls}__desc {
                color: @header-light-desc-color;
            }
        }
    &-dropdown-overlay {
      .ant-dropdown-menu-item {
        min-width: 160px;
      }
    }
  }
        &-dropdown-overlay {
            .ant-dropdown-menu-item {
                min-width: 160px;
            }
        }
    }
</style>
src/locales/lang/en/layout.ts
@@ -1,122 +1,122 @@
export default {
  footer: { onlinePreview: 'Preview', onlineDocument: 'Document' },
  header: {
    // user dropdown
    dropdownItemDoc: 'Document',
    dropdownItemLoginOut: 'Login Out',
    dropdownItemSwitchPassword: 'Password Change',
    dropdownItemSwitchDepart: 'Switch Department',
    dropdownItemRefreshCache: 'Clean cache',
    dropdownItemSwitchAccount: 'Account Setting',
    tooltipErrorLog: 'Error log',
    tooltipLock: 'Lock screen',
    tooltipNotify: 'Notification',
    footer: { onlinePreview: 'Preview', onlineDocument: 'Document' },
    header: {
        // user dropdown
        dropdownItemDoc: 'Document',
        dropdownItemLoginOut: 'Login Out',
        dropdownItemSwitchPassword: 'Password Change',
        dropdownItemSwitchDepart: 'Switch Department',
        dropdownItemRefreshCache: 'Clean cache',
        dropdownItemSwitchAccount: 'Account Setting',
    tooltipEntryFull: 'Full Screen',
    tooltipExitFull: 'Exit Full Screen',
        tooltipErrorLog: 'Error log',
        tooltipLock: 'Lock screen',
        tooltipNotify: 'Notification',
    // lock
    lockScreenPassword: 'Lock screen password',
    lockScreen: 'Lock screen',
    lockScreenBtn: 'Locking',
        tooltipEntryFull: 'Full Screen',
        tooltipExitFull: 'Exit Full Screen',
    home: 'Home',
  },
  multipleTab: {
    reload: 'Refresh current',
    close: 'Close current',
    closeLeft: 'Close Left',
    closeRight: 'Close Right',
    closeOther: 'Close Other',
    closeAll: 'Close All',
  },
  setting: {
    // content mode
    contentModeFull: 'Full',
    contentModeFixed: 'Fixed width',
    // topMenu align
    topMenuAlignLeft: 'Left',
    topMenuAlignRight: 'Center',
    topMenuAlignCenter: 'Right',
    // menu trigger
    menuTriggerNone: 'Not Show',
    menuTriggerBottom: 'Bottom',
    menuTriggerTop: 'Top',
    // menu type
    menuTypeSidebar: 'Left menu mode',
    menuTypeMixSidebar: 'Left menu mixed mode',
    menuTypeMix: 'Top Menu Mix mode',
    menuTypeTopMenu: 'Top menu mode',
        // lock
        lockScreenPassword: 'Lock screen password',
        lockScreen: 'Lock screen',
        lockScreenBtn: 'Locking',
    on: 'On',
    off: 'Off',
    minute: 'Minute',
        home: 'Home',
    },
    multipleTab: {
        reload: 'Refresh current',
        close: 'Close current',
        closeLeft: 'Close Left',
        closeRight: 'Close Right',
        closeOther: 'Close Other',
        closeAll: 'Close All',
    },
    setting: {
        // content mode
        contentModeFull: 'Full',
        contentModeFixed: 'Fixed width',
        // topMenu align
        topMenuAlignLeft: 'Left',
        topMenuAlignRight: 'Center',
        topMenuAlignCenter: 'Right',
        // menu trigger
        menuTriggerNone: 'Not Show',
        menuTriggerBottom: 'Bottom',
        menuTriggerTop: 'Top',
        // menu type
        menuTypeSidebar: 'Left menu mode',
        menuTypeMixSidebar: 'Left menu mixed mode',
        menuTypeMix: 'Top Menu Mix mode',
        menuTypeTopMenu: 'Top menu mode',
    operatingTitle: 'Successful!',
    operatingContent: 'The copy is successful, please go to src/settings/projectSetting.ts to modify the configuration!',
    resetSuccess: 'Successfully reset!',
        on: 'On',
        off: 'Off',
        minute: 'Minute',
    copyBtn: 'Copy',
    clearBtn: 'Clear cache and to the login page',
        operatingTitle: 'Successful!',
        operatingContent: 'The copy is successful, please go to src/settings/projectSetting.ts to modify the configuration!',
        resetSuccess: 'Successfully reset!',
    drawerTitle: 'Configuration',
        copyBtn: 'Copy',
        clearBtn: 'Clear cache and to the login page',
    darkMode: 'Dark mode',
    navMode: 'Navigation mode',
    interfaceFunction: 'Interface function',
    interfaceDisplay: 'Interface display',
    animation: 'Animation',
    splitMenu: 'Split menu',
    closeMixSidebarOnChange: 'Switch page to close menu',
        drawerTitle: 'Configuration',
    sysTheme: 'System theme',
    headerTheme: 'Header theme',
    sidebarTheme: 'Menu theme',
        darkMode: 'Dark mode',
        navMode: 'Navigation mode',
        interfaceFunction: 'Interface function',
        interfaceDisplay: 'Interface display',
        animation: 'Animation',
        splitMenu: 'Split menu',
        closeMixSidebarOnChange: 'Switch page to close menu',
    menuDrag: 'Drag Sidebar',
    menuSearch: 'Menu search',
    menuAccordion: 'Sidebar accordion',
    menuCollapse: 'Collapse menu',
    collapseMenuDisplayName: 'Collapse menu display name',
    topMenuLayout: 'Top menu layout',
    menuCollapseButton: 'Menu collapse button',
    contentMode: 'Content area width',
    expandedMenuWidth: 'Expanded menu width',
        sysTheme: 'System theme',
        headerTheme: 'Header theme',
        sidebarTheme: 'Menu theme',
    breadcrumb: 'Breadcrumbs',
    breadcrumbIcon: 'Breadcrumbs Icon',
    tabs: 'Tabs',
    tabDetail: 'Tab Detail',
    tabsQuickBtn: 'Tabs quick button',
    tabsRedoBtn: 'Tabs redo button',
    tabsFoldBtn: 'Tabs flod button',
    tabsTheme: 'tabs theme',
    tabsThemeSmooth: 'Smooth',
    tabsThemeCard: 'Card',
    tabsThemeSimple: 'Simple',
    sidebar: 'Sidebar',
    header: 'Header',
    footer: 'Footer',
    fullContent: 'Full content',
    grayMode: 'Gray mode',
    colorWeak: 'Color Weak Mode',
        menuDrag: 'Drag Sidebar',
        menuSearch: 'Menu search',
        menuAccordion: 'Sidebar accordion',
        menuCollapse: 'Collapse menu',
        collapseMenuDisplayName: 'Collapse menu display name',
        topMenuLayout: 'Top menu layout',
        menuCollapseButton: 'Menu collapse button',
        contentMode: 'Content area width',
        expandedMenuWidth: 'Expanded menu width',
    progress: 'Progress',
    switchLoading: 'Switch Loading',
    switchAnimation: 'Switch animation',
    animationType: 'Animation type',
        breadcrumb: 'Breadcrumbs',
        breadcrumbIcon: 'Breadcrumbs Icon',
        tabs: 'Tabs',
        tabDetail: 'Tab Detail',
        tabsQuickBtn: 'Tabs quick button',
        tabsRedoBtn: 'Tabs redo button',
        tabsFoldBtn: 'Tabs flod button',
        tabsTheme: 'tabs theme',
        tabsThemeSmooth: 'Smooth',
        tabsThemeCard: 'Card',
        tabsThemeSimple: 'Simple',
        sidebar: 'Sidebar',
        header: 'Header',
        footer: 'Footer',
        fullContent: 'Full content',
        grayMode: 'Gray mode',
        colorWeak: 'Color Weak Mode',
    autoScreenLock: 'Auto screen lock',
    notAutoScreenLock: 'Not auto lock',
        progress: 'Progress',
        switchLoading: 'Switch Loading',
        switchAnimation: 'Switch animation',
        animationType: 'Animation type',
    fixedHeader: 'Fixed header',
    fixedSideBar: 'Fixed Sidebar',
        autoScreenLock: 'Auto screen lock',
        notAutoScreenLock: 'Not auto lock',
    mixSidebarTrigger: 'Mixed menu Trigger',
    triggerHover: 'Hover',
    triggerClick: 'Click',
        fixedHeader: 'Fixed header',
        fixedSideBar: 'Fixed Sidebar',
    mixSidebarFixed: 'Fixed expanded menu',
  },
};
        mixSidebarTrigger: 'Mixed menu Trigger',
        triggerHover: 'Hover',
        triggerClick: 'Click',
        mixSidebarFixed: 'Fixed expanded menu',
    },
}
src/locales/lang/zh-CN/layout.ts
@@ -1,123 +1,123 @@
export default {
  footer: { onlinePreview: 'JEECG首页', onlineDocument: '在线文档' },
  header: {
    // user dropdown
    dropdownItemDoc: '官网',
    dropdownItemLoginOut: '退出系统',
    dropdownItemSwitchPassword: '密码修改',
    dropdownItemSwitchDepart: '切换部门',
    dropdownItemRefreshCache: '刷新缓存',
    dropdownItemSwitchAccount: '账户设置',
    footer: { onlinePreview: 'JEECG首页', onlineDocument: '在线文档' },
    header: {
        // user dropdown
        dropdownItemDoc: '官网',
        dropdownItemLoginOut: '退出系统',
        dropdownItemSwitchPassword: '密码修改',
        dropdownItemSwitchDepart: '切换部门',
        dropdownItemRefreshCache: '刷新缓存',
        dropdownItemSwitchAccount: '账户设置',
    // tooltip
    tooltipErrorLog: '错误日志',
    tooltipLock: '锁定屏幕',
    tooltipNotify: '消息通知',
        // tooltip
        tooltipErrorLog: '错误日志',
        tooltipLock: '锁定屏幕',
        tooltipNotify: '消息通知',
    tooltipEntryFull: '全屏',
    tooltipExitFull: '退出全屏',
        tooltipEntryFull: '全屏',
        tooltipExitFull: '退出全屏',
    // lock
    lockScreenPassword: '锁屏密码',
    lockScreen: '锁定屏幕',
    lockScreenBtn: '锁定',
        // lock
        lockScreenPassword: '锁屏密码',
        lockScreen: '锁定屏幕',
        lockScreenBtn: '锁定',
    home: '首页',
  },
  multipleTab: {
    reload: '重新加载',
    close: '关闭当前',
    closeLeft: '关闭左侧',
    closeRight: '关闭右侧',
    closeOther: '关闭其它',
    closeAll: '关闭全部',
  },
  setting: {
    // content mode
    contentModeFull: '流式',
    contentModeFixed: '定宽',
    // topMenu align
    topMenuAlignLeft: '居左',
    topMenuAlignRight: '居中',
    topMenuAlignCenter: '居右',
    // menu trigger
    menuTriggerNone: '不显示',
    menuTriggerBottom: '底部',
    menuTriggerTop: '顶部',
    // menu type
    menuTypeSidebar: '左侧菜单模式',
    menuTypeMixSidebar: '左侧菜单混合模式',
    menuTypeMix: '顶部菜单混合模式',
    menuTypeTopMenu: '顶部菜单模式',
        home: '首页',
    },
    multipleTab: {
        reload: '重新加载',
        close: '关闭当前',
        closeLeft: '关闭左侧',
        closeRight: '关闭右侧',
        closeOther: '关闭其它',
        closeAll: '关闭全部',
    },
    setting: {
        // content mode
        contentModeFull: '流式',
        contentModeFixed: '定宽',
        // topMenu align
        topMenuAlignLeft: '居左',
        topMenuAlignRight: '居中',
        topMenuAlignCenter: '居右',
        // menu trigger
        menuTriggerNone: '不显示',
        menuTriggerBottom: '底部',
        menuTriggerTop: '顶部',
        // menu type
        menuTypeSidebar: '左侧菜单模式',
        menuTypeMixSidebar: '左侧菜单混合模式',
        menuTypeMix: '顶部菜单混合模式',
        menuTypeTopMenu: '顶部菜单模式',
    on: '开',
    off: '关',
    minute: '分钟',
        on: '开',
        off: '关',
        minute: '分钟',
    operatingTitle: '操作成功',
    operatingContent: '复制成功,请到 src/settings/projectSetting.ts ä¸­ä¿®æ”¹é…ç½®ï¼',
    resetSuccess: '重置成功!',
        operatingTitle: '操作成功',
        operatingContent: '复制成功,请到 src/settings/projectSetting.ts ä¸­ä¿®æ”¹é…ç½®ï¼',
        resetSuccess: '重置成功!',
    copyBtn: '拷贝',
    clearBtn: '清空缓存并返回登录页',
        copyBtn: '拷贝',
        clearBtn: '清空缓存并返回登录页',
    drawerTitle: '项目配置',
        drawerTitle: '项目配置',
    darkMode: '主题',
    navMode: '导航栏模式',
    interfaceFunction: '界面设置',
    interfaceDisplay: '界面显示',
    animation: '动画',
    splitMenu: '分割菜单',
    closeMixSidebarOnChange: '切换页面关闭菜单',
        darkMode: '主题',
        navMode: '导航栏模式',
        interfaceFunction: '界面设置',
        interfaceDisplay: '界面显示',
        animation: '动画',
        splitMenu: '分割菜单',
        closeMixSidebarOnChange: '切换页面关闭菜单',
    sysTheme: '系统主题',
    headerTheme: '顶栏主题',
    sidebarTheme: '菜单主题',
        sysTheme: '系统主题',
        headerTheme: '顶栏主题',
        sidebarTheme: '菜单主题',
    menuDrag: '侧边菜单拖拽',
    menuSearch: '菜单搜索',
    menuAccordion: '侧边菜单手风琴模式',
    menuCollapse: '折叠菜单',
    collapseMenuDisplayName: '折叠菜单显示名称',
    topMenuLayout: '顶部菜单布局',
    menuCollapseButton: '菜单折叠按钮',
    contentMode: '内容区域宽度',
    expandedMenuWidth: '菜单展开宽度',
        menuDrag: '侧边菜单拖拽',
        menuSearch: '菜单搜索',
        menuAccordion: '侧边菜单手风琴模式',
        menuCollapse: '折叠菜单',
        collapseMenuDisplayName: '折叠菜单显示名称',
        topMenuLayout: '顶部菜单布局',
        menuCollapseButton: '菜单折叠按钮',
        contentMode: '内容区域宽度',
        expandedMenuWidth: '菜单展开宽度',
    breadcrumb: '面包屑',
    breadcrumbIcon: '面包屑图标',
    tabs: '标签页',
    tabDetail: '标签详情页',
    tabsQuickBtn: '标签页快捷按钮',
    tabsRedoBtn: '标签页刷新按钮',
    tabsFoldBtn: '标签页折叠按钮',
    tabsTheme: '标签页样式',
    tabsThemeSmooth: '圆滑',
    tabsThemeCard: '卡片',
    tabsThemeSimple: '极简',
    sidebar: '左侧菜单',
    header: '顶栏',
    footer: '页脚',
    fullContent: '全屏内容',
    grayMode: '灰色模式',
    colorWeak: '色弱模式',
        breadcrumb: '面包屑',
        breadcrumbIcon: '面包屑图标',
        tabs: '标签页',
        tabDetail: '标签详情页',
        tabsQuickBtn: '标签页快捷按钮',
        tabsRedoBtn: '标签页刷新按钮',
        tabsFoldBtn: '标签页折叠按钮',
        tabsTheme: '标签页样式',
        tabsThemeSmooth: '圆滑',
        tabsThemeCard: '卡片',
        tabsThemeSimple: '极简',
        sidebar: '左侧菜单',
        header: '顶栏',
        footer: '页脚',
        fullContent: '全屏内容',
        grayMode: '灰色模式',
        colorWeak: '色弱模式',
    progress: '顶部进度条',
    switchLoading: '切换loading',
    switchAnimation: '切换动画',
    animationType: '动画类型',
        progress: '顶部进度条',
        switchLoading: '切换loading',
        switchAnimation: '切换动画',
        animationType: '动画类型',
    autoScreenLock: '自动锁屏',
    notAutoScreenLock: '不自动锁屏',
        autoScreenLock: '自动锁屏',
        notAutoScreenLock: '不自动锁屏',
    fixedHeader: '固定header',
    fixedSideBar: '固定Sidebar',
        fixedHeader: '固定header',
        fixedSideBar: '固定Sidebar',
    mixSidebarTrigger: '混合菜单触发方式',
    triggerHover: '悬停',
    triggerClick: '点击',
        mixSidebarTrigger: '混合菜单触发方式',
        triggerHover: '悬停',
        triggerClick: '点击',
    mixSidebarFixed: '固定展开菜单',
  },
};
        mixSidebarFixed: '固定展开菜单',
    },
}
src/main.ts
@@ -1,72 +1,72 @@
import '/@/design/index.less';
import '/@/design/index.less'
// æ³¨å†Œ windi
import 'virtual:windi-base.css';
import 'virtual:windi-components.css';
import 'virtual:windi-utilities.css';
import 'virtual:windi-devtools';
import 'virtual:windi-base.css'
import 'virtual:windi-components.css'
import 'virtual:windi-devtools'
import 'virtual:windi-utilities.css'
// æ³¨å†Œå›¾æ ‡
import 'virtual:svg-icons-register';
import App from './App.vue';
import { createApp } from 'vue';
import { initAppConfigStore } from '/@/logics/initAppConfig';
import { setupErrorHandle } from '/@/logics/error-handle';
import { router, setupRouter } from '/@/router';
import { setupRouterGuard } from '/@/router/guard';
import { setupStore } from '/@/store';
import { setupGlobDirectives } from '/@/directives';
import { setupI18n } from '/@/locales/setupI18n';
import { registerGlobComp } from '/@/components/registerGlobComp';
import { registerThirdComp } from '/@/settings/registerThirdComp';
import { useSso } from '/@/hooks/web/useSso';
import 'virtual:svg-icons-register'
import { createApp } from 'vue'
import App from './App.vue'
import { registerGlobComp } from '/@/components/registerGlobComp'
import { setupGlobDirectives } from '/@/directives'
import { useSso } from '/@/hooks/web/useSso'
import { setupI18n } from '/@/locales/setupI18n'
import { setupErrorHandle } from '/@/logics/error-handle'
import { initAppConfigStore } from '/@/logics/initAppConfig'
import { router, setupRouter } from '/@/router'
import { setupRouterGuard } from '/@/router/guard'
import { registerThirdComp } from '/@/settings/registerThirdComp'
import { setupStore } from '/@/store'
// æ³¨å†Œonline模块lib
import { registerPackages } from '/@/utils/monorepo/registerPackages';
import { registerPackages } from '/@/utils/monorepo/registerPackages'
// åœ¨æœ¬åœ°å¼€å‘中引入的,以提高浏览器响应速度
if (import.meta.env.DEV) {
  import('ant-design-vue/dist/antd.less');
    import('ant-design-vue/dist/antd.less')
}
async function bootstrap() {
  // åˆ›å»ºåº”用实例
  const app = createApp(App);
    // åˆ›å»ºåº”用实例
    const app = createApp(App)
  // å¤šè¯­è¨€é…ç½®,异步情况:语言文件可以从服务器端获得
  await setupI18n(app);
    // å¤šè¯­è¨€é…ç½®,异步情况:语言文件可以从服务器端获得
    await setupI18n(app)
  // é…ç½®å­˜å‚¨
  setupStore(app);
    // é…ç½®å­˜å‚¨
    setupStore(app)
  // åˆå§‹åŒ–内部系统配置
  initAppConfigStore();
    // åˆå§‹åŒ–内部系统配置
    initAppConfigStore()
  // æ³¨å†Œå¤–部模块路由(注册online模块lib)
  registerPackages(app);
    // æ³¨å†Œå¤–部模块路由(注册online模块lib)
    registerPackages(app)
  // æ³¨å†Œå…¨å±€ç»„ä»¶
  registerGlobComp(app);
    // æ³¨å†Œå…¨å±€ç»„ä»¶
    registerGlobComp(app)
  //CAS单点登录
  await useSso().ssoLogin();
    //CAS单点登录
    await useSso().ssoLogin()
  // é…ç½®è·¯ç”±
  setupRouter(app);
    // é…ç½®è·¯ç”±
    setupRouter(app)
  // è·¯ç”±ä¿æŠ¤
  setupRouterGuard(router);
    // è·¯ç”±ä¿æŠ¤
    setupRouterGuard(router)
  // æ³¨å†Œå…¨å±€æŒ‡ä»¤
  setupGlobDirectives(app);
    // æ³¨å†Œå…¨å±€æŒ‡ä»¤
    setupGlobDirectives(app)
  // é…ç½®å…¨å±€é”™è¯¯å¤„理
  setupErrorHandle(app);
    // é…ç½®å…¨å±€é”™è¯¯å¤„理
    setupErrorHandle(app)
  // æ³¨å†Œç¬¬ä¸‰æ–¹ç»„ä»¶
  await registerThirdComp(app);
    // æ³¨å†Œç¬¬ä¸‰æ–¹ç»„ä»¶
    await registerThirdComp(app)
  // å½“路由准备好时再执行挂载( https://next.router.vuejs.org/api/#isready)
  await router.isReady();
    // å½“路由准备好时再执行挂载( https://next.router.vuejs.org/api/#isready)
    await router.isReady()
  // æŒ‚载应用
  app.mount('#app', true);
    // æŒ‚载应用
    app.mount('#app', true)
}
bootstrap();
bootstrap()
src/router/constant.ts
@@ -1,24 +1,24 @@
export const REDIRECT_NAME = 'Redirect';
export const REDIRECT_NAME = 'Redirect'
export const PARENT_LAYOUT_NAME = 'ParentLayout';
export const PARENT_LAYOUT_NAME = 'ParentLayout'
export const PAGE_NOT_FOUND_NAME = 'PageNotFound';
export const PAGE_NOT_FOUND_NAME = 'PageNotFound'
export const EXCEPTION_COMPONENT = () => import('/@/views/sys/exception/Exception.vue');
export const EXCEPTION_COMPONENT = () => import('/@/views/sys/exception/Exception.vue')
/**
 * @description: default layout
 */
export const LAYOUT = () => import('/@/layouts/default/index.vue');
export const LAYOUT = () => import('/@/layouts/default/index.vue')
/**
 * @description: parent-layout
 */
export const getParentLayout = (_name?: string) => {
  return () =>
    new Promise((resolve) => {
      resolve({
        name: PARENT_LAYOUT_NAME,
      });
    });
};
    return () =>
        new Promise((resolve) => {
            resolve({
                name: PARENT_LAYOUT_NAME,
            })
        })
}
src/router/index.ts
@@ -1,37 +1,37 @@
import type { RouteRecordRaw } from 'vue-router';
import type { App } from 'vue';
import type { App } from 'vue'
import type { RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
import { basicRoutes } from './routes';
import { createRouter, createWebHistory } from 'vue-router'
import { basicRoutes } from './routes'
// ç™½åå•应该包含基本静态路由
const WHITE_NAME_LIST: string[] = [];
const WHITE_NAME_LIST: string[] = []
const getRouteNames = (array: any[]) =>
  array.forEach((item) => {
    WHITE_NAME_LIST.push(item.name);
    getRouteNames(item.children || []);
  });
getRouteNames(basicRoutes);
    array.forEach((item) => {
        WHITE_NAME_LIST.push(item.name)
        getRouteNames(item.children || [])
    })
getRouteNames(basicRoutes)
// app router
export const router = createRouter({
  history: createWebHistory(import.meta.env.VITE_PUBLIC_PATH),
  routes: basicRoutes as unknown as RouteRecordRaw[],
  strict: true,
  scrollBehavior: () => ({ left: 0, top: 0 }),
});
    history: createWebHistory(import.meta.env.VITE_PUBLIC_PATH),
    routes: basicRoutes as unknown as RouteRecordRaw[],
    strict: true,
    scrollBehavior: () => ({ left: 0, top: 0 }),
})
// reset router
export function resetRouter() {
  router.getRoutes().forEach((route) => {
    const { name } = route;
    if (name && !WHITE_NAME_LIST.includes(name as string)) {
      router.hasRoute(name) && router.removeRoute(name);
    }
  });
    router.getRoutes().forEach((route) => {
        const { name } = route
        if (name && !WHITE_NAME_LIST.includes(name as string)) {
            router.hasRoute(name) && router.removeRoute(name)
        }
    })
}
// config router
export function setupRouter(app: App<Element>) {
  app.use(router);
    app.use(router)
}
src/router/menus/index.ts
@@ -1,126 +1,126 @@
import type { Menu, MenuModule } from '/@/router/types';
import type { RouteRecordNormalized } from 'vue-router';
import type { RouteRecordNormalized } from 'vue-router'
import type { Menu, MenuModule } from '/@/router/types'
import { useAppStoreWithOut } from '/@/store/modules/app';
import { usePermissionStore } from '/@/store/modules/permission';
import { transformMenuModule, getAllParentPath } from '/@/router/helper/menuHelper';
import { filter } from '/@/utils/helper/treeHelper';
import { isUrl } from '/@/utils/is';
import { router } from '/@/router';
import { PermissionModeEnum } from '/@/enums/appEnum';
import { pathToRegexp } from 'path-to-regexp';
import { pathToRegexp } from 'path-to-regexp'
import { PermissionModeEnum } from '/@/enums/appEnum'
import { router } from '/@/router'
import { getAllParentPath, transformMenuModule } from '/@/router/helper/menuHelper'
import { useAppStoreWithOut } from '/@/store/modules/app'
import { usePermissionStore } from '/@/store/modules/permission'
import { filter } from '/@/utils/helper/treeHelper'
import { isUrl } from '/@/utils/is'
const modules = import.meta.glob('./modules/**/*.ts', { eager: true });
const modules = import.meta.glob('./modules/**/*.ts', { eager: true })
const menuModules: MenuModule[] = [];
const menuModules: MenuModule[] = []
Object.keys(modules).forEach((key) => {
  const mod = (modules as Recordable)[key].default || {};
  const modList = Array.isArray(mod) ? [...mod] : [mod];
  menuModules.push(...modList);
});
    const mod = (modules as Recordable)[key].default || {}
    const modList = Array.isArray(mod) ? [...mod] : [mod]
    menuModules.push(...modList)
})
// ===========================
// ==========Helper===========
// ===========================
const getPermissionMode = () => {
  const appStore = useAppStoreWithOut();
  return appStore.getProjectConfig.permissionMode;
};
    const appStore = useAppStoreWithOut()
    return appStore.getProjectConfig.permissionMode
}
const isBackMode = () => {
  return getPermissionMode() === PermissionModeEnum.BACK;
};
    return getPermissionMode() === PermissionModeEnum.BACK
}
const isRouteMappingMode = () => {
  return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING;
};
    return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING
}
const isRoleMode = () => {
  return getPermissionMode() === PermissionModeEnum.ROLE;
};
    return getPermissionMode() === PermissionModeEnum.ROLE
}
const staticMenus: Menu[] = [];
(() => {
  menuModules.sort((a, b) => {
    return (a.orderNo || 0) - (b.orderNo || 0);
  });
const staticMenus: Menu[] = []
;(() => {
    menuModules.sort((a, b) => {
        return (a.orderNo || 0) - (b.orderNo || 0)
    })
  for (const menu of menuModules) {
    staticMenus.push(transformMenuModule(menu));
  }
})();
    for (const menu of menuModules) {
        staticMenus.push(transformMenuModule(menu))
    }
})()
async function getAsyncMenus() {
  const permissionStore = usePermissionStore();
  if (isBackMode()) {
    return permissionStore.getBackMenuList.filter((item) => !item.meta?.hideMenu && !item.hideMenu);
  }
  if (isRouteMappingMode()) {
    return permissionStore.getFrontMenuList.filter((item) => !item.hideMenu);
  }
  return staticMenus;
    const permissionStore = usePermissionStore()
    if (isBackMode()) {
        return permissionStore.getBackMenuList.filter((item) => !item.meta?.hideMenu && !item.hideMenu)
    }
    if (isRouteMappingMode()) {
        return permissionStore.getFrontMenuList.filter((item) => !item.hideMenu)
    }
    return staticMenus
}
export const getMenus = async (): Promise<Menu[]> => {
  const menus = await getAsyncMenus();
  if (isRoleMode()) {
    const routes = router.getRoutes();
    return filter(menus, basicFilter(routes));
  }
  return menus;
};
    const menus = await getAsyncMenus()
    if (isRoleMode()) {
        const routes = router.getRoutes()
        return filter(menus, basicFilter(routes))
    }
    return menus
}
export async function getCurrentParentPath(currentPath: string) {
  const menus = await getAsyncMenus();
  const allParentPath = await getAllParentPath(menus, currentPath);
  return allParentPath?.[0];
    const menus = await getAsyncMenus()
    const allParentPath = await getAllParentPath(menus, currentPath)
    return allParentPath?.[0]
}
// Get the level 1 menu, delete children
export async function getShallowMenus(): Promise<Menu[]> {
  const menus = await getAsyncMenus();
  const shallowMenuList = menus.map((item) => ({ ...item, children: undefined }));
  if (isRoleMode()) {
    const routes = router.getRoutes();
    return shallowMenuList.filter(basicFilter(routes));
  }
  return shallowMenuList;
    const menus = await getAsyncMenus()
    const shallowMenuList = menus.map((item) => ({ ...item, children: undefined }))
    if (isRoleMode()) {
        const routes = router.getRoutes()
        return shallowMenuList.filter(basicFilter(routes))
    }
    return shallowMenuList
}
// Get the children of the menu
export async function getChildrenMenus(parentPath: string) {
  const menus = await getMenus();
  const parent = menus.find((item) => item.path === parentPath);
  if (!parent || !parent.children || !!parent?.meta?.hideChildrenInMenu) {
    return [] as Menu[];
  }
  if (isRoleMode()) {
    const routes = router.getRoutes();
    return filter(parent.children, basicFilter(routes));
  }
  return parent.children;
    const menus = await getMenus()
    const parent = menus.find((item) => item.path === parentPath)
    if (!parent || !parent.children || !!parent?.meta?.hideChildrenInMenu) {
        return [] as Menu[]
    }
    if (isRoleMode()) {
        const routes = router.getRoutes()
        return filter(parent.children, basicFilter(routes))
    }
    return parent.children
}
function basicFilter(routes: RouteRecordNormalized[]) {
  return (menu: Menu) => {
    const matchRoute = routes.find((route) => {
      if (isUrl(menu.path)) return true;
    return (menu: Menu) => {
        const matchRoute = routes.find((route) => {
            if (isUrl(menu.path)) return true
      if (route.meta?.carryParam) {
        return pathToRegexp(route.path).test(menu.path);
      }
      const isSame = route.path === menu.path;
      if (!isSame) return false;
            if (route.meta?.carryParam) {
                return pathToRegexp(route.path).test(menu.path)
            }
            const isSame = route.path === menu.path
            if (!isSame) return false
      if (route.meta?.ignoreAuth) return true;
            if (route.meta?.ignoreAuth) return true
      return isSame || pathToRegexp(route.path).test(menu.path);
    });
            return isSame || pathToRegexp(route.path).test(menu.path)
        })
    if (!matchRoute) return false;
    menu.icon = (menu.icon || matchRoute.meta.icon) as string;
    menu.meta = matchRoute.meta;
    return true;
  };
        if (!matchRoute) return false
        menu.icon = (menu.icon || matchRoute.meta.icon) as string
        menu.meta = matchRoute.meta
        return true
    }
}
src/router/routes/basic.ts
@@ -1,73 +1,73 @@
import type { AppRouteRecordRaw } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
import { REDIRECT_NAME, LAYOUT, EXCEPTION_COMPONENT, PAGE_NOT_FOUND_NAME } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n'
import { EXCEPTION_COMPONENT, LAYOUT, PAGE_NOT_FOUND_NAME, REDIRECT_NAME } from '/@/router/constant'
import type { AppRouteRecordRaw } from '/@/router/types'
// 404 on a page
export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
  path: '/:path(.*)*',
  name: PAGE_NOT_FOUND_NAME,
  component: LAYOUT,
  meta: {
    title: 'ErrorPage',
    hideBreadcrumb: true,
    hideMenu: true,
  },
  children: [
    {
      path: '/:path(.*)*',
      name: PAGE_NOT_FOUND_NAME,
      component: EXCEPTION_COMPONENT,
      meta: {
        title: 'ErrorPage',
        hideBreadcrumb: true,
        hideMenu: true,
      },
    },
  ],
};
    path: '/:path(.*)*',
    name: PAGE_NOT_FOUND_NAME,
    component: LAYOUT,
    meta: {
        title: 'ErrorPage',
        hideBreadcrumb: true,
        hideMenu: true,
    },
    children: [
        {
            path: '/:path(.*)*',
            name: PAGE_NOT_FOUND_NAME,
            component: EXCEPTION_COMPONENT,
            meta: {
                title: 'ErrorPage',
                hideBreadcrumb: true,
                hideMenu: true,
            },
        },
    ],
}
export const REDIRECT_ROUTE: AppRouteRecordRaw = {
  path: '/redirect',
  component: LAYOUT,
  name: 'RedirectTo',
  meta: {
    title: REDIRECT_NAME,
    hideBreadcrumb: true,
    hideMenu: true,
  },
  children: [
    {
      path: '/redirect/:path(.*)',
      name: REDIRECT_NAME,
      component: () => import('/@/views/sys/redirect/index.vue'),
      meta: {
        title: REDIRECT_NAME,
        hideBreadcrumb: true,
      },
    },
  ],
};
    path: '/redirect',
    component: LAYOUT,
    name: 'RedirectTo',
    meta: {
        title: REDIRECT_NAME,
        hideBreadcrumb: true,
        hideMenu: true,
    },
    children: [
        {
            path: '/redirect/:path(.*)',
            name: REDIRECT_NAME,
            component: () => import('/@/views/sys/redirect/index.vue'),
            meta: {
                title: REDIRECT_NAME,
                hideBreadcrumb: true,
            },
        },
    ],
}
export const ERROR_LOG_ROUTE: AppRouteRecordRaw = {
  path: '/error-log',
  name: 'ErrorLog',
  component: LAYOUT,
  redirect: '/error-log/list',
  meta: {
    title: 'ErrorLog',
    hideBreadcrumb: true,
    hideChildrenInMenu: true,
  },
  children: [
    {
      path: 'list',
      name: 'ErrorLogList',
      component: () => import('/@/views/sys/error-log/index.vue'),
      meta: {
        title: t('routes.basic.errorLogList'),
        hideBreadcrumb: true,
        currentActiveMenu: '/error-log',
      },
    },
  ],
};
    path: '/error-log',
    name: 'ErrorLog',
    component: LAYOUT,
    redirect: '/error-log/list',
    meta: {
        title: 'ErrorLog',
        hideBreadcrumb: true,
        hideChildrenInMenu: true,
    },
    children: [
        {
            path: 'list',
            name: 'ErrorLogList',
            component: () => import('/@/views/sys/error-log/index.vue'),
            meta: {
                title: t('routes.basic.errorLogList'),
                hideBreadcrumb: true,
                currentActiveMenu: '/error-log',
            },
        },
    ],
}
src/router/routes/index.ts
@@ -1,69 +1,69 @@
import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types';
import type { AppRouteModule, AppRouteRecordRaw } from '/@/router/types'
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic'
import { mainOutRoutes } from './mainOut';
import { PageEnum } from '/@/enums/pageEnum';
import { t } from '/@/hooks/web/useI18n';
import { mainOutRoutes } from './mainOut'
import { PageEnum } from '/@/enums/pageEnum'
import { t } from '/@/hooks/web/useI18n'
const modules = import.meta.glob('./modules/**/*.ts', { eager: true });
const modules = import.meta.glob('./modules/**/*.ts', { eager: true })
const routeModuleList: AppRouteModule[] = [];
const routeModuleList: AppRouteModule[] = []
// åŠ å…¥åˆ°è·¯ç”±é›†åˆä¸­
Object.keys(modules).forEach((key) => {
  const mod = (modules as Recordable)[key].default || {};
  const modList = Array.isArray(mod) ? [...mod] : [mod];
  routeModuleList.push(...modList);
});
    const mod = (modules as Recordable)[key].default || {}
    const modList = Array.isArray(mod) ? [...mod] : [mod]
    routeModuleList.push(...modList)
})
export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList]
export const RootRoute: AppRouteRecordRaw = {
  path: '/',
  name: 'Root',
  redirect: PageEnum.BASE_HOME,
  meta: {
    title: 'Root',
  },
};
    path: '/',
    name: 'Root',
    redirect: PageEnum.BASE_HOME,
    meta: {
        title: 'Root',
    },
}
export const LoginRoute: AppRouteRecordRaw = {
  path: '/login',
  name: 'Login',
  //新版后台登录,如果想要使用旧版登录放开即可
  // component: () => import('/@/views/sys/login/Login.vue'),
  component: () => import('/@/views/system/loginmini/MiniLogin.vue'),
  meta: {
    title: t('routes.basic.login'),
  },
};
    path: '/login',
    name: 'Login',
    //新版后台登录,如果想要使用旧版登录放开即可
    // component: () => import('/@/views/sys/login/Login.vue'),
    component: () => import('/@/views/system/loginmini/MiniLogin.vue'),
    meta: {
        title: t('routes.basic.login'),
    },
}
//update-begin---author:wangshuai ---date:20220629  for:auth2登录页面路由------------
export const Oauth2LoginRoute: AppRouteRecordRaw = {
  path: '/oauth2-app/login',
  name: 'oauth2-app-login',
  //新版钉钉免登录,如果想要使用旧版放开即可
  // component: () => import('/@/views/sys/login/OAuth2Login.vue'),
  component: () => import('/@/views/system/loginmini/OAuth2Login.vue'),
  meta: {
    title: t('routes.oauth2.login'),
  },
};
    path: '/oauth2-app/login',
    name: 'oauth2-app-login',
    //新版钉钉免登录,如果想要使用旧版放开即可
    // component: () => import('/@/views/sys/login/OAuth2Login.vue'),
    component: () => import('/@/views/system/loginmini/OAuth2Login.vue'),
    meta: {
        title: t('routes.oauth2.login'),
    },
}
//update-end---author:wangshuai ---date:20220629  for:auth2登录页面路由------------
/**
 * ã€é€šè¿‡token直接静默登录】流程办理登录页面 ä¸­è½¬è·³è½¬
 */
export const TokenLoginRoute: AppRouteRecordRaw = {
  path: '/tokenLogin',
  name: 'TokenLoginRoute',
  component: () => import('/@/views/sys/login/TokenLoginPage.vue'),
  meta: {
    title: '带token登录页面',
    ignoreAuth: true,
  },
};
    path: '/tokenLogin',
    name: 'TokenLoginRoute',
    component: () => import('/@/views/sys/login/TokenLoginPage.vue'),
    meta: {
        title: '带token登录页面',
        ignoreAuth: true,
    },
}
// Basic routing without permission
export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE, PAGE_NOT_FOUND_ROUTE, TokenLoginRoute, Oauth2LoginRoute];
export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE, PAGE_NOT_FOUND_ROUTE, TokenLoginRoute, Oauth2LoginRoute]
src/router/routes/mainOut.ts
@@ -3,20 +3,20 @@
It is an independent new page.
the contents of the file still need to log in to access
 */
import type { AppRouteModule } from '/@/router/types';
import type { AppRouteModule } from '/@/router/types'
// test
// http:ip:port/main-out
export const mainOutRoutes: AppRouteModule[] = [
  {
    path: '/main-out',
    name: 'MainOut',
    component: () => import('/@/views/demo/main-out/index.vue'),
    meta: {
      title: 'MainOut',
      ignoreAuth: true,
    },
  },
];
    {
        path: '/main-out',
        name: 'MainOut',
        component: () => import('/@/views/demo/main-out/index.vue'),
        meta: {
            title: 'MainOut',
            ignoreAuth: true,
        },
    },
]
export const mainOutRouteNames = mainOutRoutes.map((item) => item.name);
export const mainOutRouteNames = mainOutRoutes.map((item) => item.name)
src/router/routes/modules/dashboard.ts
@@ -1,37 +1,37 @@
import type { AppRouteModule } from '/@/router/types';
import type { AppRouteModule } from '/@/router/types'
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
import { t } from '/@/hooks/web/useI18n'
import { LAYOUT } from '/@/router/constant'
const dashboard: AppRouteModule = {
  path: '/dashboard',
  name: 'Dashboard',
  component: LAYOUT,
  redirect: '/dashboard/analysis',
  meta: {
    orderNo: 10,
    icon: 'ion:grid-outline',
    title: t('routes.dashboard.dashboard'),
  },
  children: [
    {
      path: 'analysis',
      name: 'Analysis',
      component: () => import('/@/views/dashboard/Analysis/index.vue'),
      meta: {
        // affix: true,
        title: t('routes.dashboard.analysis'),
      },
    },
    {
      path: 'workbench',
      name: 'Workbench',
      component: () => import('/@/views/dashboard/workbench/index.vue'),
      meta: {
        title: t('routes.dashboard.workbench'),
      },
    },
  ],
};
    path: '/dashboard',
    name: 'Dashboard',
    component: LAYOUT,
    redirect: '/dashboard/analysis',
    meta: {
        orderNo: 10,
        icon: 'ion:grid-outline',
        title: t('routes.dashboard.dashboard'),
    },
    children: [
        {
            path: 'analysis',
            name: 'Analysis',
            component: () => import('/@/views/dashboard/Analysis/index.vue'),
            meta: {
                // affix: true,
                title: t('routes.dashboard.analysis'),
            },
        },
        {
            path: 'workbench',
            name: 'Workbench',
            component: () => import('/@/views/dashboard/workbench/index.vue'),
            meta: {
                title: t('routes.dashboard.workbench'),
            },
        },
    ],
}
export default dashboard;
export default dashboard
src/router/types.ts
@@ -1,59 +1,58 @@
import type { RouteRecordRaw, RouteMeta } from 'vue-router';
import { RoleEnum } from '/@/enums/roleEnum';
import { defineComponent } from 'vue';
import { defineComponent } from 'vue'
import type { RouteMeta, RouteRecordRaw } from 'vue-router'
import { RoleEnum } from '/@/enums/roleEnum'
export type Component<T extends any = any> = ReturnType<typeof defineComponent> | (() => Promise<typeof import('*.vue')>) | (() => Promise<T>);
export type Component<T extends any = any> = ReturnType<typeof defineComponent> | (() => Promise<typeof import('*.vue')>) | (() => Promise<T>)
// @ts-ignore
export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
  name: string;
  meta: RouteMeta;
  component?: Component | string;
  components?: Component;
  children?: AppRouteRecordRaw[];
  props?: Recordable;
  fullPath?: string;
  alwaysShow?: boolean;
    name: string
    meta: RouteMeta
    component?: Component | string
    components?: Component
    children?: AppRouteRecordRaw[]
    props?: Recordable
    fullPath?: string
    alwaysShow?: boolean
}
export interface MenuTag {
  type?: 'primary' | 'error' | 'warn' | 'success';
  content?: string;
  dot?: boolean;
    type?: 'primary' | 'error' | 'warn' | 'success'
    content?: string
    dot?: boolean
}
export interface Menu {
  name: string;
    name: string
  icon?: string;
    icon?: string
  path: string;
    path: string
  // path contains param, auto assignment.
  paramPath?: string;
    // path contains param, auto assignment.
    paramPath?: string
  disabled?: boolean;
    disabled?: boolean
  children?: Menu[];
    children?: Menu[]
  orderNo?: number;
    orderNo?: number
  roles?: RoleEnum[];
    roles?: RoleEnum[]
  meta?: Partial<RouteMeta>;
    meta?: Partial<RouteMeta>
  tag?: MenuTag;
    tag?: MenuTag
  hideMenu?: boolean;
  alwaysShow?: boolean;
    hideMenu?: boolean
    alwaysShow?: boolean
}
export interface MenuModule {
  orderNo?: number;
  menu: Menu;
    orderNo?: number
    menu: Menu
}
// export type AppRouteModule = RouteModule | AppRouteRecordRaw;
export type AppRouteModule = AppRouteRecordRaw;
export type AppRouteModule = AppRouteRecordRaw
src/store/modules/permission.ts
@@ -1,305 +1,304 @@
import type { AppRouteRecordRaw, Menu } from '/@/router/types';
import type { AppRouteRecordRaw, Menu } from '/@/router/types'
import { defineStore } from 'pinia';
import { store } from '/@/store';
import { useI18n } from '/@/hooks/web/useI18n';
import { useUserStore } from './user';
import { useAppStoreWithOut } from './app';
import { toRaw } from 'vue';
import { transformObjToRoute, flatMultiLevelRoutes, addSlashToRouteComponent } from '/@/router/helper/routeHelper';
import { transformRouteToMenu } from '/@/router/helper/menuHelper';
import { defineStore } from 'pinia'
import { toRaw } from 'vue'
import { useAppStoreWithOut } from './app'
import { useUserStore } from './user'
import { useI18n } from '/@/hooks/web/useI18n'
import { transformRouteToMenu } from '/@/router/helper/menuHelper'
import { addSlashToRouteComponent, flatMultiLevelRoutes, transformObjToRoute } from '/@/router/helper/routeHelper'
import { store } from '/@/store'
import projectSetting from '/@/settings/projectSetting';
import projectSetting from '/@/settings/projectSetting'
import { PermissionModeEnum } from '/@/enums/appEnum';
import { PermissionModeEnum } from '/@/enums/appEnum'
import { asyncRoutes } from '/@/router/routes';
import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
import { asyncRoutes } from '/@/router/routes'
import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'
import { filter } from '/@/utils/helper/treeHelper';
import { filter } from '/@/utils/helper/treeHelper'
import { getMenuList,switchVue3Menu } from '/@/api/sys/menu';
import { getPermCode } from '/@/api/sys/user';
import { getMenuList, switchVue3Menu } from '/@/api/sys/menu'
import { getPermCode } from '/@/api/sys/user'
import { useMessage } from '/@/hooks/web/useMessage';
import { PageEnum } from '/@/enums/pageEnum';
import { PageEnum } from '/@/enums/pageEnum'
import { useMessage } from '/@/hooks/web/useMessage'
// ç³»ç»Ÿæƒé™
interface AuthItem {
  // èœå•权限编码,例如:“sys:schedule:list,sys:schedule:info”,多个逗号隔开
  action: string;
  // æƒé™ç­–ç•¥1显示2禁用
  type: string | number;
  // æƒé™çŠ¶æ€(0无效1有效)
  status: string | number;
  // æƒé™åç§°
  describe?: string;
  isAuth?: boolean;
    // èœå•权限编码,例如:“sys:schedule:list,sys:schedule:info”,多个逗号隔开
    action: string
    // æƒé™ç­–ç•¥1显示2禁用
    type: string | number
    // æƒé™çŠ¶æ€(0无效1有效)
    status: string | number
    // æƒé™åç§°
    describe?: string
    isAuth?: boolean
}
interface PermissionState {
  // Permission code list
  permCodeList: string[] | number[];
  // Whether the route has been dynamically added
  isDynamicAddedRoute: boolean;
  // To trigger a menu update
  lastBuildMenuTime: number;
  // Backstage menu list
  backMenuList: Menu[];
  frontMenuList: Menu[];
  // ç”¨æˆ·æ‰€æ‹¥æœ‰çš„æƒé™
  authList: AuthItem[];
  // å…¨éƒ¨æƒé™é…ç½®
  allAuthList: AuthItem[];
  // ç³»ç»Ÿå®‰å…¨æ¨¡å¼
  sysSafeMode: boolean;
  // online子表按钮权限
  onlineSubTableAuthMap: object;
    // Permission code list
    permCodeList: string[] | number[]
    // Whether the route has been dynamically added
    isDynamicAddedRoute: boolean
    // To trigger a menu update
    lastBuildMenuTime: number
    // Backstage menu list
    backMenuList: Menu[]
    frontMenuList: Menu[]
    // ç”¨æˆ·æ‰€æ‹¥æœ‰çš„æƒé™
    authList: AuthItem[]
    // å…¨éƒ¨æƒé™é…ç½®
    allAuthList: AuthItem[]
    // ç³»ç»Ÿå®‰å…¨æ¨¡å¼
    sysSafeMode: boolean
    // online子表按钮权限
    onlineSubTableAuthMap: object
}
export const usePermissionStore = defineStore({
  id: 'app-permission',
  state: (): PermissionState => ({
    permCodeList: [],
    // Whether the route has been dynamically added
    isDynamicAddedRoute: false,
    // To trigger a menu update
    lastBuildMenuTime: 0,
    // Backstage menu list
    backMenuList: [],
    // menu List
    frontMenuList: [],
    authList: [],
    allAuthList: [],
    sysSafeMode: false,
    onlineSubTableAuthMap: {},
  }),
  getters: {
    getPermCodeList(): string[] | number[] {
      return this.permCodeList;
    },
    getBackMenuList(): Menu[] {
      return this.backMenuList;
    },
    getFrontMenuList(): Menu[] {
      return this.frontMenuList;
    },
    getLastBuildMenuTime(): number {
      return this.lastBuildMenuTime;
    },
    getIsDynamicAddedRoute(): boolean {
      return this.isDynamicAddedRoute;
    },
    id: 'app-permission',
    state: (): PermissionState => ({
        permCodeList: [],
        // Whether the route has been dynamically added
        isDynamicAddedRoute: false,
        // To trigger a menu update
        lastBuildMenuTime: 0,
        // Backstage menu list
        backMenuList: [],
        // menu List
        frontMenuList: [],
        authList: [],
        allAuthList: [],
        sysSafeMode: false,
        onlineSubTableAuthMap: {},
    }),
    getters: {
        getPermCodeList(): string[] | number[] {
            return this.permCodeList
        },
        getBackMenuList(): Menu[] {
            return this.backMenuList
        },
        getFrontMenuList(): Menu[] {
            return this.frontMenuList
        },
        getLastBuildMenuTime(): number {
            return this.lastBuildMenuTime
        },
        getIsDynamicAddedRoute(): boolean {
            return this.isDynamicAddedRoute
        },
    //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
    getOnlineSubTableAuth: (state) => {
      return (code) => state.onlineSubTableAuthMap[code];
    },
    //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
  },
  actions: {
    setPermCodeList(codeList: string[]) {
      this.permCodeList = codeList;
    },
        //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
        getOnlineSubTableAuth: (state) => {
            return (code) => state.onlineSubTableAuthMap[code]
        },
        //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
    },
    actions: {
        setPermCodeList(codeList: string[]) {
            this.permCodeList = codeList
        },
    setBackMenuList(list: Menu[]) {
      this.backMenuList = list;
      list?.length > 0 && this.setLastBuildMenuTime();
    },
        setBackMenuList(list: Menu[]) {
            this.backMenuList = list
            list?.length > 0 && this.setLastBuildMenuTime()
        },
    setFrontMenuList(list: Menu[]) {
      this.frontMenuList = list;
    },
        setFrontMenuList(list: Menu[]) {
            this.frontMenuList = list
        },
    setLastBuildMenuTime() {
      this.lastBuildMenuTime = new Date().getTime();
    },
        setLastBuildMenuTime() {
            this.lastBuildMenuTime = new Date().getTime()
        },
    setDynamicAddedRoute(added: boolean) {
      this.isDynamicAddedRoute = added;
    },
    resetState(): void {
      this.isDynamicAddedRoute = false;
      this.permCodeList = [];
      this.backMenuList = [];
      this.lastBuildMenuTime = 0;
    },
    async changePermissionCode() {
      const systemPermission = await getPermCode();
      const codeList = systemPermission.codeList;
      this.setPermCodeList(codeList);
      this.setAuthData(systemPermission);
    },
    async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
      const { t } = useI18n();
      const userStore = useUserStore();
      const appStore = useAppStoreWithOut();
        setDynamicAddedRoute(added: boolean) {
            this.isDynamicAddedRoute = added
        },
        resetState(): void {
            this.isDynamicAddedRoute = false
            this.permCodeList = []
            this.backMenuList = []
            this.lastBuildMenuTime = 0
        },
        async changePermissionCode() {
            const systemPermission = await getPermCode()
            const codeList = systemPermission.codeList
            this.setPermCodeList(codeList)
            this.setAuthData(systemPermission)
        },
        async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
            const { t } = useI18n()
            const userStore = useUserStore()
            const appStore = useAppStoreWithOut()
      let routes: AppRouteRecordRaw[] = [];
      const roleList = toRaw(userStore.getRoleList) || [];
      const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig;
            let routes: AppRouteRecordRaw[] = []
            const roleList = toRaw(userStore.getRoleList) || []
            const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig
      const routeFilter = (route: AppRouteRecordRaw) => {
        const { meta } = route;
        const { roles } = meta || {};
        if (!roles) return true;
        return roleList.some((role) => roles.includes(role));
      };
            const routeFilter = (route: AppRouteRecordRaw) => {
                const { meta } = route
                const { roles } = meta || {}
                if (!roles) return true
                return roleList.some((role) => roles.includes(role))
            }
      const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
        const { meta } = route;
        const { ignoreRoute } = meta || {};
        return !ignoreRoute;
      };
            const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
                const { meta } = route
                const { ignoreRoute } = meta || {}
                return !ignoreRoute
            }
      /**
       * @description æ ¹æ®è®¾ç½®çš„首页path,修正routes中的affix标记(固定首页)
       * */
      const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
        if (!routes || routes.length === 0) return;
        let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME;
        function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
          if (parentPath) parentPath = parentPath + '/';
          routes.forEach((route: AppRouteRecordRaw) => {
            const { path, children, redirect } = route;
            const currentPath = path.startsWith('/') ? path : parentPath + path;
            if (currentPath === homePath) {
              if (redirect) {
                homePath = route.redirect! as string;
              } else {
                route.meta = Object.assign({}, route.meta, { affix: true });
                throw new Error('end');
              }
            }
            children && children.length > 0 && patcher(children, currentPath);
          });
        }
        try {
          patcher(routes);
        } catch (e) {
          // å·²å¤„理完毕跳出循环
        }
        return;
      };
            /**
             * @description æ ¹æ®è®¾ç½®çš„首页path,修正routes中的affix标记(固定首页)
             * */
            const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
                if (!routes || routes.length === 0) return
                let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME
                function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
                    if (parentPath) parentPath = parentPath + '/'
                    routes.forEach((route: AppRouteRecordRaw) => {
                        const { path, children, redirect } = route
                        const currentPath = path.startsWith('/') ? path : parentPath + path
                        if (currentPath === homePath) {
                            if (redirect) {
                                homePath = route.redirect! as string
                            } else {
                                route.meta = Object.assign({}, route.meta, { affix: true })
                                throw new Error('end')
                            }
                        }
                        children && children.length > 0 && patcher(children, currentPath)
                    })
                }
                try {
                    patcher(routes)
                } catch (e) {
                    // å·²å¤„理完毕跳出循环
                }
                return
            }
      switch (permissionMode) {
        case PermissionModeEnum.ROLE:
          routes = filter(asyncRoutes, routeFilter);
          routes = routes.filter(routeFilter);
          //  å°†å¤šçº§è·¯ç”±è½¬æ¢ä¸ºäºŒçº§
          routes = flatMultiLevelRoutes(routes);
          break;
            switch (permissionMode) {
                case PermissionModeEnum.ROLE:
                    routes = filter(asyncRoutes, routeFilter)
                    routes = routes.filter(routeFilter)
                    //  å°†å¤šçº§è·¯ç”±è½¬æ¢ä¸ºäºŒçº§
                    routes = flatMultiLevelRoutes(routes)
                    break
        case PermissionModeEnum.ROUTE_MAPPING:
          routes = filter(asyncRoutes, routeFilter);
          routes = routes.filter(routeFilter);
          const menuList = transformRouteToMenu(routes, true);
          routes = filter(routes, routeRemoveIgnoreFilter);
          routes = routes.filter(routeRemoveIgnoreFilter);
          menuList.sort((a, b) => {
            return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0);
          });
                case PermissionModeEnum.ROUTE_MAPPING:
                    routes = filter(asyncRoutes, routeFilter)
                    routes = routes.filter(routeFilter)
                    const menuList = transformRouteToMenu(routes, true)
                    routes = filter(routes, routeRemoveIgnoreFilter)
                    routes = routes.filter(routeRemoveIgnoreFilter)
                    menuList.sort((a, b) => {
                        return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0)
                    })
          this.setFrontMenuList(menuList);
          // å°†å¤šçº§è·¯ç”±è½¬æ¢ä¸ºäºŒçº§
          routes = flatMultiLevelRoutes(routes);
          break;
                    this.setFrontMenuList(menuList)
                    // å°†å¤šçº§è·¯ç”±è½¬æ¢ä¸ºäºŒçº§
                    routes = flatMultiLevelRoutes(routes)
                    break
        // åŽå°èœå•构建
        case PermissionModeEnum.BACK:
          const { createMessage, createWarningModal } = useMessage();
          // èœå•加载提示
          // createMessage.loading({
          //   content: t('sys.app.menuLoading'),
          //   duration: 1,
          // });
                // åŽå°èœå•构建
                case PermissionModeEnum.BACK:
                    const { createMessage, createWarningModal } = useMessage()
                    // èœå•加载提示
                    // createMessage.loading({
                    //   content: t('sys.app.menuLoading'),
                    //   duration: 1,
                    // });
          // ä»ŽåŽå°èŽ·å–æƒé™ç ï¼Œ
          // è¿™ä¸ªå‡½æ•°å¯èƒ½åªéœ€è¦æ‰§è¡Œä¸€æ¬¡ï¼Œå¹¶ä¸”实际的项目可以在正确的时间被放置
          let routeList: AppRouteRecordRaw[] = [];
          try {
            this.changePermissionCode();
            routeList = (await getMenuList()) as AppRouteRecordRaw[];
            // update-begin----author:sunjianlei---date:20220315------for: åˆ¤æ–­æ˜¯å¦æ˜¯ vue3 ç‰ˆæœ¬çš„菜单 ---
            let hasIndex: boolean = false;
            let hasIcon: boolean = false;
            for (let menuItem of routeList) {
              // æ¡ä»¶1:判断组件是否是 layouts/default/index
              if (!hasIndex) {
                hasIndex = menuItem.component === 'layouts/default/index';
              }
              // æ¡ä»¶2:判断图标是否带有 å†’号
              if (!hasIcon) {
                hasIcon = !!menuItem.meta?.icon?.includes(':');
              }
              // æ»¡è¶³ä»»ä½•一个条件都直接跳出循环
              if (hasIcon || hasIndex) {
                break;
              }
            }
            // ä¸¤ä¸ªæ¡ä»¶éƒ½ä¸æ»¡è¶³ï¼Œå°±å¼¹å‡ºæç¤ºæ¡†
            if (!hasIcon && !hasIndex) {
              // å»¶è¿Ÿ1.5秒之后再出现提示,否则提示框出不来
              setTimeout(
                () =>
                  createWarningModal({
                    title: '检测提示',
                    content:
                      '当前菜单表是 <b>Vue2版本</b>,导致菜单加载异常!<br>点击确认,切换到Vue3版菜单!',
                    onOk:function () {
                      switchVue3Menu();
                      location.reload();
                    }
                  }),
                100
              );
            }
            // update-end----author:sunjianlei---date:20220315------for: åˆ¤æ–­æ˜¯å¦æ˜¯ vue3 ç‰ˆæœ¬çš„菜单 ---
          } catch (error) {
            console.error(error);
          }
          // ç»„件地址前加斜杠处理  author: lsq date:2021-09-08
          routeList = addSlashToRouteComponent(routeList);
          // åŠ¨æ€å¼•å…¥ç»„ä»¶
          routeList = transformObjToRoute(routeList);
                    // ä»ŽåŽå°èŽ·å–æƒé™ç ï¼Œ
                    // è¿™ä¸ªå‡½æ•°å¯èƒ½åªéœ€è¦æ‰§è¡Œä¸€æ¬¡ï¼Œå¹¶ä¸”实际的项目可以在正确的时间被放置
                    let routeList: AppRouteRecordRaw[] = []
                    try {
                        this.changePermissionCode()
                        routeList = (await getMenuList()) as AppRouteRecordRaw[]
                        // update-begin----author:sunjianlei---date:20220315------for: åˆ¤æ–­æ˜¯å¦æ˜¯ vue3 ç‰ˆæœ¬çš„菜单 ---
                        let hasIndex = false
                        let hasIcon = false
                        for (const menuItem of routeList) {
                            // æ¡ä»¶1:判断组件是否是 layouts/default/index
                            if (!hasIndex) {
                                hasIndex = menuItem.component === 'layouts/default/index'
                            }
                            // æ¡ä»¶2:判断图标是否带有 å†’号
                            if (!hasIcon) {
                                hasIcon = !!menuItem.meta?.icon?.includes(':')
                            }
                            // æ»¡è¶³ä»»ä½•一个条件都直接跳出循环
                            if (hasIcon || hasIndex) {
                                break
                            }
                        }
                        // ä¸¤ä¸ªæ¡ä»¶éƒ½ä¸æ»¡è¶³ï¼Œå°±å¼¹å‡ºæç¤ºæ¡†
                        if (!hasIcon && !hasIndex) {
                            // å»¶è¿Ÿ1.5秒之后再出现提示,否则提示框出不来
                            setTimeout(
                                () =>
                                    createWarningModal({
                                        title: '检测提示',
                                        content: '当前菜单表是 <b>Vue2版本</b>,导致菜单加载异常!<br>点击确认,切换到Vue3版菜单!',
                                        onOk: function () {
                                            switchVue3Menu()
                                            location.reload()
                                        },
                                    }),
                                100
                            )
                        }
                        // update-end----author:sunjianlei---date:20220315------for: åˆ¤æ–­æ˜¯å¦æ˜¯ vue3 ç‰ˆæœ¬çš„菜单 ---
                    } catch (error) {
                        console.error(error)
                    }
                    // ç»„件地址前加斜杠处理  author: lsq date:2021-09-08
                    routeList = addSlashToRouteComponent(routeList)
                    // åŠ¨æ€å¼•å…¥ç»„ä»¶
                    routeList = transformObjToRoute(routeList)
          // æž„建后台路由菜单
          const backMenuList = transformRouteToMenu(routeList);
          this.setBackMenuList(backMenuList);
                    // æž„建后台路由菜单
                    const backMenuList = transformRouteToMenu(routeList)
                    this.setBackMenuList(backMenuList)
          // åˆ é™¤meta.ignoreRoute项
          routeList = filter(routeList, routeRemoveIgnoreFilter);
          routeList = routeList.filter(routeRemoveIgnoreFilter);
                    // åˆ é™¤meta.ignoreRoute项
                    routeList = filter(routeList, routeRemoveIgnoreFilter)
                    routeList = routeList.filter(routeRemoveIgnoreFilter)
          routeList = flatMultiLevelRoutes(routeList);
          routes = [PAGE_NOT_FOUND_ROUTE, ...routeList];
          break;
      }
                    routeList = flatMultiLevelRoutes(routeList)
                    routes = [PAGE_NOT_FOUND_ROUTE, ...routeList]
                    break
            }
      routes.push(ERROR_LOG_ROUTE);
      patchHomeAffix(routes);
      return routes;
    },
    setAuthData(systemPermission) {
      this.authList = systemPermission.auth;
      this.allAuthList = systemPermission.allAuth;
      this.sysSafeMode = systemPermission.sysSafeMode;
    },
    setAuthList(authList: AuthItem[]) {
      this.authList = authList;
    },
    setAllAuthList(authList: AuthItem[]) {
      this.allAuthList = authList;
    },
            routes.push(ERROR_LOG_ROUTE)
            patchHomeAffix(routes)
            return routes
        },
        setAuthData(systemPermission) {
            this.authList = systemPermission.auth
            this.allAuthList = systemPermission.allAuth
            this.sysSafeMode = systemPermission.sysSafeMode
        },
        setAuthList(authList: AuthItem[]) {
            this.authList = authList
        },
        setAllAuthList(authList: AuthItem[]) {
            this.allAuthList = authList
        },
    //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
    setOnlineSubTableAuth(code, hideBtnList) {
      this.onlineSubTableAuthMap[code] = hideBtnList;
    },
    //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
  },
});
        //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
        setOnlineSubTableAuth(code, hideBtnList) {
            this.onlineSubTableAuthMap[code] = hideBtnList
        },
        //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 å­è¡¨æŒ‰é’®æ²¡æŽ§åˆ¶
    },
})
// éœ€è¦åœ¨è®¾ç½®ä¹‹å¤–使用
export function usePermissionStoreWithOut() {
  return usePermissionStore(store);
    return usePermissionStore(store)
}
src/utils/aes.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,505 @@
var t = t || function(t, e) {
  var r = {}, i = r.lib = {}, n = function() {}, o = i.Base = {
    extend: function(t) {
      n.prototype = this;
      var e = new n();
      return t && e.mixIn(t), e.hasOwnProperty("init") || (e.init = function() {
        e.$super.init.apply(this, arguments);
      }), e.init.prototype = e, e.$super = this, e;
    },
    create: function() {
      var t = this.extend();
      return t.init.apply(t, arguments), t;
    },
    init: function() {},
    mixIn: function(t) {
      for (var e in t) t.hasOwnProperty(e) && (this[e] = t[e]);
      t.hasOwnProperty("toString") && (this.toString = t.toString);
    },
    clone: function() {
      return this.init.prototype.extend(this);
    }
  }, s = i.WordArray = o.extend({
    init: function(t, e) {
      t = this.words = t || [], this.sigBytes = void 0 != e ? e : 4 * t.length;
    },
    toString: function(t) {
      return (t || a).stringify(this);
    },
    concat: function(t) {
      var e = this.words, r = t.words, i = this.sigBytes;
      if (t = t.sigBytes, this.clamp(), i % 4) for (var n = 0; n < t; n++) e[i + n >>> 2] |= (r[n >>> 2] >>> 24 - n % 4 * 8 & 255) << 24 - (i + n) % 4 * 8; else if (65535 < r.length) for (n = 0; n < t; n += 4) e[i + n >>> 2] = r[n >>> 2]; else e.push.apply(e, r);
      return this.sigBytes += t, this;
    },
    clamp: function() {
      var e = this.words, r = this.sigBytes;
      e[r >>> 2] &= 4294967295 << 32 - r % 4 * 8, e.length = t.ceil(r / 4);
    },
    clone: function() {
      var t = o.clone.call(this);
      return t.words = this.words.slice(0), t;
    },
    random: function(e) {
      for (var r = [], i = 0; i < e; i += 4) r.push(4294967296 * t.random() | 0);
      return new s.init(r, e);
    }
  }), c = r.enc = {}, a = c.Hex = {
    stringify: function(t) {
      var e = t.words;
      t = t.sigBytes;
      for (var r = [], i = 0; i < t; i++) {
        var n = e[i >>> 2] >>> 24 - i % 4 * 8 & 255;
        r.push((n >>> 4).toString(16)), r.push((15 & n).toString(16));
      }
      return r.join("");
    },
    parse: function(t) {
      for (var e = t.length, r = [], i = 0; i < e; i += 2) r[i >>> 3] |= parseInt(t.substr(i, 2), 16) << 24 - i % 8 * 4;
      return new s.init(r, e / 2);
    }
  }, f = c.Latin1 = {
    stringify: function(t) {
      var e = t.words;
      t = t.sigBytes;
      for (var r = [], i = 0; i < t; i++) r.push(String.fromCharCode(e[i >>> 2] >>> 24 - i % 4 * 8 & 255));
      return r.join("");
    },
    parse: function(t) {
      for (var e = t.length, r = [], i = 0; i < e; i++) r[i >>> 2] |= (255 & t.charCodeAt(i)) << 24 - i % 4 * 8;
      return new s.init(r, e);
    }
  }, h = c.Utf8 = {
    stringify: function(t) {
      try {
        return decodeURIComponent(escape(f.stringify(t)));
      } catch (t) {
        throw Error("Malformed UTF-8 data");
      }
    },
    parse: function(t) {
      return f.parse(unescape(encodeURIComponent(t)));
    }
  }, u = i.BufferedBlockAlgorithm = o.extend({
    reset: function() {
      this._data = new s.init(), this._nDataBytes = 0;
    },
    _append: function(t) {
      "string" == typeof t && (t = h.parse(t)), this._data.concat(t), this._nDataBytes += t.sigBytes;
    },
    _process: function(e) {
      var r = this._data, i = r.words, n = r.sigBytes, o = this.blockSize, c = n / (4 * o);
      if (e = (c = e ? t.ceil(c) : t.max((0 | c) - this._minBufferSize, 0)) * o, n = t.min(4 * e, n),
        e) {
        for (var a = 0; a < e; a += o) this._doProcessBlock(i, a);
        a = i.splice(0, e), r.sigBytes -= n;
      }
      return new s.init(a, n);
    },
    clone: function() {
      var t = o.clone.call(this);
      return t._data = this._data.clone(), t;
    },
    _minBufferSize: 0
  });
  i.Hasher = u.extend({
    cfg: o.extend(),
    init: function(t) {
      this.cfg = this.cfg.extend(t), this.reset();
    },
    reset: function() {
      u.reset.call(this), this._doReset();
    },
    update: function(t) {
      return this._append(t), this._process(), this;
    },
    finalize: function(t) {
      return t && this._append(t), this._doFinalize();
    },
    blockSize: 16,
    _createHelper: function(t) {
      return function(e, r) {
        return new t.init(r).finalize(e);
      };
    },
    _createHmacHelper: function(t) {
      return function(e, r) {
        return new p.HMAC.init(t, r).finalize(e);
      };
    }
  });
  var p = r.algo = {};
  return r;
}(Math);
!function() {
  var e = t, r = e.lib.WordArray;
  e.enc.Base64 = {
    stringify: function(t) {
      var e = t.words, r = t.sigBytes, i = this._map;
      t.clamp(), t = [];
      for (var n = 0; n < r; n += 3) for (var o = (e[n >>> 2] >>> 24 - n % 4 * 8 & 255) << 16 | (e[n + 1 >>> 2] >>> 24 - (n + 1) % 4 * 8 & 255) << 8 | e[n + 2 >>> 2] >>> 24 - (n + 2) % 4 * 8 & 255, s = 0; 4 > s && n + .75 * s < r; s++) t.push(i.charAt(o >>> 6 * (3 - s) & 63));
      if (e = i.charAt(64)) for (;t.length % 4; ) t.push(e);
      return t.join("");
    },
    parse: function(t) {
      var e = t.length, i = this._map;
      (n = i.charAt(64)) && -1 != (n = t.indexOf(n)) && (e = n);
      for (var n = [], o = 0, s = 0; s < e; s++) if (s % 4) {
        var c = i.indexOf(t.charAt(s - 1)) << s % 4 * 2, a = i.indexOf(t.charAt(s)) >>> 6 - s % 4 * 2;
        n[o >>> 2] |= (c | a) << 24 - o % 4 * 8, o++;
      }
      return r.create(n, o);
    },
    _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
  };
}(), function(e) {
  function r(t, e, r, i, n, o, s) {
    return ((t = t + (e & r | ~e & i) + n + s) << o | t >>> 32 - o) + e;
  }
  function i(t, e, r, i, n, o, s) {
    return ((t = t + (e & i | r & ~i) + n + s) << o | t >>> 32 - o) + e;
  }
  function n(t, e, r, i, n, o, s) {
    return ((t = t + (e ^ r ^ i) + n + s) << o | t >>> 32 - o) + e;
  }
  function o(t, e, r, i, n, o, s) {
    return ((t = t + (r ^ (e | ~i)) + n + s) << o | t >>> 32 - o) + e;
  }
  for (var s = t, c = (f = s.lib).WordArray, a = f.Hasher, f = s.algo, h = [], u = 0; 64 > u; u++) h[u] = 4294967296 * e.abs(e.sin(u + 1)) | 0;
  f = f.MD5 = a.extend({
    _doReset: function() {
      this._hash = new c.init([ 1732584193, 4023233417, 2562383102, 271733878 ]);
    },
    _doProcessBlock: function(t, e) {
      for (s = 0; 16 > s; s++) {
        a = t[c = e + s];
        t[c] = 16711935 & (a << 8 | a >>> 24) | 4278255360 & (a << 24 | a >>> 8);
      }
      var s = this._hash.words, c = t[e + 0], a = t[e + 1], f = t[e + 2], u = t[e + 3], p = t[e + 4], d = t[e + 5], l = t[e + 6], y = t[e + 7], v = t[e + 8], _ = t[e + 9], g = t[e + 10], B = t[e + 11], k = t[e + 12], x = t[e + 13], m = t[e + 14], S = t[e + 15], w = s[0], z = s[1], C = s[2], E = s[3], z = o(z = o(z = o(z = o(z = n(z = n(z = n(z = n(z = i(z = i(z = i(z = i(z = r(z = r(z = r(z = r(z, C = r(C, E = r(E, w = r(w, z, C, E, c, 7, h[0]), z, C, a, 12, h[1]), w, z, f, 17, h[2]), E, w, u, 22, h[3]), C = r(C, E = r(E, w = r(w, z, C, E, p, 7, h[4]), z, C, d, 12, h[5]), w, z, l, 17, h[6]), E, w, y, 22, h[7]), C = r(C, E = r(E, w = r(w, z, C, E, v, 7, h[8]), z, C, _, 12, h[9]), w, z, g, 17, h[10]), E, w, B, 22, h[11]), C = r(C, E = r(E, w = r(w, z, C, E, k, 7, h[12]), z, C, x, 12, h[13]), w, z, m, 17, h[14]), E, w, S, 22, h[15]), C = i(C, E = i(E, w = i(w, z, C, E, a, 5, h[16]), z, C, l, 9, h[17]), w, z, B, 14, h[18]), E, w, c, 20, h[19]), C = i(C, E = i(E, w = i(w, z, C, E, d, 5, h[20]), z, C, g, 9, h[21]), w, z, S, 14, h[22]), E, w, p, 20, h[23]), C = i(C, E = i(E, w = i(w, z, C, E, _, 5, h[24]), z, C, m, 9, h[25]), w, z, u, 14, h[26]), E, w, v, 20, h[27]), C = i(C, E = i(E, w = i(w, z, C, E, x, 5, h[28]), z, C, f, 9, h[29]), w, z, y, 14, h[30]), E, w, k, 20, h[31]), C = n(C, E = n(E, w = n(w, z, C, E, d, 4, h[32]), z, C, v, 11, h[33]), w, z, B, 16, h[34]), E, w, m, 23, h[35]), C = n(C, E = n(E, w = n(w, z, C, E, a, 4, h[36]), z, C, p, 11, h[37]), w, z, y, 16, h[38]), E, w, g, 23, h[39]), C = n(C, E = n(E, w = n(w, z, C, E, x, 4, h[40]), z, C, c, 11, h[41]), w, z, u, 16, h[42]), E, w, l, 23, h[43]), C = n(C, E = n(E, w = n(w, z, C, E, _, 4, h[44]), z, C, k, 11, h[45]), w, z, S, 16, h[46]), E, w, f, 23, h[47]), C = o(C, E = o(E, w = o(w, z, C, E, c, 6, h[48]), z, C, y, 10, h[49]), w, z, m, 15, h[50]), E, w, d, 21, h[51]), C = o(C, E = o(E, w = o(w, z, C, E, k, 6, h[52]), z, C, u, 10, h[53]), w, z, g, 15, h[54]), E, w, a, 21, h[55]), C = o(C, E = o(E, w = o(w, z, C, E, v, 6, h[56]), z, C, S, 10, h[57]), w, z, l, 15, h[58]), E, w, x, 21, h[59]), C = o(C, E = o(E, w = o(w, z, C, E, p, 6, h[60]), z, C, B, 10, h[61]), w, z, f, 15, h[62]), E, w, _, 21, h[63]);
      s[0] = s[0] + w | 0, s[1] = s[1] + z | 0, s[2] = s[2] + C | 0, s[3] = s[3] + E | 0;
    },
    _doFinalize: function() {
      var t = this._data, r = t.words, i = 8 * this._nDataBytes, n = 8 * t.sigBytes;
      r[n >>> 5] |= 128 << 24 - n % 32;
      var o = e.floor(i / 4294967296);
      for (r[15 + (n + 64 >>> 9 << 4)] = 16711935 & (o << 8 | o >>> 24) | 4278255360 & (o << 24 | o >>> 8),
             r[14 + (n + 64 >>> 9 << 4)] = 16711935 & (i << 8 | i >>> 24) | 4278255360 & (i << 24 | i >>> 8),
             t.sigBytes = 4 * (r.length + 1), this._process(), r = (t = this._hash).words, i = 0; 4 > i; i++) n = r[i],
        r[i] = 16711935 & (n << 8 | n >>> 24) | 4278255360 & (n << 24 | n >>> 8);
      return t;
    },
    clone: function() {
      var t = a.clone.call(this);
      return t._hash = this._hash.clone(), t;
    }
  }), s.MD5 = a._createHelper(f), s.HmacMD5 = a._createHmacHelper(f);
}(Math), function() {
  var e = t, r = e.lib, i = r.Base, n = r.WordArray, o = (r = e.algo).EvpKDF = i.extend({
    cfg: i.extend({
      keySize: 4,
      hasher: r.MD5,
      iterations: 1
    }),
    init: function(t) {
      this.cfg = this.cfg.extend(t);
    },
    compute: function(t, e) {
      for (var r = (c = this.cfg).hasher.create(), i = n.create(), o = i.words, s = c.keySize, c = c.iterations; o.length < s; ) {
        a && r.update(a);
        var a = r.update(t).finalize(e);
        r.reset();
        for (var f = 1; f < c; f++) a = r.finalize(a), r.reset();
        i.concat(a);
      }
      return i.sigBytes = 4 * s, i;
    }
  });
  e.EvpKDF = function(t, e, r) {
    return o.create(r).compute(t, e);
  };
}(), t.lib.Cipher || function(e) {
  var r = (l = t).lib, i = r.Base, n = r.WordArray, o = r.BufferedBlockAlgorithm, s = l.enc.Base64, c = l.algo.EvpKDF, a = r.Cipher = o.extend({
    cfg: i.extend(),
    createEncryptor: function(t, e) {
      return this.create(this._ENC_XFORM_MODE, t, e);
    },
    createDecryptor: function(t, e) {
      return this.create(this._DEC_XFORM_MODE, t, e);
    },
    init: function(t, e, r) {
      this.cfg = this.cfg.extend(r), this._xformMode = t, this._key = e, this.reset();
    },
    reset: function() {
      o.reset.call(this), this._doReset();
    },
    process: function(t) {
      return this._append(t), this._process();
    },
    finalize: function(t) {
      return t && this._append(t), this._doFinalize();
    },
    keySize: 4,
    ivSize: 4,
    _ENC_XFORM_MODE: 1,
    _DEC_XFORM_MODE: 2,
    _createHelper: function(t) {
      return {
        encrypt: function(e, r, i) {
          return ("string" == typeof r ? y : d).encrypt(t, e, r, i);
        },
        decrypt: function(e, r, i) {
          return ("string" == typeof r ? y : d).decrypt(t, e, r, i);
        }
      };
    }
  });
  r.StreamCipher = a.extend({
    _doFinalize: function() {
      return this._process(!0);
    },
    blockSize: 1
  });
  var f = l.mode = {}, h = function(t, e, r) {
    var i = this._iv;
    i ? this._iv = void 0 : i = this._prevBlock;
    for (var n = 0; n < r; n++) t[e + n] ^= i[n];
  }, u = (r.BlockCipherMode = i.extend({
    createEncryptor: function(t, e) {
      return this.Encryptor.create(t, e);
    },
    createDecryptor: function(t, e) {
      return this.Decryptor.create(t, e);
    },
    init: function(t, e) {
      this._cipher = t, this._iv = e;
    }
  })).extend();
  u.Encryptor = u.extend({
    processBlock: function(t, e) {
      var r = this._cipher, i = r.blockSize;
      h.call(this, t, e, i), r.encryptBlock(t, e), this._prevBlock = t.slice(e, e + i);
    }
  }), u.Decryptor = u.extend({
    processBlock: function(t, e) {
      var r = this._cipher, i = r.blockSize, n = t.slice(e, e + i);
      r.decryptBlock(t, e), h.call(this, t, e, i), this._prevBlock = n;
    }
  }), f = f.CBC = u, u = (l.pad = {}).Pkcs7 = {
    pad: function(t, e) {
      for (var r = 4 * e, i = (r = r - t.sigBytes % r) << 24 | r << 16 | r << 8 | r, o = [], s = 0; s < r; s += 4) o.push(i);
      r = n.create(o, r), t.concat(r);
    },
    unpad: function(t) {
      t.sigBytes -= 255 & t.words[t.sigBytes - 1 >>> 2];
    }
  }, r.BlockCipher = a.extend({
    cfg: a.cfg.extend({
      mode: f,
      padding: u
    }),
    reset: function() {
      a.reset.call(this);
      var t = (e = this.cfg).iv, e = e.mode;
      if (this._xformMode == this._ENC_XFORM_MODE) var r = e.createEncryptor; else r = e.createDecryptor,
        this._minBufferSize = 1;
      this._mode = r.call(e, this, t && t.words);
    },
    _doProcessBlock: function(t, e) {
      this._mode.processBlock(t, e);
    },
    _doFinalize: function() {
      var t = this.cfg.padding;
      if (this._xformMode == this._ENC_XFORM_MODE) {
        t.pad(this._data, this.blockSize);
        var e = this._process(!0);
      } else e = this._process(!0), t.unpad(e);
      return e;
    },
    blockSize: 4
  });
  var p = r.CipherParams = i.extend({
    init: function(t) {
      this.mixIn(t);
    },
    toString: function(t) {
      return (t || this.formatter).stringify(this);
    }
  }), f = (l.format = {}).OpenSSL = {
    stringify: function(t) {
      var e = t.ciphertext;
      return ((t = t.salt) ? n.create([ 1398893684, 1701076831 ]).concat(t).concat(e) : e).toString(s);
    },
    parse: function(t) {
      var e = (t = s.parse(t)).words;
      if (1398893684 == e[0] && 1701076831 == e[1]) {
        var r = n.create(e.slice(2, 4));
        e.splice(0, 4), t.sigBytes -= 16;
      }
      return p.create({
        ciphertext: t,
        salt: r
      });
    }
  }, d = r.SerializableCipher = i.extend({
    cfg: i.extend({
      format: f
    }),
    encrypt: function(t, e, r, i) {
      i = this.cfg.extend(i);
      var n = t.createEncryptor(r, i);
      return e = n.finalize(e), n = n.cfg, p.create({
        ciphertext: e,
        key: r,
        iv: n.iv,
        algorithm: t,
        mode: n.mode,
        padding: n.padding,
        blockSize: t.blockSize,
        formatter: i.format
      });
    },
    decrypt: function(t, e, r, i) {
      return i = this.cfg.extend(i), e = this._parse(e, i.format), t.createDecryptor(r, i).finalize(e.ciphertext);
    },
    _parse: function(t, e) {
      return "string" == typeof t ? e.parse(t, this) : t;
    }
  }), l = (l.kdf = {}).OpenSSL = {
    execute: function(t, e, r, i) {
      return i || (i = n.random(8)), t = c.create({
        keySize: e + r
      }).compute(t, i), r = n.create(t.words.slice(e), 4 * r), t.sigBytes = 4 * e, p.create({
        key: t,
        iv: r,
        salt: i
      });
    }
  }, y = r.PasswordBasedCipher = d.extend({
    cfg: d.cfg.extend({
      kdf: l
    }),
    encrypt: function(t, e, r, i) {
      return i = this.cfg.extend(i), r = i.kdf.execute(r, t.keySize, t.ivSize), i.iv = r.iv,
        (t = d.encrypt.call(this, t, e, r.key, i)).mixIn(r), t;
    },
    decrypt: function(t, e, r, i) {
      return i = this.cfg.extend(i), e = this._parse(e, i.format), r = i.kdf.execute(r, t.keySize, t.ivSize, e.salt),
        i.iv = r.iv, d.decrypt.call(this, t, e, r.key, i);
    }
  });
}(), function() {
  for (var e = t, r = e.lib.BlockCipher, i = e.algo, n = [], o = [], s = [], c = [], a = [], f = [], h = [], u = [], p = [], d = [], l = [], y = 0; 256 > y; y++) l[y] = 128 > y ? y << 1 : y << 1 ^ 283;
  for (var v = 0, _ = 0, y = 0; 256 > y; y++) {
    var g = (g = _ ^ _ << 1 ^ _ << 2 ^ _ << 3 ^ _ << 4) >>> 8 ^ 255 & g ^ 99;
    n[v] = g, o[g] = v;
    var B = l[v], k = l[B], x = l[k], m = 257 * l[g] ^ 16843008 * g;
    s[v] = m << 24 | m >>> 8, c[v] = m << 16 | m >>> 16, a[v] = m << 8 | m >>> 24, f[v] = m,
      m = 16843009 * x ^ 65537 * k ^ 257 * B ^ 16843008 * v, h[g] = m << 24 | m >>> 8,
      u[g] = m << 16 | m >>> 16, p[g] = m << 8 | m >>> 24, d[g] = m, v ? (v = B ^ l[l[l[x ^ B]]],
      _ ^= l[l[_]]) : v = _ = 1;
  }
  var S = [ 0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 ], i = i.AES = r.extend({
    _doReset: function() {
      for (var t = (r = this._key).words, e = r.sigBytes / 4, r = 4 * ((this._nRounds = e + 6) + 1), i = this._keySchedule = [], o = 0; o < r; o++) if (o < e) i[o] = t[o]; else {
        var s = i[o - 1];
        o % e ? 6 < e && 4 == o % e && (s = n[s >>> 24] << 24 | n[s >>> 16 & 255] << 16 | n[s >>> 8 & 255] << 8 | n[255 & s]) : (s = s << 8 | s >>> 24,
          s = n[s >>> 24] << 24 | n[s >>> 16 & 255] << 16 | n[s >>> 8 & 255] << 8 | n[255 & s],
          s ^= S[o / e | 0] << 24), i[o] = i[o - e] ^ s;
      }
      for (t = this._invKeySchedule = [], e = 0; e < r; e++) o = r - e, s = e % 4 ? i[o] : i[o - 4],
        t[e] = 4 > e || 4 >= o ? s : h[n[s >>> 24]] ^ u[n[s >>> 16 & 255]] ^ p[n[s >>> 8 & 255]] ^ d[n[255 & s]];
    },
    encryptBlock: function(t, e) {
      this._doCryptBlock(t, e, this._keySchedule, s, c, a, f, n);
    },
    decryptBlock: function(t, e) {
      var r = t[e + 1];
      t[e + 1] = t[e + 3], t[e + 3] = r, this._doCryptBlock(t, e, this._invKeySchedule, h, u, p, d, o),
        r = t[e + 1], t[e + 1] = t[e + 3], t[e + 3] = r;
    },
    _doCryptBlock: function(t, e, r, i, n, o, s, c) {
      for (var a = this._nRounds, f = t[e] ^ r[0], h = t[e + 1] ^ r[1], u = t[e + 2] ^ r[2], p = t[e + 3] ^ r[3], d = 4, l = 1; l < a; l++) var y = i[f >>> 24] ^ n[h >>> 16 & 255] ^ o[u >>> 8 & 255] ^ s[255 & p] ^ r[d++], v = i[h >>> 24] ^ n[u >>> 16 & 255] ^ o[p >>> 8 & 255] ^ s[255 & f] ^ r[d++], _ = i[u >>> 24] ^ n[p >>> 16 & 255] ^ o[f >>> 8 & 255] ^ s[255 & h] ^ r[d++], p = i[p >>> 24] ^ n[f >>> 16 & 255] ^ o[h >>> 8 & 255] ^ s[255 & u] ^ r[d++], f = y, h = v, u = _;
      y = (c[f >>> 24] << 24 | c[h >>> 16 & 255] << 16 | c[u >>> 8 & 255] << 8 | c[255 & p]) ^ r[d++],
        v = (c[h >>> 24] << 24 | c[u >>> 16 & 255] << 16 | c[p >>> 8 & 255] << 8 | c[255 & f]) ^ r[d++],
        _ = (c[u >>> 24] << 24 | c[p >>> 16 & 255] << 16 | c[f >>> 8 & 255] << 8 | c[255 & h]) ^ r[d++],
        p = (c[p >>> 24] << 24 | c[f >>> 16 & 255] << 16 | c[h >>> 8 & 255] << 8 | c[255 & u]) ^ r[d++],
        t[e] = y, t[e + 1] = v, t[e + 2] = _, t[e + 3] = p;
    },
    keySize: 8
  });
  e.AES = r._createHelper(i);
}(), t.enc.u8array = {
  stringify: function(t) {
    for (var e = t.words, r = t.sigBytes, i = new Uint8Array(r), n = 0; n < r; n++) {
      var o = e[n >>> 2] >>> 24 - n % 4 * 8 & 255;
      i[n] = o;
    }
    return i;
  },
  parse: function(e) {
    for (var r = e.length, i = [], n = 0; n < r; n++) i[n >>> 2] |= (255 & e[n]) << 24 - n % 4 * 8;
    return t.lib.WordArray.create(i, r);
  }
}, t.enc.int8array = {
  stringify: function(t) {
    for (var e = t.words, r = t.sigBytes, i = new Int8Array(r), n = 0; n < r; n++) {
      var o = e[n >>> 2] >> 24 - n % 4 * 8 & 255;
      i[n] = o;
    }
    return i;
  },
  parse: function(e) {
    for (var r = e.length, i = [], n = 0; n < r; n++) i[n >>> 2] |= (255 & e[n]) << 24 - n % 4 * 8;
    return t.lib.WordArray.create(i, r);
  }
}, t.enc.int16array = {
  stringify: function(t) {
    for (var e = t.words, r = t.sigBytes, i = new Uint8Array(r), n = 0; n < r; n++) {
      var o = e[n >>> 2] >>> 24 - n % 4 * 8 & 255;
      i[n] = o;
    }
    return i;
  },
  parse: function(e) {
    for (var r = e.length, i = [], n = 0; n < r; n++) i[n >>> 2] |= (255 & e[n]) << 24 - n % 4 * 8;
    return t.lib.WordArray.create(i, r);
  }
}, t.mode.CFB = function() {
  function e(t, e, r, i) {
    var n = this._iv;
    if (n) {
      o = n.slice(0);
      this._iv = void 0;
    } else var o = this._prevBlock;
    i.encryptBlock(o, 0);
    for (var s = 0; s < r; s++) t[e + s] ^= o[s];
  }
  var r = t.lib.BlockCipherMode.extend();
  return r.Encryptor = r.extend({
    processBlock: function(t, r) {
      var i = this._cipher, n = i.blockSize;
      e.call(this, t, r, n, i), this._prevBlock = t.slice(r, r + n);
    }
  }), r.Decryptor = r.extend({
    processBlock: function(t, r) {
      var i = this._cipher, n = i.blockSize, o = t.slice(r, r + n);
      e.call(this, t, r, n, i), this._prevBlock = o;
    }
  }), r;
}(), t.mode.ECB = function() {
  var e = t.lib.BlockCipherMode.extend();
  return e.Encryptor = e.extend({
    processBlock: function(t, e) {
      this._cipher.encryptBlock(t, e);
    }
  }), e.Decryptor = e.extend({
    processBlock: function(t, e) {
      this._cipher.decryptBlock(t, e);
    }
  }), e;
}(), t.pad.NoPadding = {
  pad: function() {},
  unpad: function() {}
}, module.exports = {
  CryptoJS: t
};
src/utils/http/axios/index.ts
@@ -26,257 +26,257 @@
 * @description: æ•°æ®å¤„理,方便区分多种处理方式
 */
const transform: AxiosTransform = {
  /**
   * @description: å¤„理请求数据。如果数据不是预期格式,可直接抛出错误
   */
  transformRequestHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
    //console.log(`output->res`, res)
    //console.log(`output->options`, options)
    const { t } = useI18n()
    const { isTransformResponse, isReturnNativeResponse } = options
    // æ˜¯å¦è¿”回原生响应头 æ¯”如:需要获取响应头时使用该属性
    if (isReturnNativeResponse) {
      return res
    }
    // ä¸è¿›è¡Œä»»ä½•处理,直接返回
    // ç”¨äºŽé¡µé¢ä»£ç å¯èƒ½éœ€è¦ç›´æŽ¥èŽ·å–code,data,message这些信息时开启
    if (!isTransformResponse) {
      return res.data
    }
    // é”™è¯¯çš„æ—¶å€™è¿”回
    /**
     * @description: å¤„理请求数据。如果数据不是预期格式,可直接抛出错误
     */
    transformRequestHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
        //console.log(`output->res`, res)
        //console.log(`output->options`, options)
        const { t } = useI18n()
        const { isTransformResponse, isReturnNativeResponse } = options
        // æ˜¯å¦è¿”回原生响应头 æ¯”如:需要获取响应头时使用该属性
        if (isReturnNativeResponse) {
            return res
        }
        // ä¸è¿›è¡Œä»»ä½•处理,直接返回
        // ç”¨äºŽé¡µé¢ä»£ç å¯èƒ½éœ€è¦ç›´æŽ¥èŽ·å–code,data,message这些信息时开启
        if (!isTransformResponse) {
            return res.data
        }
        // é”™è¯¯çš„æ—¶å€™è¿”回
    const { data } = res
    if (!data) {
      // return '[HTTP] Request has no return value';
      throw new Error(t('sys.api.apiRequestFailed'))
    }
    //  è¿™é‡Œ code,result,message为 åŽå°ç»Ÿä¸€çš„字段,需要在 types.ts内修改为项目自己的接口返回格式
    const { code, result, message, success } = data
    // è¿™é‡Œé€»è¾‘可以根据项目进行修改
    const hasSuccess = data && Reflect.has(data, 'code') && (code === ResultEnum.SUCCESS || code === 200)
    if (hasSuccess) {
      if (success && message && options.successMessageMode === 'success') {
        //信息成功提示
        createMessage.success(message)
      }
      return result
    }
        const { data } = res
        if (!data) {
            // return '[HTTP] Request has no return value';
            throw new Error(t('sys.api.apiRequestFailed'))
        }
        //  è¿™é‡Œ code,result,message为 åŽå°ç»Ÿä¸€çš„字段,需要在 types.ts内修改为项目自己的接口返回格式
        const { code, result, message, success } = data
        // è¿™é‡Œé€»è¾‘可以根据项目进行修改
        const hasSuccess = data && Reflect.has(data, 'code') && (code === ResultEnum.SUCCESS || code === 200)
        if (hasSuccess) {
            if (success && message && options.successMessageMode === 'success') {
                //信息成功提示
                createMessage.success(message)
            }
            return result
        }
    // åœ¨æ­¤å¤„根据自己项目的实际情况对不同的code执行不同的操作
    // å¦‚果不希望中断当前请求,请return数据,否则直接抛出异常即可
    let timeoutMsg = ''
    switch (code) {
      case ResultEnum.TIMEOUT:
        timeoutMsg = t('sys.api.timeoutMessage')
        const userStore = useUserStoreWithOut()
        userStore.setToken(undefined)
        userStore.logout(true)
        break
      default:
        if (message) {
          timeoutMsg = message
        }
    }
        // åœ¨æ­¤å¤„根据自己项目的实际情况对不同的code执行不同的操作
        // å¦‚果不希望中断当前请求,请return数据,否则直接抛出异常即可
        let timeoutMsg = ''
        switch (code) {
            case ResultEnum.TIMEOUT:
                timeoutMsg = t('sys.api.timeoutMessage')
                const userStore = useUserStoreWithOut()
                userStore.setToken(undefined)
                userStore.logout(true)
                break
            default:
                if (message) {
                    timeoutMsg = message
                }
        }
    // errorMessageMode=‘modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
    // errorMessageMode='none' ä¸€èˆ¬æ˜¯è°ƒç”¨æ—¶æ˜Žç¡®è¡¨ç¤ºä¸å¸Œæœ›è‡ªåŠ¨å¼¹å‡ºé”™è¯¯æç¤º
    if (options.errorMessageMode === 'modal') {
      createErrorModal({ title: t('sys.api.errorTip'), content: timeoutMsg })
    } else if (options.errorMessageMode === 'message') {
      createMessage.error(timeoutMsg)
    }
        // errorMessageMode=‘modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
        // errorMessageMode='none' ä¸€èˆ¬æ˜¯è°ƒç”¨æ—¶æ˜Žç¡®è¡¨ç¤ºä¸å¸Œæœ›è‡ªåŠ¨å¼¹å‡ºé”™è¯¯æç¤º
        if (options.errorMessageMode === 'modal') {
            createErrorModal({ title: t('sys.api.errorTip'), content: timeoutMsg })
        } else if (options.errorMessageMode === 'message') {
            createMessage.error(timeoutMsg)
        }
    throw new Error(timeoutMsg || t('sys.api.apiRequestFailed'))
  },
        throw new Error(timeoutMsg || t('sys.api.apiRequestFailed'))
    },
  // è¯·æ±‚之前处理config
  beforeRequestHook: (config, options) => {
    const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options
    // è¯·æ±‚之前处理config
    beforeRequestHook: (config, options) => {
        const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options
    if (joinPrefix) {
      config.url = `${urlPrefix}${config.url}`
    }
        if (joinPrefix) {
            config.url = `${urlPrefix}${config.url}`
        }
    if (apiUrl && isString(apiUrl)) {
      config.url = `${apiUrl}${config.url}`
    }
    const params = config.params || {}
    const data = config.data || false
    formatDate && data && !isString(data) && formatRequestDate(data)
    if (config.method?.toUpperCase() === RequestEnum.GET) {
      if (!isString(params)) {
        // ç»™ get è¯·æ±‚加上时间戳参数,避免从缓存中拿数据。
        config.params = Object.assign(params || {}, joinTimestamp(joinTime, false))
      } else {
        // å…¼å®¹restful风格
        config.url = config.url + params + `${joinTimestamp(joinTime, true)}`
        config.params = undefined
      }
    } else {
      if (!isString(params)) {
        formatDate && formatRequestDate(params)
        if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
          config.data = data
          config.params = params
        } else {
          // éžGET请求如果没有提供data,则将params视为data
          config.data = params
          config.params = undefined
        }
        if (joinParamsToUrl) {
          config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data))
        }
      } else {
        // å…¼å®¹restful风格
        config.url = config.url + params
        config.params = undefined
      }
    }
    return config
  },
        if (apiUrl && isString(apiUrl)) {
            config.url = `${apiUrl}${config.url}`
        }
        const params = config.params || {}
        const data = config.data || false
        formatDate && data && !isString(data) && formatRequestDate(data)
        if (config.method?.toUpperCase() === RequestEnum.GET) {
            if (!isString(params)) {
                // ç»™ get è¯·æ±‚加上时间戳参数,避免从缓存中拿数据。
                config.params = Object.assign(params || {}, joinTimestamp(joinTime, false))
            } else {
                // å…¼å®¹restful风格
                config.url = config.url + params + `${joinTimestamp(joinTime, true)}`
                config.params = undefined
            }
        } else {
            if (!isString(params)) {
                formatDate && formatRequestDate(params)
                if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
                    config.data = data
                    config.params = params
                } else {
                    // éžGET请求如果没有提供data,则将params视为data
                    config.data = params
                    config.params = undefined
                }
                if (joinParamsToUrl) {
                    config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data))
                }
            } else {
                // å…¼å®¹restful风格
                config.url = config.url + params
                config.params = undefined
            }
        }
        return config
    },
  /**
   * @description: è¯·æ±‚拦截器处理
   */
  requestInterceptors: (config: Recordable, options) => {
    // console.log(`output->config`, config);
    // è¯·æ±‚之前处理config
    const token = getToken()
    let tenantid = getTenantId()
    //--update-begin--author:liusq---date:20220325---for: å¢žåŠ vue3标记
    config.headers[ConfigEnum.VERSION] = 'v3'
    //--update-end--author:liusq---date:20220325---for:增加vue3标记
    if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
      // jwt token
      config.headers.Authorization = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token
      config.headers[ConfigEnum.TOKEN] = token
      //--update-begin--author:liusq---date:20210831---for:将签名和时间戳,添加在请求接口 Header
    /**
     * @description: è¯·æ±‚拦截器处理
     */
    requestInterceptors: (config: Recordable, options) => {
        // console.log(`output->config`, config);
        // è¯·æ±‚之前处理config
        const token = getToken()
        let tenantid = getTenantId()
        //--update-begin--author:liusq---date:20220325---for: å¢žåŠ vue3标记
        config.headers[ConfigEnum.VERSION] = 'v3'
        //--update-end--author:liusq---date:20220325---for:增加vue3标记
        if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
            // jwt token
            config.headers.Authorization = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token
            config.headers[ConfigEnum.TOKEN] = token
            //--update-begin--author:liusq---date:20210831---for:将签名和时间戳,添加在请求接口 Header
      // update-begin--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
      config.headers[ConfigEnum.TIMESTAMP] = signMd5Utils.getTimestamp()
      // update-end--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
            // update-begin--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
            config.headers[ConfigEnum.TIMESTAMP] = signMd5Utils.getTimestamp()
            // update-end--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
      config.headers[ConfigEnum.Sign] = signMd5Utils.getSign(config.url, config.params)
      //--update-end--author:liusq---date:20210831---for:将签名和时间戳,添加在请求接口 Header
      //--update-begin--author:liusq---date:20211105---for: for:将多租户id,添加在请求接口 Header
      if (!tenantid) {
        tenantid = '0'
      }
      config.headers[ConfigEnum.TENANT_ID] = tenantid
            config.headers[ConfigEnum.Sign] = signMd5Utils.getSign(config.url, config.params)
            //--update-end--author:liusq---date:20210831---for:将签名和时间戳,添加在请求接口 Header
            //--update-begin--author:liusq---date:20211105---for: for:将多租户id,添加在请求接口 Header
            if (!tenantid) {
                tenantid = '0'
            }
            config.headers[ConfigEnum.TENANT_ID] = tenantid
      //--update-end--author:liusq---date:20211105---for:将多租户id,添加在请求接口 Header
            //--update-end--author:liusq---date:20211105---for:将多租户id,添加在请求接口 Header
      // ========================================================================================
      // update-begin--author:sunjianlei---date:20220624--for: æ·»åŠ ä½Žä»£ç åº”ç”¨ID
      const routeParams = router.currentRoute.value.params
      if (routeParams.appId) {
        config.headers[ConfigEnum.X_LOW_APP_ID] = routeParams.appId
        // lowApp自定义筛选条件
        if (routeParams.lowAppFilter) {
          config.params = { ...config.params, ...JSON.parse(routeParams.lowAppFilter as string) }
          delete routeParams.lowAppFilter
        }
      }
      // update-end--author:sunjianlei---date:20220624--for: æ·»åŠ ä½Žä»£ç åº”ç”¨ID
      // ========================================================================================
    }
    return config
  },
            // ========================================================================================
            // update-begin--author:sunjianlei---date:20220624--for: æ·»åŠ ä½Žä»£ç åº”ç”¨ID
            const routeParams = router.currentRoute.value.params
            if (routeParams.appId) {
                config.headers[ConfigEnum.X_LOW_APP_ID] = routeParams.appId
                // lowApp自定义筛选条件
                if (routeParams.lowAppFilter) {
                    config.params = { ...config.params, ...JSON.parse(routeParams.lowAppFilter as string) }
                    delete routeParams.lowAppFilter
                }
            }
            // update-end--author:sunjianlei---date:20220624--for: æ·»åŠ ä½Žä»£ç åº”ç”¨ID
            // ========================================================================================
        }
        return config
    },
  /**
   * @description: å“åº”拦截器处理
   */
  responseInterceptors: (res: AxiosResponse<any>) => {
    return res
  },
    /**
     * @description: å“åº”拦截器处理
     */
    responseInterceptors: (res: AxiosResponse<any>) => {
        return res
    },
  /**
   * @description: å“åº”错误处理
   */
  responseInterceptorsCatch: (error: any) => {
    const { t } = useI18n()
    const errorLogStore = useErrorLogStoreWithOut()
    errorLogStore.addAjaxErrorInfo(error)
    const { response, code, message, config } = error || {}
    const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'
    //scott 20211022 token失效提示信息
    //const msg: string = response?.data?.error?.message ?? '';
    const msg: string = response?.data?.message ?? ''
    const err: string = error?.toString?.() ?? ''
    let errMessage = ''
    /**
     * @description: å“åº”错误处理
     */
    responseInterceptorsCatch: (error: any) => {
        const { t } = useI18n()
        const errorLogStore = useErrorLogStoreWithOut()
        errorLogStore.addAjaxErrorInfo(error)
        const { response, code, message, config } = error || {}
        const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'
        //scott 20211022 token失效提示信息
        //const msg: string = response?.data?.error?.message ?? '';
        const msg: string = response?.data?.message ?? ''
        const err: string = error?.toString?.() ?? ''
        let errMessage = ''
    try {
      if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
        errMessage = t('sys.api.apiTimeoutMessage')
      }
      if (err?.includes('Network Error')) {
        errMessage = t('sys.api.networkExceptionMsg')
      }
        try {
            if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
                errMessage = t('sys.api.apiTimeoutMessage')
            }
            if (err?.includes('Network Error')) {
                errMessage = t('sys.api.networkExceptionMsg')
            }
      if (errMessage) {
        if (errorMessageMode === 'modal') {
          createErrorModal({ title: t('sys.api.errorTip'), content: errMessage })
        } else if (errorMessageMode === 'message') {
          createMessage.error(errMessage)
        }
        return Promise.reject(error)
      }
    } catch (error) {
      throw new Error(error)
    }
            if (errMessage) {
                if (errorMessageMode === 'modal') {
                    createErrorModal({ title: t('sys.api.errorTip'), content: errMessage })
                } else if (errorMessageMode === 'message') {
                    createMessage.error(errMessage)
                }
                return Promise.reject(error)
            }
        } catch (error) {
            throw new Error(error)
        }
    checkStatus(error?.response?.status, msg, errorMessageMode)
    return Promise.reject(error)
  },
        checkStatus(error?.response?.status, msg, errorMessageMode)
        return Promise.reject(error)
    },
}
function createAxios(opt?: Partial<CreateAxiosOptions>) {
  return new VAxios(
    deepMerge(
      {
        // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
        // authentication schemes,e.g: Bearer
        // authenticationScheme: 'Bearer',
        authenticationScheme: '',
        timeout: 10 * 1000,
        // åŸºç¡€æŽ¥å£åœ°å€
        // baseURL: globSetting.apiUrl,
        headers: { 'Content-Type': ContentTypeEnum.JSON },
        // å¦‚果是form-data格式
        // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
        // æ•°æ®å¤„理方式
        transform,
        // é…ç½®é¡¹ï¼Œä¸‹é¢çš„选项都可以在独立的接口请求中覆盖
        requestOptions: {
          // é»˜è®¤å°†prefix æ·»åŠ åˆ°url
          joinPrefix: true,
          // æ˜¯å¦è¿”回原生响应头 æ¯”如:需要获取响应头时使用该属性
          isReturnNativeResponse: false,
          // éœ€è¦å¯¹è¿”回数据进行处理
          isTransformResponse: true,
          // post请求的时候添加参数到url
          joinParamsToUrl: false,
          // æ ¼å¼åŒ–提交参数时间
          formatDate: true,
          // å¼‚常消息提示类型
          errorMessageMode: 'message',
          // æˆåŠŸæ¶ˆæ¯æç¤ºç±»åž‹
          successMessageMode: 'success',
          // æŽ¥å£åœ°å€
          apiUrl: globSetting.apiUrl,
          // æŽ¥å£æ‹¼æŽ¥åœ°å€
          urlPrefix: urlPrefix,
          //  æ˜¯å¦åŠ å…¥æ—¶é—´æˆ³
          joinTime: true,
          // å¿½ç•¥é‡å¤è¯·æ±‚
          ignoreCancelToken: true,
          // æ˜¯å¦æºå¸¦token
          withToken: true,
        },
      },
      opt || {}
    )
  )
    return new VAxios(
        deepMerge(
            {
                // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
                // authentication schemes,e.g: Bearer
                // authenticationScheme: 'Bearer',
                authenticationScheme: '',
                timeout: 10 * 1000,
                // åŸºç¡€æŽ¥å£åœ°å€
                // baseURL: globSetting.apiUrl,
                headers: { 'Content-Type': ContentTypeEnum.JSON },
                // å¦‚果是form-data格式
                // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
                // æ•°æ®å¤„理方式
                transform,
                // é…ç½®é¡¹ï¼Œä¸‹é¢çš„选项都可以在独立的接口请求中覆盖
                requestOptions: {
                    // é»˜è®¤å°†prefix æ·»åŠ åˆ°url
                    joinPrefix: true,
                    // æ˜¯å¦è¿”回原生响应头 æ¯”如:需要获取响应头时使用该属性
                    isReturnNativeResponse: false,
                    // éœ€è¦å¯¹è¿”回数据进行处理
                    isTransformResponse: true,
                    // post请求的时候添加参数到url
                    joinParamsToUrl: false,
                    // æ ¼å¼åŒ–提交参数时间
                    formatDate: true,
                    // å¼‚常消息提示类型
                    errorMessageMode: 'message',
                    // æˆåŠŸæ¶ˆæ¯æç¤ºç±»åž‹
                    successMessageMode: 'success',
                    // æŽ¥å£åœ°å€
                    apiUrl: globSetting.apiUrl,
                    // æŽ¥å£æ‹¼æŽ¥åœ°å€
                    urlPrefix: urlPrefix,
                    //  æ˜¯å¦åŠ å…¥æ—¶é—´æˆ³
                    joinTime: true,
                    // å¿½ç•¥é‡å¤è¯·æ±‚
                    ignoreCancelToken: true,
                    // æ˜¯å¦æºå¸¦token
                    withToken: true,
                },
            },
            opt || {}
        )
    )
}
export const defHttp = createAxios()
src/utils/libgif/libgif.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,990 @@
/*
    SuperGif
    Example usage:
        <img src="./example1_preview.gif" rel:animated_src="./example1.gif" width="360" height="360" rel:auto_play="1" />
        <script type="text/javascript">
            $$('img').each(function (img_tag) {
                if (/.*\.gif/.test(img_tag.src)) {
                    var rub = new SuperGif({ gif: img_tag } );
                    rub.load();
                }
            });
        </script>
    Image tag attributes:
        rel:animated_src -    If this url is specified, it's loaded into the player instead of src.
                            This allows a preview frame to be shown until animated gif data is streamed into the canvas
        rel:auto_play -        Defaults to 1 if not specified. If set to zero, a call to the play() method is needed
    Constructor options args
        gif                 Required. The DOM element of an img tag.
        loop_mode            Optional. Setting this to false will force disable looping of the gif.
        auto_play             Optional. Same as the rel:auto_play attribute above, this arg overrides the img tag info.
        max_width            Optional. Scale images over max_width down to max_width. Helpful with mobile.
            on_end                Optional. Add a callback for when the gif reaches the end of a single loop (one iteration). The first argument passed will be the gif HTMLElement.
        loop_delay            Optional. The amount of time to pause (in ms) after each single loop (iteration).
        draw_while_loading    Optional. Determines whether the gif will be drawn to the canvas whilst it is loaded.
        show_progress_bar    Optional. Only applies when draw_while_loading is set to true.
    Instance methods
        // loading
        load( callback )        Loads the gif specified by the src or rel:animated_src sttributie of the img tag into a canvas element and then calls callback if one is passed
        load_url( src, callback )    Loads the gif file specified in the src argument into a canvas element and then calls callback if one is passed
        // play controls
        play -                Start playing the gif
        pause -                Stop playing the gif
        move_to(i) -        Move to frame i of the gif
        move_relative(i) -    Move i frames ahead (or behind if i < 0)
        // getters
        get_canvas            The canvas element that the gif is playing in. Handy for assigning event handlers to.
        get_playing            Whether or not the gif is currently playing
        get_loading            Whether or not the gif has finished loading/parsing
        get_auto_play        Whether or not the gif is set to play automatically
        get_length            The number of frames in the gif
        get_current_frame    The index of the currently displayed frame of the gif
        For additional customization (viewport inside iframe) these params may be passed:
        c_w, c_h - width and height of canvas
        vp_t, vp_l, vp_ w, vp_h - top, left, width and height of the viewport
        A bonus: few articles to understand what is going on
            http://enthusiasms.org/post/16976438906
            http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
            http://humpy77.deviantart.com/journal/Frame-Delay-Times-for-Animated-GIFs-214150546
*/
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
      define([], factory);
  } else if (typeof exports === 'object') {
      module.exports = factory();
  } else {
      root.SuperGif = factory();
  }
}(this, function () {
  // Generic functions
  var bitsToNum = function (ba) {
      return ba.reduce(function (s, n) {
          return s * 2 + n;
      }, 0);
  };
  var byteToBitArr = function (bite) {
      var a = [];
      for (var i = 7; i >= 0; i--) {
          a.push(!!(bite & (1 << i)));
      }
      return a;
  };
  // Stream
  /**
   * @constructor
   */
  // Make compiler happy.
  var Stream = function (data) {
      this.data = data;
      this.len = this.data.length;
      this.pos = 0;
      this.readByte = function () {
          if (this.pos >= this.data.length) {
              throw new Error('Attempted to read past end of stream.');
          }
          if (data instanceof Uint8Array)
              return data[this.pos++];
          else
              return data.charCodeAt(this.pos++) & 0xFF;
      };
      this.readBytes = function (n) {
          var bytes = [];
          for (var i = 0; i < n; i++) {
              bytes.push(this.readByte());
          }
          return bytes;
      };
      this.read = function (n) {
          var s = '';
          for (var i = 0; i < n; i++) {
              s += String.fromCharCode(this.readByte());
          }
          return s;
      };
      this.readUnsigned = function () { // Little-endian.
          var a = this.readBytes(2);
          return (a[1] << 8) + a[0];
      };
  };
  var lzwDecode = function (minCodeSize, data) {
      // TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String?
      var pos = 0; // Maybe this streaming thing should be merged with the Stream?
      var readCode = function (size) {
          var code = 0;
          for (var i = 0; i < size; i++) {
              if (data.charCodeAt(pos >> 3) & (1 << (pos & 7))) {
                  code |= 1 << i;
              }
              pos++;
          }
          return code;
      };
      var output = [];
      var clearCode = 1 << minCodeSize;
      var eoiCode = clearCode + 1;
      var codeSize = minCodeSize + 1;
      var dict = [];
      var clear = function () {
          dict = [];
          codeSize = minCodeSize + 1;
          for (var i = 0; i < clearCode; i++) {
              dict[i] = [i];
          }
          dict[clearCode] = [];
          dict[eoiCode] = null;
      };
      var code;
      var last;
      while (true) {
          last = code;
          code = readCode(codeSize);
          if (code === clearCode) {
              clear();
              continue;
          }
          if (code === eoiCode) break;
          if (code < dict.length) {
              if (last !== clearCode) {
                  dict.push(dict[last].concat(dict[code][0]));
              }
          }
          else {
              if (code !== dict.length) throw new Error('Invalid LZW code.');
              dict.push(dict[last].concat(dict[last][0]));
          }
          output.push.apply(output, dict[code]);
          if (dict.length === (1 << codeSize) && codeSize < 12) {
              // If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long.
              codeSize++;
          }
      }
      // I don't know if this is technically an error, but some GIFs do it.
      //if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.');
      return output;
  };
  // The actual parsing; returns an object with properties.
  var parseGIF = function (st, handler) {
      handler || (handler = {});
      // LZW (GIF-specific)
      var parseCT = function (entries) { // Each entry is 3 bytes, for RGB.
          var ct = [];
          for (var i = 0; i < entries; i++) {
              ct.push(st.readBytes(3));
          }
          return ct;
      };
      var readSubBlocks = function () {
          var size, data;
          data = '';
          do {
              size = st.readByte();
              data += st.read(size);
          } while (size !== 0);
          return data;
      };
      var parseHeader = function () {
          var hdr = {};
          hdr.sig = st.read(3);
          hdr.ver = st.read(3);
          if (hdr.sig !== 'GIF') throw new Error('Not a GIF file.'); // XXX: This should probably be handled more nicely.
          hdr.width = st.readUnsigned();
          hdr.height = st.readUnsigned();
          var bits = byteToBitArr(st.readByte());
          hdr.gctFlag = bits.shift();
          hdr.colorRes = bitsToNum(bits.splice(0, 3));
          hdr.sorted = bits.shift();
          hdr.gctSize = bitsToNum(bits.splice(0, 3));
          hdr.bgColor = st.readByte();
          hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
          if (hdr.gctFlag) {
              hdr.gct = parseCT(1 << (hdr.gctSize + 1));
          }
          handler.hdr && handler.hdr(hdr);
      };
      var parseExt = function (block) {
          var parseGCExt = function (block) {
              var blockSize = st.readByte(); // Always 4
              var bits = byteToBitArr(st.readByte());
              block.reserved = bits.splice(0, 3); // Reserved; should be 000.
              block.disposalMethod = bitsToNum(bits.splice(0, 3));
              block.userInput = bits.shift();
              block.transparencyGiven = bits.shift();
              block.delayTime = st.readUnsigned();
              block.transparencyIndex = st.readByte();
              block.terminator = st.readByte();
              handler.gce && handler.gce(block);
          };
          var parseComExt = function (block) {
              block.comment = readSubBlocks();
              handler.com && handler.com(block);
          };
          var parsePTExt = function (block) {
              // No one *ever* uses this. If you use it, deal with parsing it yourself.
              var blockSize = st.readByte(); // Always 12
              block.ptHeader = st.readBytes(12);
              block.ptData = readSubBlocks();
              handler.pte && handler.pte(block);
          };
          var parseAppExt = function (block) {
              var parseNetscapeExt = function (block) {
                  var blockSize = st.readByte(); // Always 3
                  block.unknown = st.readByte(); // ??? Always 1? What is this?
                  block.iterations = st.readUnsigned();
                  block.terminator = st.readByte();
                  handler.app && handler.app.NETSCAPE && handler.app.NETSCAPE(block);
              };
              var parseUnknownAppExt = function (block) {
                  block.appData = readSubBlocks();
                  // FIXME: This won't work if a handler wants to match on any identifier.
                  handler.app && handler.app[block.identifier] && handler.app[block.identifier](block);
              };
              var blockSize = st.readByte(); // Always 11
              block.identifier = st.read(8);
              block.authCode = st.read(3);
              switch (block.identifier) {
                  case 'NETSCAPE':
                      parseNetscapeExt(block);
                      break;
                  default:
                      parseUnknownAppExt(block);
                      break;
              }
          };
          var parseUnknownExt = function (block) {
              block.data = readSubBlocks();
              handler.unknown && handler.unknown(block);
          };
          block.label = st.readByte();
          switch (block.label) {
              case 0xF9:
                  block.extType = 'gce';
                  parseGCExt(block);
                  break;
              case 0xFE:
                  block.extType = 'com';
                  parseComExt(block);
                  break;
              case 0x01:
                  block.extType = 'pte';
                  parsePTExt(block);
                  break;
              case 0xFF:
                  block.extType = 'app';
                  parseAppExt(block);
                  break;
              default:
                  block.extType = 'unknown';
                  parseUnknownExt(block);
                  break;
          }
      };
      var parseImg = function (img) {
          var deinterlace = function (pixels, width) {
              // Of course this defeats the purpose of interlacing. And it's *probably*
              // the least efficient way it's ever been implemented. But nevertheless...
              var newPixels = new Array(pixels.length);
              var rows = pixels.length / width;
              var cpRow = function (toRow, fromRow) {
                  var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width);
                  newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels));
              };
              // See appendix E.
              var offsets = [0, 4, 2, 1];
              var steps = [8, 8, 4, 2];
              var fromRow = 0;
              for (var pass = 0; pass < 4; pass++) {
                  for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {
                      cpRow(toRow, fromRow)
                      fromRow++;
                  }
              }
              return newPixels;
          };
          img.leftPos = st.readUnsigned();
          img.topPos = st.readUnsigned();
          img.width = st.readUnsigned();
          img.height = st.readUnsigned();
          var bits = byteToBitArr(st.readByte());
          img.lctFlag = bits.shift();
          img.interlaced = bits.shift();
          img.sorted = bits.shift();
          img.reserved = bits.splice(0, 2);
          img.lctSize = bitsToNum(bits.splice(0, 3));
          if (img.lctFlag) {
              img.lct = parseCT(1 << (img.lctSize + 1));
          }
          img.lzwMinCodeSize = st.readByte();
          var lzwData = readSubBlocks();
          img.pixels = lzwDecode(img.lzwMinCodeSize, lzwData);
          if (img.interlaced) { // Move
              img.pixels = deinterlace(img.pixels, img.width);
          }
          handler.img && handler.img(img);
      };
      var parseBlock = function () {
          var block = {};
          block.sentinel = st.readByte();
          switch (String.fromCharCode(block.sentinel)) { // For ease of matching
              case '!':
                  block.type = 'ext';
                  parseExt(block);
                  break;
              case ',':
                  block.type = 'img';
                  parseImg(block);
                  break;
              case ';':
                  block.type = 'eof';
                  handler.eof && handler.eof(block);
                  break;
              default:
                  throw new Error('Unknown block: 0x' + block.sentinel.toString(16)); // TODO: Pad this with a 0.
          }
          if (block.type !== 'eof') setTimeout(parseBlock, 0);
      };
      var parse = function () {
          parseHeader();
          setTimeout(parseBlock, 0);
      };
      parse();
  };
  var SuperGif = function (opts) {
      var options = {
          //viewport position
          vp_l: 0,
          vp_t: 0,
          vp_w: null,
          vp_h: null,
          //canvas sizes
          c_w: null,
          c_h: null
      };
      for (var i in opts) { options[i] = opts[i] }
      if (options.vp_w && options.vp_h) options.is_vp = true;
      var stream;
      var hdr;
      var loadError = null;
      var loading = false;
      var transparency = null;
      var delay = null;
      var disposalMethod = null;
      var disposalRestoreFromIdx = null;
      var lastDisposalMethod = null;
      var frame = null;
      var lastImg = null;
      var playing = true;
      var forward = true;
      var ctx_scaled = false;
      var frames = [];
      var frameOffsets = []; // elements have .x and .y properties
      var gif = options.gif;
      if (typeof options.auto_play == 'undefined')
          options.auto_play = (!gif.getAttribute('rel:auto_play') || gif.getAttribute('rel:auto_play') == '1');
      var onEndListener = (options.hasOwnProperty('on_end') ? options.on_end : null);
      var loopDelay = (options.hasOwnProperty('loop_delay') ? options.loop_delay : 0);
      var overrideLoopMode = (options.hasOwnProperty('loop_mode') ? options.loop_mode : 'auto');
      var drawWhileLoading = (options.hasOwnProperty('draw_while_loading') ? options.draw_while_loading : true);
      var showProgressBar = drawWhileLoading ? (options.hasOwnProperty('show_progress_bar') ? options.show_progress_bar : true) : false;
      var progressBarHeight = (options.hasOwnProperty('progressbar_height') ? options.progressbar_height : 25);
      var progressBarBackgroundColor = (options.hasOwnProperty('progressbar_background_color') ? options.progressbar_background_color : 'rgba(255,255,255,0.4)');
      var progressBarForegroundColor = (options.hasOwnProperty('progressbar_foreground_color') ? options.progressbar_foreground_color : 'rgba(255,0,22,.8)');
      var clear = function () {
          transparency = null;
          delay = null;
          lastDisposalMethod = disposalMethod;
          disposalMethod = null;
          frame = null;
      };
      // XXX: There's probably a better way to handle catching exceptions when
      // callbacks are involved.
      var doParse = function () {
          try {
              parseGIF(stream, handler);
          }
          catch (err) {
              doLoadError('parse');
          }
      };
      var doText = function (text) {
          toolbar.innerHTML = text; // innerText? Escaping? Whatever.
          toolbar.style.visibility = 'visible';
      };
      var setSizes = function (w, h) {
          canvas.width = w * get_canvas_scale();
          canvas.height = h * get_canvas_scale();
          toolbar.style.minWidth = (w * get_canvas_scale()) + 'px';
          tmpCanvas.width = w;
          tmpCanvas.height = h;
          tmpCanvas.style.width = w + 'px';
          tmpCanvas.style.height = h + 'px';
          tmpCanvas.getContext('2d').setTransform(1, 0, 0, 1, 0, 0);
      };
      var setFrameOffset = function (frame, offset) {
          if (!frameOffsets[frame]) {
              frameOffsets[frame] = offset;
              return;
          }
          if (typeof offset.x !== 'undefined') {
              frameOffsets[frame].x = offset.x;
          }
          if (typeof offset.y !== 'undefined') {
              frameOffsets[frame].y = offset.y;
          }
      };
      var doShowProgress = function (pos, length, draw) {
          if (draw && showProgressBar) {
              var height = progressBarHeight;
              var left, mid, top, width;
              if (options.is_vp) {
                  if (!ctx_scaled) {
                      top = (options.vp_t + options.vp_h - height);
                      height = height;
                      left = options.vp_l;
                      mid = left + (pos / length) * options.vp_w;
                      width = canvas.width;
                  } else {
                      top = (options.vp_t + options.vp_h - height) / get_canvas_scale();
                      height = height / get_canvas_scale();
                      left = (options.vp_l / get_canvas_scale());
                      mid = left + (pos / length) * (options.vp_w / get_canvas_scale());
                      width = canvas.width / get_canvas_scale();
                  }
                  //some debugging, draw rect around viewport
                  if (false) {
                      if (!ctx_scaled) {
                          var l = options.vp_l, t = options.vp_t;
                          var w = options.vp_w, h = options.vp_h;
                      } else {
                          var l = options.vp_l / get_canvas_scale(), t = options.vp_t / get_canvas_scale();
                          var w = options.vp_w / get_canvas_scale(), h = options.vp_h / get_canvas_scale();
                      }
                      ctx.rect(l, t, w, h);
                      ctx.stroke();
                  }
              }
              else {
                  top = (canvas.height - height) / (ctx_scaled ? get_canvas_scale() : 1);
                  mid = ((pos / length) * canvas.width) / (ctx_scaled ? get_canvas_scale() : 1);
                  width = canvas.width / (ctx_scaled ? get_canvas_scale() : 1);
                  height /= ctx_scaled ? get_canvas_scale() : 1;
              }
              ctx.fillStyle = progressBarBackgroundColor;
              ctx.fillRect(mid, top, width - mid, height);
              ctx.fillStyle = progressBarForegroundColor;
              ctx.fillRect(0, top, mid, height);
          }
      };
      var doLoadError = function (originOfError) {
          var drawError = function () {
              ctx.fillStyle = 'black';
              ctx.fillRect(0, 0, options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height);
              ctx.strokeStyle = 'red';
              ctx.lineWidth = 3;
              ctx.moveTo(0, 0);
              ctx.lineTo(options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height);
              ctx.moveTo(0, options.c_h ? options.c_h : hdr.height);
              ctx.lineTo(options.c_w ? options.c_w : hdr.width, 0);
              ctx.stroke();
          };
          loadError = originOfError;
          hdr = {
              width: gif.width,
              height: gif.height
          }; // Fake header.
          frames = [];
          drawError();
      };
      var doHdr = function (_hdr) {
          hdr = _hdr;
          setSizes(hdr.width, hdr.height)
      };
      var doGCE = function (gce) {
          pushFrame();
          clear();
          transparency = gce.transparencyGiven ? gce.transparencyIndex : null;
          delay = gce.delayTime;
          disposalMethod = gce.disposalMethod;
          // We don't have much to do with the rest of GCE.
      };
      var pushFrame = function () {
          if (!frame) return;
          frames.push({
              data: frame.getImageData(0, 0, hdr.width, hdr.height),
              delay: delay
          });
          frameOffsets.push({ x: 0, y: 0 });
      };
      var doImg = function (img) {
          if (!frame) frame = tmpCanvas.getContext('2d');
          var currIdx = frames.length;
          //ct = color table, gct = global color table
          var ct = img.lctFlag ? img.lct : hdr.gct; // TODO: What if neither exists?
          /*
          Disposal method indicates the way in which the graphic is to
          be treated after being displayed.
          Values :    0 - No disposal specified. The decoder is
                          not required to take any action.
                      1 - Do not dispose. The graphic is to be left
                          in place.
                      2 - Restore to background color. The area used by the
                          graphic must be restored to the background color.
                      3 - Restore to previous. The decoder is required to
                          restore the area overwritten by the graphic with
                          what was there prior to rendering the graphic.
                          Importantly, "previous" means the frame state
                          after the last disposal of method 0, 1, or 2.
          */
          if (currIdx > 0) {
              if (lastDisposalMethod === 3) {
                  // Restore to previous
                  // If we disposed every frame including first frame up to this point, then we have
                  // no composited frame to restore to. In this case, restore to background instead.
                  if (disposalRestoreFromIdx !== null) {
                      frame.putImageData(frames[disposalRestoreFromIdx].data, 0, 0);
                  } else {
                      frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height);
                  }
              } else {
                  disposalRestoreFromIdx = currIdx - 1;
              }
              if (lastDisposalMethod === 2) {
                  // Restore to background color
                  // Browser implementations historically restore to transparent; we do the same.
                  // http://www.wizards-toolkit.org/discourse-server/viewtopic.php?f=1&t=21172#p86079
                  frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height);
              }
          }
          // else, Undefined/Do not dispose.
          // frame contains final pixel data from the last frame; do nothing
          //Get existing pixels for img region after applying disposal method
          var imgData = frame.getImageData(img.leftPos, img.topPos, img.width, img.height);
          //apply color table colors
          img.pixels.forEach(function (pixel, i) {
              // imgData.data === [R,G,B,A,R,G,B,A,...]
              if (pixel !== transparency) {
                  imgData.data[i * 4 + 0] = ct[pixel][0];
                  imgData.data[i * 4 + 1] = ct[pixel][1];
                  imgData.data[i * 4 + 2] = ct[pixel][2];
                  imgData.data[i * 4 + 3] = 255; // Opaque.
              }
          });
          frame.putImageData(imgData, img.leftPos, img.topPos);
          if (!ctx_scaled) {
              ctx.scale(get_canvas_scale(), get_canvas_scale());
              ctx_scaled = true;
          }
          // We could use the on-page canvas directly, except that we draw a progress
          // bar for each image chunk (not just the final image).
          if (drawWhileLoading) {
              ctx.drawImage(tmpCanvas, 0, 0);
              drawWhileLoading = options.auto_play;
          }
          lastImg = img;
      };
      var player = (function () {
          var i = -1;
          var iterationCount = 0;
          var showingInfo = false;
          var pinned = false;
          /**
           * Gets the index of the frame "up next".
           * @returns {number}
           */
          var getNextFrameNo = function () {
              var delta = (forward ? 1 : -1);
              return (i + delta + frames.length) % frames.length;
          };
          var stepFrame = function (amount) { // XXX: Name is confusing.
              i = i + amount;
              putFrame();
          };
          var step = (function () {
              var stepping = false;
              var completeLoop = function () {
                  if (onEndListener !== null)
                      onEndListener(gif);
                  iterationCount++;
                  if (overrideLoopMode !== false || iterationCount < 0) {
                      doStep();
                  } else {
                      stepping = false;
                      playing = false;
                  }
              };
              var doStep = function () {
                  stepping = playing;
                  if (!stepping) return;
                  stepFrame(1);
                  var delay = frames[i].delay * 10;
                  if (!delay) delay = 100; // FIXME: Should this even default at all? What should it be?
                  var nextFrameNo = getNextFrameNo();
                  if (nextFrameNo === 0) {
                      delay += loopDelay;
                      setTimeout(completeLoop, delay);
                  } else {
                      setTimeout(doStep, delay);
                  }
              };
              return function () {
                  if (!stepping) setTimeout(doStep, 0);
              };
          }());
          var putFrame = function () {
              var offset;
              i = parseInt(i, 10);
              if (i > frames.length - 1) {
                  i = 0;
              }
              if (i < 0) {
                  i = 0;
              }
              offset = frameOffsets[i];
              tmpCanvas.getContext("2d").putImageData(frames[i].data, offset.x, offset.y);
              ctx.globalCompositeOperation = "copy";
              ctx.drawImage(tmpCanvas, 0, 0);
          };
          var play = function () {
              playing = true;
              step();
          };
          var pause = function () {
              playing = false;
          };
          return {
              init: function () {
                  if (loadError) return;
                  if (!(options.c_w && options.c_h)) {
                      ctx.scale(get_canvas_scale(), get_canvas_scale());
                  }
                  if (options.auto_play) {
                      step();
                  }
                  else {
                      i = 0;
                      putFrame();
                  }
              },
              step: step,
              play: play,
              pause: pause,
              playing: playing,
              move_relative: stepFrame,
              current_frame: function () { return i; },
              length: function () { return frames.length },
              move_to: function (frame_idx) {
                  i = frame_idx;
                  putFrame();
              }
          }
      }());
      var doDecodeProgress = function (draw) {
          doShowProgress(stream.pos, stream.data.length, draw);
      };
      var doNothing = function () { };
      /**
       * @param{boolean=} draw Whether to draw progress bar or not; this is not idempotent because of translucency.
       *                       Note that this means that the text will be unsynchronized with the progress bar on non-frames;
       *                       but those are typically so small (GCE etc.) that it doesn't really matter. TODO: Do this properly.
       */
      var withProgress = function (fn, draw) {
          return function (block) {
              fn(block);
              doDecodeProgress(draw);
          };
      };
      var handler = {
          hdr: withProgress(doHdr),
          gce: withProgress(doGCE),
          com: withProgress(doNothing),
          // I guess that's all for now.
          app: {
              // TODO: Is there much point in actually supporting iterations?
              NETSCAPE: withProgress(doNothing)
          },
          img: withProgress(doImg, true),
          eof: function (block) {
              //toolbar.style.display = '';
              pushFrame();
              doDecodeProgress(false);
              if (!(options.c_w && options.c_h)) {
                  canvas.width = hdr.width * get_canvas_scale();
                  canvas.height = hdr.height * get_canvas_scale();
              }
              player.init();
              loading = false;
              if (load_callback) {
                  load_callback(gif);
              }
          }
      };
      var init = function () {
          var parent = gif.parentNode;
          var div = document.createElement('div');
          canvas = document.createElement('canvas');
          ctx = canvas.getContext('2d');
          toolbar = document.createElement('div');
          tmpCanvas = document.createElement('canvas');
          div.width = canvas.width = gif.width;
          div.height = canvas.height = gif.height;
          toolbar.style.minWidth = gif.width + 'px';
          div.className = 'jsgif';
          toolbar.className = 'jsgif_toolbar';
          div.appendChild(canvas);
          div.appendChild(toolbar);
          parent.insertBefore(div, gif);
          parent.removeChild(gif);
          if (options.c_w && options.c_h) setSizes(options.c_w, options.c_h);
          initialized = true;
      };
      var get_canvas_scale = function () {
          var scale;
          if (options.max_width && hdr && hdr.width > options.max_width) {
              scale = options.max_width / hdr.width;
          }
          else {
              scale = 1;
          }
          return scale;
      }
      var canvas, ctx, toolbar, tmpCanvas;
      var initialized = false;
      var load_callback = false;
      var load_setup = function (callback) {
          if (loading) return false;
          if (callback) load_callback = callback;
          else load_callback = false;
          loading = true;
          frames = [];
          clear();
          disposalRestoreFromIdx = null;
          lastDisposalMethod = null;
          frame = null;
          lastImg = null;
          return true;
      }
      return {
          // play controls
          play: player.play,
          pause: player.pause,
          move_relative: player.move_relative,
          move_to: player.move_to,
          // getters for instance vars
          get_playing: function () { return playing },
          get_canvas: function () { return canvas },
          get_canvas_scale: function () { return get_canvas_scale() },
          get_loading: function () { return loading },
          get_auto_play: function () { return options.auto_play },
          get_length: function () { return player.length() },
          get_current_frame: function () { return player.current_frame() },
          load_url: function (src, callback) {
              if (!load_setup(callback)) return;
              var h = new XMLHttpRequest();
              // new browsers (XMLHttpRequest2-compliant)
              h.open('GET', src, true);
              if ('overrideMimeType' in h) {
                  h.overrideMimeType('text/plain; charset=x-user-defined');
              }
              // old browsers (XMLHttpRequest-compliant)
              else if ('responseType' in h) {
                  h.responseType = 'arraybuffer';
              }
              // IE9 (Microsoft.XMLHTTP-compliant)
              else {
                  h.setRequestHeader('Accept-Charset', 'x-user-defined');
              }
              h.onloadstart = function () {
                  // Wait until connection is opened to replace the gif element with a canvas to avoid a blank img
                  if (!initialized) init();
              };
              h.onload = function (e) {
                  if (this.status != 200) {
                      doLoadError('xhr - response');
                  }
                  // emulating response field for IE9
                  if (!('response' in this)) {
                      this.response = new VBArray(this.responseText).toArray().map(String.fromCharCode).join('');
                  }
                  var data = this.response;
                  if (data.toString().indexOf("ArrayBuffer") > 0) {
                      data = new Uint8Array(data);
                  }
                  stream = new Stream(data);
                  setTimeout(doParse, 0);
              };
              h.onprogress = function (e) {
                  if (e.lengthComputable) doShowProgress(e.loaded, e.total, true);
              };
              h.onerror = function () { doLoadError('xhr'); };
              h.send();
          },
          load: function (callback) {
              this.load_url(gif.getAttribute('rel:animated_src') || gif.src, callback);
          },
          load_raw: function (arr, callback) {
              if (!load_setup(callback)) return;
              if (!initialized) init();
              stream = new Stream(arr);
              setTimeout(doParse, 0);
          },
          set_frame_offset: setFrameOffset
      };
  };
  return SuperGif;
}));
src/utils/public.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
const jm = require("./aes.js");
//  ç¡¬ä»¶æ–¹æä¾›çš„key,开发文档会说明的
var key = "d4acfcad77843a0ba17fe906c65255c6";
// åР坆
function encryptionData(data) {
  var byteKey = jm.CryptoJS.enc.Hex.parse(key);
  var byteData = jm.CryptoJS.enc.Hex.parse(data);
  var encrypt = jm.CryptoJS.AES.encrypt(byteData, byteKey, { mode: jm.CryptoJS.mode.ECB, padding: jm.CryptoJS.pad.NoPadding });
  var encryptedStr = encrypt.ciphertext.toString();
  return encryptedStr;
}
//解密
function decryptData(data) {
  var byteKey = jm.CryptoJS.enc.Hex.parse(key);
  var byteData = jm.CryptoJS.enc.Hex.parse(data);
  byteData = jm.CryptoJS.enc.Base64.stringify(byteData);
  var decrypt = jm.CryptoJS.AES.decrypt(byteData, byteKey, { mode: jm.CryptoJS.mode.ECB, padding: jm.CryptoJS.pad.NoPadding });
  var decryptedStr = decrypt.toString(jm.CryptoJS.enc.Hex);
  return decryptedStr.toString();
}
module.exports = {
  decryptData, //  è§£å¯†
  encryptionData, //  åР坆
}
src/views/demo/form/index.vue
@@ -1,621 +1,621 @@
<template>
  <PageWrapper title="表单基础示例" contentFullHeight>
    <CollapseContainer title="基础示例">
      <BasicForm
        autoFocusFirstItem
        :labelWidth="200"
        :schemas="schemas"
        :actionColOptions="{ span: 24 }"
        :labelCol="{ span: 8 }"
        @submit="handleSubmit"
        @reset="handleReset"
      >
        <template #jAreaLinkage="{ model, field }">
          <JAreaLinkage v-model:value="model[field]" :showArea="true" :showAll="false" />
        </template>
        <template #localSearch="{ model, field }">
          <ApiSelect
            :api="optionsListApi"
            showSearch
            v-model:value="model[field]"
            optionFilterProp="label"
            resultField="list"
            labelField="name"
            valueField="id"
          />
        </template>
        <template #selectA="{ model, field }">
          <a-select :options="optionsA" mode="multiple" v-model:value="model[field]" @change="valueSelectA = model[field]" allowClear />
        </template>
        <template #selectB="{ model, field }">
          <a-select :options="optionsB" mode="multiple" v-model:value="model[field]" @change="valueSelectB = model[field]" allowClear />
        </template>
        <template #remoteSearch="{ model, field }">
          <ApiSelect
            :api="optionsListApi"
            showSearch
            v-model:value="model[field]"
            :filterOption="false"
            resultField="list"
            labelField="name"
            valueField="id"
            @search="onSearch"
          />
        </template>
      </BasicForm>
    </CollapseContainer>
  </PageWrapper>
    <PageWrapper title="表单基础示例" contentFullHeight>
        <CollapseContainer title="基础示例">
            <BasicForm
                autoFocusFirstItem
                :labelWidth="200"
                :schemas="schemas"
                :actionColOptions="{ span: 24 }"
                :labelCol="{ span: 8 }"
                @submit="handleSubmit"
                @reset="handleReset"
            >
                <template #jAreaLinkage="{ model, field }">
                    <JAreaLinkage v-model:value="model[field]" :showArea="true" :showAll="false" />
                </template>
                <template #localSearch="{ model, field }">
                    <ApiSelect
                        :api="optionsListApi"
                        showSearch
                        v-model:value="model[field]"
                        optionFilterProp="label"
                        resultField="list"
                        labelField="name"
                        valueField="id"
                    />
                </template>
                <template #selectA="{ model, field }">
                    <a-select :options="optionsA" mode="multiple" v-model:value="model[field]" @change="valueSelectA = model[field]" allowClear />
                </template>
                <template #selectB="{ model, field }">
                    <a-select :options="optionsB" mode="multiple" v-model:value="model[field]" @change="valueSelectB = model[field]" allowClear />
                </template>
                <template #remoteSearch="{ model, field }">
                    <ApiSelect
                        :api="optionsListApi"
                        showSearch
                        v-model:value="model[field]"
                        :filterOption="false"
                        resultField="list"
                        labelField="name"
                        valueField="id"
                        @search="onSearch"
                    />
                </template>
            </BasicForm>
        </CollapseContainer>
    </PageWrapper>
</template>
<script lang="ts">
  import { computed, defineComponent, unref, ref } from 'vue';
  import { BasicForm, FormSchema, ApiSelect, JAreaLinkage } from '/@/components/Form/index';
  import { CollapseContainer } from '/@/components/Container';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { PageWrapper } from '/@/components/Page';
    import { computed, defineComponent, unref, ref } from 'vue'
    import { BasicForm, FormSchema, ApiSelect, JAreaLinkage } from '/@/components/Form/index'
    import { CollapseContainer } from '/@/components/Container'
    import { useMessage } from '/@/hooks/web/useMessage'
    import { PageWrapper } from '/@/components/Page'
  import { optionsListApi } from '/@/api/demo/select';
  import { useDebounceFn } from '@vueuse/core';
  import { treeOptionsListApi } from '/@/api/demo/tree';
  import { Select } from 'ant-design-vue';
  import { cloneDeep } from 'lodash-es';
    import { optionsListApi } from '/@/api/demo/select'
    import { useDebounceFn } from '@vueuse/core'
    import { treeOptionsListApi } from '/@/api/demo/tree'
    import { Select } from 'ant-design-vue'
    import { cloneDeep } from 'lodash-es'
  const valueSelectA = ref<string[]>([]);
  const valueSelectB = ref<string[]>([]);
  const options = ref<Recordable[]>([]);
  for (let i = 1; i < 10; i++) options.value.push({ label: '选项' + i, value: `${i}` });
    const valueSelectA = ref<string[]>([])
    const valueSelectB = ref<string[]>([])
    const options = ref<Recordable[]>([])
    for (let i = 1; i < 10; i++) options.value.push({ label: '选项' + i, value: `${i}` })
  const optionsA = computed(() => {
    return cloneDeep(unref(options)).map((op) => {
      op.disabled = unref(valueSelectB).indexOf(op.value) !== -1;
      return op;
    });
  });
  const optionsB = computed(() => {
    return cloneDeep(unref(options)).map((op) => {
      op.disabled = unref(valueSelectA).indexOf(op.value) !== -1;
      return op;
    });
  });
  const provincesOptions = [
    {
      id: 'guangdong',
      label: '广东省',
      value: '1',
      key: '1',
    },
    {
      id: 'jiangsu',
      label: '江苏省',
      value: '2',
      key: '2',
    },
  ];
  const citiesOptionsData = {
    guangdong: [
      {
        label: '珠海市',
        value: '1',
        key: '1',
      },
      {
        label: '深圳市',
        value: '2',
        key: '2',
      },
      {
        label: '广州市',
        value: '3',
        key: '3',
      },
    ],
    jiangsu: [
      {
        label: '南京市',
        value: '1',
        key: '1',
      },
      {
        label: '无锡市',
        value: '2',
        key: '2',
      },
      {
        label: '苏州市',
        value: '3',
        key: '3',
      },
    ],
  };
    const optionsA = computed(() => {
        return cloneDeep(unref(options)).map((op) => {
            op.disabled = unref(valueSelectB).indexOf(op.value) !== -1
            return op
        })
    })
    const optionsB = computed(() => {
        return cloneDeep(unref(options)).map((op) => {
            op.disabled = unref(valueSelectA).indexOf(op.value) !== -1
            return op
        })
    })
    const provincesOptions = [
        {
            id: 'guangdong',
            label: '广东省',
            value: '1',
            key: '1',
        },
        {
            id: 'jiangsu',
            label: '江苏省',
            value: '2',
            key: '2',
        },
    ]
    const citiesOptionsData = {
        guangdong: [
            {
                label: '珠海市',
                value: '1',
                key: '1',
            },
            {
                label: '深圳市',
                value: '2',
                key: '2',
            },
            {
                label: '广州市',
                value: '3',
                key: '3',
            },
        ],
        jiangsu: [
            {
                label: '南京市',
                value: '1',
                key: '1',
            },
            {
                label: '无锡市',
                value: '2',
                key: '2',
            },
            {
                label: '苏州市',
                value: '3',
                key: '3',
            },
        ],
    }
  const schemas: FormSchema[] = [
    {
      field: 'divider-basic',
      component: 'Divider',
      label: '基础字段',
    },
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
    const schemas: FormSchema[] = [
        {
            field: 'divider-basic',
            component: 'Divider',
            label: '基础字段',
        },
        {
            field: 'field1',
            component: 'Input',
            label: '字段1',
      colProps: {
        span: 8,
      },
      // componentProps:{},
      // can func
      componentProps: ({ schema, formModel }) => {
        console.log('form:', schema);
        console.log('formModel:', formModel);
        return {
          placeholder: '自定义placeholder',
          onChange: (e: any) => {
            console.log(e);
          },
        };
      },
      renderComponentContent: () => {
        return {
          prefix: () => 'pSlot',
          suffix: () => 'sSlot',
        };
      },
    },
    {
      field: 'field2',
      component: 'Input',
      label: '字段2',
      defaultValue: '111',
      colProps: {
        span: 8,
      },
      componentProps: {
        onChange: (e: any) => {
          console.log(e);
        },
      },
      suffix: '天',
    },
    {
      field: 'field3',
      component: 'DatePicker',
      label: '字段3',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field4',
      component: 'Select',
      label: '字段4',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
            key: '1',
          },
          {
            label: '选项2',
            value: '2',
            key: '2',
          },
        ],
      },
    },
    {
      field: 'field5',
      component: 'CheckboxGroup',
      label: '字段5',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
    },
    {
      field: 'field7',
      component: 'RadioGroup',
      label: '字段7',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
    },
    {
      field: 'field8',
      component: 'Checkbox',
      label: '字段8',
      colProps: {
        span: 8,
      },
      renderComponentContent: 'Check',
    },
    {
      field: 'field9',
      component: 'Switch',
      label: '字段9',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field10',
      component: 'RadioButtonGroup',
      label: '字段10',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            label: '选项1',
            value: '1',
          },
          {
            label: '选项2',
            value: '2',
          },
        ],
      },
    },
    {
      field: 'field11',
      component: 'Cascader',
      label: '字段11',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [
          {
            value: 'zhejiang',
            label: 'Zhejiang',
            children: [
              {
                value: 'hangzhou',
                label: 'Hangzhou',
                children: [
                  {
                    value: 'xihu',
                    label: 'West Lake',
                  },
                ],
              },
            ],
          },
          {
            value: 'jiangsu',
            label: 'Jiangsu',
            children: [
              {
                value: 'nanjing',
                label: 'Nanjing',
                children: [
                  {
                    value: 'zhonghuamen',
                    label: 'Zhong Hua Men',
                  },
                ],
              },
            ],
          },
        ],
      },
    },
    {
      field: 'divider-api-select',
      component: 'Divider',
      label: '远程下拉演示',
    },
    {
      field: 'field30',
      component: 'ApiSelect',
      label: '懒加载远程下拉',
      required: true,
      componentProps: {
        // more details see /src/components/Form/src/components/ApiSelect.vue
        api: optionsListApi,
        params: {
          id: 1,
        },
        resultField: 'list',
        // use name as label
        labelField: 'name',
        // use id as value
        valueField: 'id',
        // not request untill to select
        immediate: false,
        onChange: (e) => {
          console.log('selected:', e);
        },
        // atfer request callback
        onOptionsChange: (options) => {
          console.log('get options', options.length, options);
        },
      },
      colProps: {
        span: 8,
      },
      defaultValue: '0',
    },
    {
      field: 'field311',
      component: 'JAreaLinkage',
      label: '省市区选择',
      helpMessage: ['JAreaLinkage组件', '省市区选择'],
      required: true,
      slot: 'jAreaLinkage',
      colProps: {
        span: 8,
      },
      defaultValue: ['130000', '130200'],
    },
    {
      field: 'field31',
      component: 'Input',
      label: '下拉本地搜索',
      helpMessage: ['ApiSelect组件', '远程数据源本地搜索', '只发起一次请求获取所有选项'],
      required: true,
      slot: 'localSearch',
      colProps: {
        span: 8,
      },
      defaultValue: '0',
    },
    {
      field: 'field32',
      component: 'Input',
      label: '下拉远程搜索',
      helpMessage: ['ApiSelect组件', '将关键词发送到接口进行远程搜索'],
      required: true,
      slot: 'remoteSearch',
      colProps: {
        span: 8,
      },
      defaultValue: '0',
    },
    {
      field: 'field33',
      component: 'ApiTreeSelect',
      label: '远程下拉树',
      helpMessage: ['ApiTreeSelect组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: treeOptionsListApi,
        resultField: 'list',
      },
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field34',
      component: 'ApiRadioGroup',
      label: '远程Radio',
      helpMessage: ['ApiRadioGroup组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: optionsListApi,
        params: {
          count: 2,
        },
        resultField: 'list',
        // use name as label
        labelField: 'name',
        // use id as value
        valueField: 'id',
      },
      defaultValue: '1',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field35',
      component: 'ApiRadioGroup',
      label: '远程Radio',
      helpMessage: ['ApiRadioGroup组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: optionsListApi,
        params: {
          count: 2,
        },
        resultField: 'list',
        // use name as label
        labelField: 'name',
        // use id as value
        valueField: 'id',
        isBtn: true,
      },
      colProps: {
        span: 8,
      },
    },
    {
      field: 'divider-linked',
      component: 'Divider',
      label: '字段联动',
    },
    {
      field: 'province',
      component: 'Select',
      label: '省份',
      colProps: {
        span: 8,
      },
      componentProps: ({ formModel, formActionType }) => {
        return {
          options: provincesOptions,
          placeholder: '省份与城市联动',
          onChange: (e: any) => {
            // console.log(e)
            let citiesOptions = e == 1 ? citiesOptionsData[provincesOptions[0].id] : citiesOptionsData[provincesOptions[1].id];
            // console.log(citiesOptions)
            if (e === undefined) {
              citiesOptions = [];
            }
            formModel.city = undefined; //  reset city value
            const { updateSchema } = formActionType;
            updateSchema({
              field: 'city',
              componentProps: {
                options: citiesOptions,
              },
            });
          },
        };
      },
    },
    {
      field: 'city',
      component: 'Select',
      label: '城市',
      colProps: {
        span: 8,
      },
      componentProps: {
        options: [], // defalut []
        placeholder: '省份与城市联动',
      },
    },
    {
      field: 'divider-selects',
      component: 'Divider',
      label: '互斥多选',
      helpMessage: ['两个Select共用数据源', '但不可选择对方已选中的项目'],
    },
    {
      field: 'selectA',
      component: 'Select',
      label: '互斥SelectA',
      slot: 'selectA',
      defaultValue: [],
      colProps: {
        span: 8,
      },
    },
    {
      field: 'selectB',
      component: 'Select',
      label: '互斥SelectB',
      slot: 'selectB',
      defaultValue: [],
      colProps: {
        span: 8,
      },
    },
    {
      field: 'divider-others',
      component: 'Divider',
      label: '其它',
    },
    {
      field: 'field20',
      component: 'InputNumber',
      label: '字段20',
      required: true,
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field21',
      component: 'Slider',
      label: '字段21',
      componentProps: {
        min: 0,
        max: 100,
        range: true,
        marks: {
          20: '20°C',
          60: '60°C',
        },
      },
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field22',
      component: 'Rate',
      label: '字段22',
      defaultValue: 3,
      colProps: {
        span: 8,
      },
      componentProps: {
        disabled: false,
        allowHalf: true,
      },
    },
  ];
            colProps: {
                span: 8,
            },
            // componentProps:{},
            // can func
            componentProps: ({ schema, formModel }) => {
                console.log('form:', schema)
                console.log('formModel:', formModel)
                return {
                    placeholder: '自定义placeholder',
                    onChange: (e: any) => {
                        console.log(e)
                    },
                }
            },
            renderComponentContent: () => {
                return {
                    prefix: () => 'pSlot',
                    suffix: () => 'sSlot',
                }
            },
        },
        {
            field: 'field2',
            component: 'Input',
            label: '字段2',
            defaultValue: '111',
            colProps: {
                span: 8,
            },
            componentProps: {
                onChange: (e: any) => {
                    console.log(e)
                },
            },
            suffix: '天',
        },
        {
            field: 'field3',
            component: 'DatePicker',
            label: '字段3',
            colProps: {
                span: 8,
            },
        },
        {
            field: 'field4',
            component: 'Select',
            label: '字段4',
            colProps: {
                span: 8,
            },
            componentProps: {
                options: [
                    {
                        label: '选项1',
                        value: '1',
                        key: '1',
                    },
                    {
                        label: '选项2',
                        value: '2',
                        key: '2',
                    },
                ],
            },
        },
        {
            field: 'field5',
            component: 'CheckboxGroup',
            label: '字段5',
            colProps: {
                span: 8,
            },
            componentProps: {
                options: [
                    {
                        label: '选项1',
                        value: '1',
                    },
                    {
                        label: '选项2',
                        value: '2',
                    },
                ],
            },
        },
        {
            field: 'field7',
            component: 'RadioGroup',
            label: '字段7',
            colProps: {
                span: 8,
            },
            componentProps: {
                options: [
                    {
                        label: '选项1',
                        value: '1',
                    },
                    {
                        label: '选项2',
                        value: '2',
                    },
                ],
            },
        },
        {
            field: 'field8',
            component: 'Checkbox',
            label: '字段8',
            colProps: {
                span: 8,
            },
            renderComponentContent: 'Check',
        },
        {
            field: 'field9',
            component: 'Switch',
            label: '字段9',
            colProps: {
                span: 8,
            },
        },
        {
            field: 'field10',
            component: 'RadioButtonGroup',
            label: '字段10',
            colProps: {
                span: 8,
            },
            componentProps: {
                options: [
                    {
                        label: '选项1',
                        value: '1',
                    },
                    {
                        label: '选项2',
                        value: '2',
                    },
                ],
            },
        },
        {
            field: 'field11',
            component: 'Cascader',
            label: '字段11',
            colProps: {
                span: 8,
            },
            componentProps: {
                options: [
                    {
                        value: 'zhejiang',
                        label: 'Zhejiang',
                        children: [
                            {
                                value: 'hangzhou',
                                label: 'Hangzhou',
                                children: [
                                    {
                                        value: 'xihu',
                                        label: 'West Lake',
                                    },
                                ],
                            },
                        ],
                    },
                    {
                        value: 'jiangsu',
                        label: 'Jiangsu',
                        children: [
                            {
                                value: 'nanjing',
                                label: 'Nanjing',
                                children: [
                                    {
                                        value: 'zhonghuamen',
                                        label: 'Zhong Hua Men',
                                    },
                                ],
                            },
                        ],
                    },
                ],
            },
        },
        {
            field: 'divider-api-select',
            component: 'Divider',
            label: '远程下拉演示',
        },
        {
            field: 'field30',
            component: 'ApiSelect',
            label: '懒加载远程下拉',
            required: true,
            componentProps: {
                // more details see /src/components/Form/src/components/ApiSelect.vue
                api: optionsListApi,
                params: {
                    id: 1,
                },
                resultField: 'list',
                // use name as label
                labelField: 'name',
                // use id as value
                valueField: 'id',
                // not request untill to select
                immediate: false,
                onChange: (e) => {
                    console.log('selected:', e)
                },
                // atfer request callback
                onOptionsChange: (options) => {
                    console.log('get options', options.length, options)
                },
            },
            colProps: {
                span: 8,
            },
            defaultValue: '0',
        },
        {
            field: 'field311',
            component: 'JAreaLinkage',
            label: '省市区选择',
            helpMessage: ['JAreaLinkage组件', '省市区选择'],
            required: true,
            slot: 'jAreaLinkage',
            colProps: {
                span: 8,
            },
            defaultValue: ['130000', '130200'],
        },
        {
            field: 'field31',
            component: 'Input',
            label: '下拉本地搜索',
            helpMessage: ['ApiSelect组件', '远程数据源本地搜索', '只发起一次请求获取所有选项'],
            required: true,
            slot: 'localSearch',
            colProps: {
                span: 8,
            },
            defaultValue: '0',
        },
        {
            field: 'field32',
            component: 'Input',
            label: '下拉远程搜索',
            helpMessage: ['ApiSelect组件', '将关键词发送到接口进行远程搜索'],
            required: true,
            slot: 'remoteSearch',
            colProps: {
                span: 8,
            },
            defaultValue: '0',
        },
        {
            field: 'field33',
            component: 'ApiTreeSelect',
            label: '远程下拉树',
            helpMessage: ['ApiTreeSelect组件', '使用接口提供的数据生成选项'],
            required: true,
            componentProps: {
                api: treeOptionsListApi,
                resultField: 'list',
            },
            colProps: {
                span: 8,
            },
        },
        {
            field: 'field34',
            component: 'ApiRadioGroup',
            label: '远程Radio',
            helpMessage: ['ApiRadioGroup组件', '使用接口提供的数据生成选项'],
            required: true,
            componentProps: {
                api: optionsListApi,
                params: {
                    count: 2,
                },
                resultField: 'list',
                // use name as label
                labelField: 'name',
                // use id as value
                valueField: 'id',
            },
            defaultValue: '1',
            colProps: {
                span: 8,
            },
        },
        {
            field: 'field35',
            component: 'ApiRadioGroup',
            label: '远程Radio',
            helpMessage: ['ApiRadioGroup组件', '使用接口提供的数据生成选项'],
            required: true,
            componentProps: {
                api: optionsListApi,
                params: {
                    count: 2,
                },
                resultField: 'list',
                // use name as label
                labelField: 'name',
                // use id as value
                valueField: 'id',
                isBtn: true,
            },
            colProps: {
                span: 8,
            },
        },
        {
            field: 'divider-linked',
            component: 'Divider',
            label: '字段联动',
        },
        {
            field: 'province',
            component: 'Select',
            label: '省份',
            colProps: {
                span: 8,
            },
            componentProps: ({ formModel, formActionType }) => {
                return {
                    options: provincesOptions,
                    placeholder: '省份与城市联动',
                    onChange: (e: any) => {
                        // console.log(e)
                        let citiesOptions = e == 1 ? citiesOptionsData[provincesOptions[0].id] : citiesOptionsData[provincesOptions[1].id]
                        // console.log(citiesOptions)
                        if (e === undefined) {
                            citiesOptions = []
                        }
                        formModel.city = undefined //  reset city value
                        const { updateSchema } = formActionType
                        updateSchema({
                            field: 'city',
                            componentProps: {
                                options: citiesOptions,
                            },
                        })
                    },
                }
            },
        },
        {
            field: 'city',
            component: 'Select',
            label: '城市',
            colProps: {
                span: 8,
            },
            componentProps: {
                options: [], // defalut []
                placeholder: '省份与城市联动',
            },
        },
        {
            field: 'divider-selects',
            component: 'Divider',
            label: '互斥多选',
            helpMessage: ['两个Select共用数据源', '但不可选择对方已选中的项目'],
        },
        {
            field: 'selectA',
            component: 'Select',
            label: '互斥SelectA',
            slot: 'selectA',
            defaultValue: [],
            colProps: {
                span: 8,
            },
        },
        {
            field: 'selectB',
            component: 'Select',
            label: '互斥SelectB',
            slot: 'selectB',
            defaultValue: [],
            colProps: {
                span: 8,
            },
        },
        {
            field: 'divider-others',
            component: 'Divider',
            label: '其它',
        },
        {
            field: 'field20',
            component: 'InputNumber',
            label: '字段20',
            required: true,
            colProps: {
                span: 8,
            },
        },
        {
            field: 'field21',
            component: 'Slider',
            label: '字段21',
            componentProps: {
                min: 0,
                max: 100,
                range: true,
                marks: {
                    20: '20°C',
                    60: '60°C',
                },
            },
            colProps: {
                span: 8,
            },
        },
        {
            field: 'field22',
            component: 'Rate',
            label: '字段22',
            defaultValue: 3,
            colProps: {
                span: 8,
            },
            componentProps: {
                disabled: false,
                allowHalf: true,
            },
        },
    ]
  export default defineComponent({
    components: {
      BasicForm,
      CollapseContainer,
      PageWrapper,
      ApiSelect,
      JAreaLinkage,
      ASelect: Select,
    },
    setup() {
      const check = ref(null);
      const { createMessage } = useMessage();
      const keyword = ref<string>('');
      const searchParams = computed<Recordable>(() => {
        return { keyword: unref(keyword) };
      });
    export default defineComponent({
        components: {
            BasicForm,
            CollapseContainer,
            PageWrapper,
            ApiSelect,
            JAreaLinkage,
            ASelect: Select,
        },
        setup() {
            const check = ref(null)
            const { createMessage } = useMessage()
            const keyword = ref<string>('')
            const searchParams = computed<Recordable>(() => {
                return { keyword: unref(keyword) }
            })
      function onSearch(value: string) {
        keyword.value = value;
      }
      function areaChange(value) {
        alert(value);
      }
            function onSearch(value: string) {
                keyword.value = value
            }
            function areaChange(value) {
                alert(value)
            }
      return {
        schemas,
        optionsListApi,
        optionsA,
        optionsB,
        valueSelectA,
        valueSelectB,
        onSearch: useDebounceFn(onSearch, 300),
        searchParams,
        handleReset: () => {
          keyword.value = '';
        },
        handleSubmit: (values: any) => {
          createMessage.success('click search,values:' + JSON.stringify(values));
        },
        check,
      };
    },
  });
            return {
                schemas,
                optionsListApi,
                optionsA,
                optionsB,
                valueSelectA,
                valueSelectB,
                onSearch: useDebounceFn(onSearch, 300),
                searchParams,
                handleReset: () => {
                    keyword.value = ''
                },
                handleSubmit: (values: any) => {
                    createMessage.success('click search,values:' + JSON.stringify(values))
                },
                check,
            }
        },
    })
</script>
src/views/demo/page/list/basic/index.vue
@@ -1,161 +1,161 @@
<template>
  <PageWrapper :class="prefixCls" title="标准列表">
    <div :class="`${prefixCls}__top`">
      <a-row :gutter="12">
        <a-col :span="8" :class="`${prefixCls}__top-col`">
          <div>我的待办</div>
          <p>8个任务</p>
        </a-col>
        <a-col :span="8" :class="`${prefixCls}__top-col`">
          <div>本周任务平均处理时间</div>
          <p>32分钟</p>
        </a-col>
        <a-col :span="8" :class="`${prefixCls}__top-col`">
          <div>本周完成任务数</div>
          <p>24个任务</p>
        </a-col>
      </a-row>
    </div>
    <PageWrapper :class="prefixCls" title="标准列表">
        <div :class="`${prefixCls}__top`">
            <a-row :gutter="12">
                <a-col :span="8" :class="`${prefixCls}__top-col`">
                    <div>我的待办</div>
                    <p>8个任务</p>
                </a-col>
                <a-col :span="8" :class="`${prefixCls}__top-col`">
                    <div>本周任务平均处理时间</div>
                    <p>32分钟</p>
                </a-col>
                <a-col :span="8" :class="`${prefixCls}__top-col`">
                    <div>本周完成任务数</div>
                    <p>24个任务</p>
                </a-col>
            </a-row>
        </div>
    <div :class="`${prefixCls}__content`">
      <a-list :pagination="pagination">
        <template v-for="item in list" :key="item.id">
          <a-list-item class="list">
            <a-list-item-meta>
              <template #avatar>
                <Icon class="icon" v-if="item.icon" :icon="item.icon" :color="item.color" />
              </template>
              <template #title>
                <span>{{ item.title }}</span>
                <div class="extra" v-if="item.extra">
                  {{ item.extra }}
                </div>
              </template>
              <template #description>
                <div class="description">
                  {{ item.description }}
                </div>
                <div class="info">
                  <div><span>Owner</span>{{ item.author }}</div>
                  <div><span>开始时间</span>{{ item.datetime }}</div>
                </div>
                <div class="progress">
                  <Progress :percent="item.percent" status="active" />
                </div>
              </template>
            </a-list-item-meta>
          </a-list-item>
        </template>
      </a-list>
    </div>
  </PageWrapper>
        <div :class="`${prefixCls}__content`">
            <a-list :pagination="pagination">
                <template v-for="item in list" :key="item.id">
                    <a-list-item class="list">
                        <a-list-item-meta>
                            <template #avatar>
                                <Icon class="icon" v-if="item.icon" :icon="item.icon" :color="item.color" />
                            </template>
                            <template #title>
                                <span>{{ item.title }}</span>
                                <div class="extra" v-if="item.extra">
                                    {{ item.extra }}
                                </div>
                            </template>
                            <template #description>
                                <div class="description">
                                    {{ item.description }}
                                </div>
                                <div class="info">
                                    <div><span>Owner</span>{{ item.author }}</div>
                                    <div><span>开始时间</span>{{ item.datetime }}</div>
                                </div>
                                <div class="progress">
                                    <Progress :percent="item.percent" status="active" />
                                </div>
                            </template>
                        </a-list-item-meta>
                    </a-list-item>
                </template>
            </a-list>
        </div>
    </PageWrapper>
</template>
<script lang="ts">
  import { Progress, Row, Col } from 'ant-design-vue';
  import { defineComponent } from 'vue';
  import Icon from '/@/components/Icon/index';
  import { cardList } from './data';
  import { PageWrapper } from '/@/components/Page';
  import { List } from 'ant-design-vue';
    import { Progress, Row, Col } from 'ant-design-vue'
    import { defineComponent } from 'vue'
    import Icon from '/@/components/Icon/index'
    import { cardList } from './data'
    import { PageWrapper } from '/@/components/Page'
    import { List } from 'ant-design-vue'
  export default defineComponent({
    components: {
      Icon,
      Progress,
      PageWrapper,
      [List.name]: List,
      [List.Item.name]: List.Item,
      AListItemMeta: List.Item.Meta,
      [Row.name]: Row,
      [Col.name]: Col,
    },
    setup() {
      return {
        prefixCls: 'list-basic',
        list: cardList,
        pagination: {
          show: true,
          pageSize: 3,
        },
      };
    },
  });
    export default defineComponent({
        components: {
            Icon,
            Progress,
            PageWrapper,
            [List.name]: List,
            [List.Item.name]: List.Item,
            AListItemMeta: List.Item.Meta,
            [Row.name]: Row,
            [Col.name]: Col,
        },
        setup() {
            return {
                prefixCls: 'list-basic',
                list: cardList,
                pagination: {
                    show: true,
                    pageSize: 3,
                },
            }
        },
    })
</script>
<style lang="less" scoped>
  .list-basic {
    &__top {
      padding: 24px;
      text-align: center;
      background-color: @component-background;
    .list-basic {
        &__top {
            padding: 24px;
            text-align: center;
            background-color: @component-background;
      &-col {
        &:not(:last-child) {
          border-right: 1px dashed @border-color-base;
        }
            &-col {
                &:not(:last-child) {
                    border-right: 1px dashed @border-color-base;
                }
        div {
          margin-bottom: 12px;
          font-size: 14px;
          line-height: 22px;
          color: @text-color;
        }
                div {
                    margin-bottom: 12px;
                    font-size: 14px;
                    line-height: 22px;
                    color: @text-color;
                }
        p {
          margin: 0;
          font-size: 24px;
          line-height: 32px;
          color: @text-color;
        }
      }
    }
                p {
                    margin: 0;
                    font-size: 24px;
                    line-height: 32px;
                    color: @text-color;
                }
            }
        }
    &__content {
      padding: 24px;
      margin-top: 12px;
      background-color: @component-background;
        &__content {
            padding: 24px;
            margin-top: 12px;
            background-color: @component-background;
      .list {
        position: relative;
      }
            .list {
                position: relative;
            }
      .icon {
        font-size: 40px !important;
      }
            .icon {
                font-size: 40px !important;
            }
      .extra {
        position: absolute;
        top: 20px;
        right: 15px;
        font-weight: normal;
        color: @primary-color;
        cursor: pointer;
      }
            .extra {
                position: absolute;
                top: 20px;
                right: 15px;
                font-weight: normal;
                color: @primary-color;
                cursor: pointer;
            }
      .description {
        display: inline-block;
        width: 40%;
      }
            .description {
                display: inline-block;
                width: 40%;
            }
      .info {
        display: inline-block;
        width: 30%;
        text-align: center;
            .info {
                display: inline-block;
                width: 30%;
                text-align: center;
        div {
          display: inline-block;
          padding: 0 20px;
                div {
                    display: inline-block;
                    padding: 0 20px;
          span {
            display: block;
          }
        }
      }
                    span {
                        display: block;
                    }
                }
            }
      .progress {
        display: inline-block;
        width: 15%;
        vertical-align: top;
      }
    }
  }
            .progress {
                display: inline-block;
                width: 15%;
                vertical-align: top;
            }
        }
    }
</style>
src/views/dry/DryEquipmentList.vue
@@ -1,169 +1,169 @@
<template>
  <div>
    <!--引用表格-->
    <BasicTable @register="registerTable" :rowSelection="rowSelection">
      <!--插槽:table标题-->
      <template #tableTitle>
        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
        <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
        <a-dropdown v-if="selectedRowKeys.length > 0">
          <template #overlay>
            <a-menu>
              <a-menu-item key="1" @click="batchHandleDelete">
                <Icon icon="ant-design:delete-outlined" />
                åˆ é™¤
              </a-menu-item>
            </a-menu>
          </template>
          <a-button>批量操作
            <Icon icon="mdi:chevron-down" />
          </a-button>
        </a-dropdown>
      </template>
      <!--操作栏-->
      <template #action="{ record }">
        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
      </template>
      <!--字段回显插槽-->
      <template #htmlSlot="{ text }">
        <div v-html="text"></div>
      </template>
      <!--省市区字段回显插槽-->
      <template #pcaSlot="{ text }">
        {{ getAreaTextByCode(text) }}
      </template>
      <template #fileSlot="{ text }">
        <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
        <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small"
          @click="downloadFile(text)">下载</a-button>
      </template>
    </BasicTable>
    <!-- è¡¨å•区域 -->
    <DryEquipmentModal @register="registerModal" @success="handleSuccess" />
  </div>
    <div>
        <!--引用表格-->
        <BasicTable @register="registerTable" :rowSelection="rowSelection">
            <!--插槽:table标题-->
            <template #tableTitle>
                <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
                <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
                <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
                <a-dropdown v-if="selectedRowKeys.length > 0">
                    <template #overlay>
                        <a-menu>
                            <a-menu-item key="1" @click="batchHandleDelete">
                                <Icon icon="ant-design:delete-outlined" />
                                åˆ é™¤
                            </a-menu-item>
                        </a-menu>
                    </template>
                    <a-button
                        >批量操作
                        <Icon icon="mdi:chevron-down" />
                    </a-button>
                </a-dropdown>
            </template>
            <!--操作栏-->
            <template #action="{ record }">
                <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
            </template>
            <!--字段回显插槽-->
            <template #htmlSlot="{ text }">
                <div v-html="text"></div>
            </template>
            <!--省市区字段回显插槽-->
            <template #pcaSlot="{ text }">
                {{ getAreaTextByCode(text) }}
            </template>
            <template #fileSlot="{ text }">
                <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
                <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
            </template>
        </BasicTable>
        <!-- è¡¨å•区域 -->
        <DryEquipmentModal @register="registerModal" @success="handleSuccess" />
    </div>
</template>
<script lang="ts" name="dry-dryEquipment" setup>
import { ref } from 'vue'
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryEquipment.api'
import DryEquipmentModal from './components/DryEquipmentModal.vue'
import { columns, searchFormSchema } from './dataDefine/DryEquipment.data'
import { useModal } from '/@/components/Modal'
import { BasicTable, TableAction } from '/@/components/Table'
import { useListPage } from '/@/hooks/system/useListPage'
import { downloadFile } from '/@/utils/common/renderUtils'
const checkedKeys = ref<Array<string | number>>([])
//注册model
const [registerModal, { openModal }] = useModal()
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
  tableProps: {
    title: '干燥机',
    api: list,
    columns,
    canResize: false,
    formConfig: {
      //labelWidth: 120,
      schemas: searchFormSchema,
      autoSubmitOnEnter: true,
      showAdvancedButton: true,
      fieldMapToNumber: [],
      fieldMapToTime: [],
    },
    actionColumn: {
      width: 120,
      fixed: 'right',
    },
  },
  exportConfig: {
    name: '干燥机',
    url: getExportUrl,
  },
  importConfig: {
    url: getImportUrl,
    success: handleSuccess,
  },
})
    import { ref } from 'vue'
    import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryEquipment.api'
    import DryEquipmentModal from './components/DryEquipmentModal.vue'
    import { columns, searchFormSchema } from './dataDefine/DryEquipment.data'
    import { useModal } from '/@/components/Modal'
    import { BasicTable, TableAction } from '/@/components/Table'
    import { useListPage } from '/@/hooks/system/useListPage'
    import { downloadFile } from '/@/utils/common/renderUtils'
    const checkedKeys = ref<Array<string | number>>([])
    //注册model
    const [registerModal, { openModal }] = useModal()
    //注册table数据
    const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
        tableProps: {
            title: '干燥机',
            api: list,
            columns,
            canResize: false,
            formConfig: {
                //labelWidth: 120,
                schemas: searchFormSchema,
                autoSubmitOnEnter: true,
                showAdvancedButton: true,
                fieldMapToNumber: [],
                fieldMapToTime: [],
            },
            actionColumn: {
                width: 120,
                fixed: 'right',
            },
        },
        exportConfig: {
            name: '干燥机',
            url: getExportUrl,
        },
        importConfig: {
            url: getImportUrl,
            success: handleSuccess,
        },
    })
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
    const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
/**
 * æ–°å¢žäº‹ä»¶
 */
function handleAdd() {
  openModal(true, {
    isUpdate: false,
    showFooter: true,
  })
}
/**
 * ç¼–辑事件
 */
function handleEdit(record: Recordable) {
  openModal(true, {
    record,
    isUpdate: true,
    showFooter: true,
  })
}
/**
 * è¯¦æƒ…
 */
function handleDetail(record: Recordable) {
  openModal(true, {
    record,
    isUpdate: true,
    showFooter: false,
  })
}
/**
 * åˆ é™¤äº‹ä»¶
 */
async function handleDelete(record) {
  await deleteOne({ id: record.id }, handleSuccess)
}
/**
 * æ‰¹é‡åˆ é™¤äº‹ä»¶
 */
async function batchHandleDelete() {
  await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
}
/**
 * æˆåŠŸå›žè°ƒ
 */
function handleSuccess() {
  ; (selectedRowKeys.value = []) && reload()
}
/**
 * æ“ä½œæ 
 */
function getTableAction(record) {
  return [
    {
      label: '编辑',
      onClick: handleEdit.bind(null, record),
    },
  ]
}
/**
 * ä¸‹æ‹‰æ“ä½œæ 
 */
function getDropDownAction(record) {
  return [
    {
      label: '详情',
      onClick: handleDetail.bind(null, record),
    },
    {
      label: '删除',
      popConfirm: {
        title: '是否确认删除',
        confirm: handleDelete.bind(null, record),
      },
    },
  ]
}
    /**
     * æ–°å¢žäº‹ä»¶
     */
    function handleAdd() {
        openModal(true, {
            isUpdate: false,
            showFooter: true,
        })
    }
    /**
     * ç¼–辑事件
     */
    function handleEdit(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: true,
        })
    }
    /**
     * è¯¦æƒ…
     */
    function handleDetail(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: false,
        })
    }
    /**
     * åˆ é™¤äº‹ä»¶
     */
    async function handleDelete(record) {
        await deleteOne({ id: record.id }, handleSuccess)
    }
    /**
     * æ‰¹é‡åˆ é™¤äº‹ä»¶
     */
    async function batchHandleDelete() {
        await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
    }
    /**
     * æˆåŠŸå›žè°ƒ
     */
    function handleSuccess() {
        ;(selectedRowKeys.value = []) && reload()
    }
    /**
     * æ“ä½œæ 
     */
    function getTableAction(record) {
        return [
            {
                label: '编辑',
                onClick: handleEdit.bind(null, record),
            },
        ]
    }
    /**
     * ä¸‹æ‹‰æ“ä½œæ 
     */
    function getDropDownAction(record) {
        return [
            {
                label: '详情',
                onClick: handleDetail.bind(null, record),
            },
            {
                label: '删除',
                popConfirm: {
                    title: '是否确认删除',
                    confirm: handleDelete.bind(null, record),
                },
            },
        ]
    }
</script>
<style scoped></style>
src/views/dry/DryHerbFormulaList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,169 @@
<template>
    <div>
        <!--引用表格-->
        <BasicTable @register="registerTable" :rowSelection="rowSelection">
            <!--插槽:table标题-->
            <template #tableTitle>
                <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
                <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
                <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
                <a-dropdown v-if="selectedRowKeys.length > 0">
                    <template #overlay>
                        <a-menu>
                            <a-menu-item key="1" @click="batchHandleDelete">
                                <Icon icon="ant-design:delete-outlined" />
                                åˆ é™¤
                            </a-menu-item>
                        </a-menu>
                    </template>
                    <a-button
                        >批量操作
                        <Icon icon="mdi:chevron-down" />
                    </a-button>
                </a-dropdown>
            </template>
            <!--操作栏-->
            <template #action="{ record }">
                <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
            </template>
            <!--字段回显插槽-->
            <template #htmlSlot="{ text }">
                <div v-html="text"></div>
            </template>
            <!--省市区字段回显插槽-->
            <template #pcaSlot="{ text }">
                {{ getAreaTextByCode(text) }}
            </template>
            <template #fileSlot="{ text }">
                <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
                <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
            </template>
        </BasicTable>
        <!-- è¡¨å•区域 -->
        <DryHerbFormulaModal @register="registerModal" @success="handleSuccess" />
    </div>
</template>
<script lang="ts" name="dry-dryHerbFormula" setup>
    import { ref, computed, unref } from 'vue'
    import { BasicTable, useTable, TableAction } from '/@/components/Table'
    import { useModal } from '/@/components/Modal'
    import { useListPage } from '/@/hooks/system/useListPage'
    import DryHerbFormulaModal from './components/DryHerbFormulaModal.vue'
    import { columns, searchFormSchema } from './dataDefine/DryHerbFormula.data'
    import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './api/DryHerbFormula.api'
    import { downloadFile } from '/@/utils/common/renderUtils'
    const checkedKeys = ref<Array<string | number>>([])
    //注册model
    const [registerModal, { openModal }] = useModal()
    //注册table数据
    const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
        tableProps: {
            title: '干燥配方',
            api: list,
            columns,
            canResize: false,
            formConfig: {
                //labelWidth: 120,
                schemas: searchFormSchema,
                autoSubmitOnEnter: true,
                showAdvancedButton: true,
                fieldMapToNumber: [],
                fieldMapToTime: [],
            },
            actionColumn: {
                width: 120,
                fixed: 'right',
            },
        },
        exportConfig: {
            name: '干燥配方',
            url: getExportUrl,
        },
        importConfig: {
            url: getImportUrl,
            success: handleSuccess,
        },
    })
    const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
    /**
     * æ–°å¢žäº‹ä»¶
     */
    function handleAdd() {
        openModal(true, {
            isUpdate: false,
            showFooter: true,
        })
    }
    /**
     * ç¼–辑事件
     */
    function handleEdit(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: true,
        })
    }
    /**
     * è¯¦æƒ…
     */
    function handleDetail(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: false,
        })
    }
    /**
     * åˆ é™¤äº‹ä»¶
     */
    async function handleDelete(record) {
        await deleteOne({ id: record.id }, handleSuccess)
    }
    /**
     * æ‰¹é‡åˆ é™¤äº‹ä»¶
     */
    async function batchHandleDelete() {
        await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
    }
    /**
     * æˆåŠŸå›žè°ƒ
     */
    function handleSuccess() {
        ;(selectedRowKeys.value = []) && reload()
    }
    /**
     * æ“ä½œæ 
     */
    function getTableAction(record) {
        return [
            {
                label: '编辑',
                onClick: handleEdit.bind(null, record),
            },
        ]
    }
    /**
     * ä¸‹æ‹‰æ“ä½œæ 
     */
    function getDropDownAction(record) {
        return [
            {
                label: '详情',
                onClick: handleDetail.bind(null, record),
            },
            {
                label: '删除',
                popConfirm: {
                    title: '是否确认删除',
                    confirm: handleDelete.bind(null, record),
                },
            },
        ]
    }
</script>
<style scoped></style>
src/views/dry/DryHerbList.vue
@@ -1,169 +1,169 @@
<template>
  <div>
    <!--引用表格-->
    <BasicTable @register="registerTable" :rowSelection="rowSelection">
      <!--插槽:table标题-->
      <template #tableTitle>
        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
        <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
        <a-dropdown v-if="selectedRowKeys.length > 0">
          <template #overlay>
            <a-menu>
              <a-menu-item key="1" @click="batchHandleDelete">
                <Icon icon="ant-design:delete-outlined" />
                åˆ é™¤
              </a-menu-item>
            </a-menu>
          </template>
          <a-button
            >批量操作
            <Icon icon="mdi:chevron-down" />
          </a-button>
        </a-dropdown>
      </template>
      <!--操作栏-->
      <template #action="{ record }">
        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
      </template>
      <!--字段回显插槽-->
      <template #htmlSlot="{ text }">
        <div v-html="text"></div>
      </template>
      <!--省市区字段回显插槽-->
      <template #pcaSlot="{ text }">
        {{ getAreaTextByCode(text) }}
      </template>
      <template #fileSlot="{ text }">
        <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
        <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
      </template>
    </BasicTable>
    <!-- è¡¨å•区域 -->
    <DryHerbModal @register="registerModal" @success="handleSuccess" />
  </div>
    <div>
        <!--引用表格-->
        <BasicTable @register="registerTable" :rowSelection="rowSelection">
            <!--插槽:table标题-->
            <template #tableTitle>
                <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
                <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
                <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
                <a-dropdown v-if="selectedRowKeys.length > 0">
                    <template #overlay>
                        <a-menu>
                            <a-menu-item key="1" @click="batchHandleDelete">
                                <Icon icon="ant-design:delete-outlined" />
                                åˆ é™¤
                            </a-menu-item>
                        </a-menu>
                    </template>
                    <a-button
                        >批量操作
                        <Icon icon="mdi:chevron-down" />
                    </a-button>
                </a-dropdown>
            </template>
            <!--操作栏-->
            <template #action="{ record }">
                <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
            </template>
            <!--字段回显插槽-->
            <template #htmlSlot="{ text }">
                <div v-html="text"></div>
            </template>
            <!--省市区字段回显插槽-->
            <template #pcaSlot="{ text }">
                {{ getAreaTextByCode(text) }}
            </template>
            <template #fileSlot="{ text }">
                <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
                <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
            </template>
        </BasicTable>
        <!-- è¡¨å•区域 -->
        <DryHerbModal @register="registerModal" @success="handleSuccess" />
    </div>
</template>
<script lang="ts" name="dry-dryHerb" setup>
  import { ref } from 'vue';
  import DryHerbModal from './components/DryHerbModal.vue';
  import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryHerb.api';
  import { columns, searchFormSchema } from './dataDefine/DryHerb.data';
  import { useModal } from '/@/components/Modal';
  import { BasicTable, TableAction } from '/@/components/Table';
  import { useListPage } from '/@/hooks/system/useListPage';
  import { downloadFile } from '/@/utils/common/renderUtils';
  const checkedKeys = ref<Array<string | number>>([]);
  //注册model
  const [registerModal, { openModal }] = useModal();
  //注册table数据
  const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
    tableProps: {
      title: '药材',
      api: list,
      columns,
      canResize: false,
      formConfig: {
        //labelWidth: 120,
        schemas: searchFormSchema,
        autoSubmitOnEnter: true,
        showAdvancedButton: true,
        fieldMapToNumber: [],
        fieldMapToTime: [],
      },
      actionColumn: {
        width: 120,
        fixed: 'right',
      },
    },
    exportConfig: {
      name: '药材',
      url: getExportUrl,
    },
    importConfig: {
      url: getImportUrl,
      success: handleSuccess,
    },
  });
    import { ref } from 'vue'
    import DryHerbModal from './components/DryHerbModal.vue'
    import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryHerb.api'
    import { columns, searchFormSchema } from './dataDefine/DryHerb.data'
    import { useModal } from '/@/components/Modal'
    import { BasicTable, TableAction } from '/@/components/Table'
    import { useListPage } from '/@/hooks/system/useListPage'
    import { downloadFile } from '/@/utils/common/renderUtils'
    const checkedKeys = ref<Array<string | number>>([])
    //注册model
    const [registerModal, { openModal }] = useModal()
    //注册table数据
    const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
        tableProps: {
            title: '药材',
            api: list,
            columns,
            canResize: false,
            formConfig: {
                //labelWidth: 120,
                schemas: searchFormSchema,
                autoSubmitOnEnter: true,
                showAdvancedButton: true,
                fieldMapToNumber: [],
                fieldMapToTime: [],
            },
            actionColumn: {
                width: 120,
                fixed: 'right',
            },
        },
        exportConfig: {
            name: '药材',
            url: getExportUrl,
        },
        importConfig: {
            url: getImportUrl,
            success: handleSuccess,
        },
    })
  const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
    const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
  /**
   * æ–°å¢žäº‹ä»¶
   */
  function handleAdd() {
    openModal(true, {
      isUpdate: false,
      showFooter: true,
    });
  }
  /**
   * ç¼–辑事件
   */
  function handleEdit(record: Recordable) {
    openModal(true, {
      record,
      isUpdate: true,
      showFooter: true,
    });
  }
  /**
   * è¯¦æƒ…
   */
  function handleDetail(record: Recordable) {
    openModal(true, {
      record,
      isUpdate: true,
      showFooter: false,
    });
  }
  /**
   * åˆ é™¤äº‹ä»¶
   */
  async function handleDelete(record) {
    await deleteOne({ id: record.id }, handleSuccess);
  }
  /**
   * æ‰¹é‡åˆ é™¤äº‹ä»¶
   */
  async function batchHandleDelete() {
    await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
  }
  /**
   * æˆåŠŸå›žè°ƒ
   */
  function handleSuccess() {
    (selectedRowKeys.value = []) && reload();
  }
  /**
   * æ“ä½œæ 
   */
  function getTableAction(record) {
    return [
      {
        label: '编辑',
        onClick: handleEdit.bind(null, record),
      },
    ];
  }
  /**
   * ä¸‹æ‹‰æ“ä½œæ 
   */
  function getDropDownAction(record) {
    return [
      {
        label: '详情',
        onClick: handleDetail.bind(null, record),
      },
      {
        label: '删除',
        popConfirm: {
          title: '是否确认删除',
          confirm: handleDelete.bind(null, record),
        },
      },
    ];
  }
    /**
     * æ–°å¢žäº‹ä»¶
     */
    function handleAdd() {
        openModal(true, {
            isUpdate: false,
            showFooter: true,
        })
    }
    /**
     * ç¼–辑事件
     */
    function handleEdit(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: true,
        })
    }
    /**
     * è¯¦æƒ…
     */
    function handleDetail(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: false,
        })
    }
    /**
     * åˆ é™¤äº‹ä»¶
     */
    async function handleDelete(record) {
        await deleteOne({ id: record.id }, handleSuccess)
    }
    /**
     * æ‰¹é‡åˆ é™¤äº‹ä»¶
     */
    async function batchHandleDelete() {
        await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
    }
    /**
     * æˆåŠŸå›žè°ƒ
     */
    function handleSuccess() {
        ;(selectedRowKeys.value = []) && reload()
    }
    /**
     * æ“ä½œæ 
     */
    function getTableAction(record) {
        return [
            {
                label: '编辑',
                onClick: handleEdit.bind(null, record),
            },
        ]
    }
    /**
     * ä¸‹æ‹‰æ“ä½œæ 
     */
    function getDropDownAction(record) {
        return [
            {
                label: '详情',
                onClick: handleDetail.bind(null, record),
            },
            {
                label: '删除',
                popConfirm: {
                    title: '是否确认删除',
                    confirm: handleDelete.bind(null, record),
                },
            },
        ]
    }
</script>
<style scoped></style>
src/views/dry/DryHerbTypeList.vue
@@ -1,310 +1,315 @@
<template>
  <div>
    <!--引用表格-->
    <BasicTable @register="registerTable" :rowSelection="rowSelection" :expandedRowKeys="expandedRowKeys"
      @expand="handleExpand" @fetch-success="onFetchSuccess">
      <!--插槽:table标题-->
      <template #tableTitle>
        <a-button type="primary" @click="handleCreate" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
        <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
    <div>
        <!--引用表格-->
        <BasicTable
            @register="registerTable"
            :rowSelection="rowSelection"
            :expandedRowKeys="expandedRowKeys"
            @expand="handleExpand"
            @fetch-success="onFetchSuccess"
        >
            <!--插槽:table标题-->
            <template #tableTitle>
                <a-button type="primary" @click="handleCreate" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
                <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
                <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
        <a-dropdown v-if="selectedRowKeys.length > 0">
          <template #overlay>
            <a-menu>
              <a-menu-item key="1" @click="batchHandleDelete">
                <Icon icon="ant-design:delete-outlined" />
                åˆ é™¤
              </a-menu-item>
            </a-menu>
          </template>
          <a-button>批量操作
            <Icon icon="ant-design:down-outlined" />
          </a-button>
        </a-dropdown>
      </template>
      <!--操作栏-->
      <template #action="{ record }">
        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
      </template>
      <!--字段回显插槽-->
      <template #htmlSlot="{ text }">
        <div v-html="text"></div>
      </template>
      <!--省市区字段回显插槽-->
      <template #pcaSlot="{ text }">
        {{ getAreaTextByCode(text) }}
      </template>
      <template #fileSlot="{ text }">
        <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
        <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small"
          @click="downloadFile(text)">下载</a-button>
      </template>
    </BasicTable>
    <!--字典弹窗-->
    <DryHerbTypeModal @register="registerModal" @success="handleSuccess" />
  </div>
                <a-dropdown v-if="selectedRowKeys.length > 0">
                    <template #overlay>
                        <a-menu>
                            <a-menu-item key="1" @click="batchHandleDelete">
                                <Icon icon="ant-design:delete-outlined" />
                                åˆ é™¤
                            </a-menu-item>
                        </a-menu>
                    </template>
                    <a-button
                        >批量操作
                        <Icon icon="ant-design:down-outlined" />
                    </a-button>
                </a-dropdown>
            </template>
            <!--操作栏-->
            <template #action="{ record }">
                <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
            </template>
            <!--字段回显插槽-->
            <template #htmlSlot="{ text }">
                <div v-html="text"></div>
            </template>
            <!--省市区字段回显插槽-->
            <template #pcaSlot="{ text }">
                {{ getAreaTextByCode(text) }}
            </template>
            <template #fileSlot="{ text }">
                <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
                <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
            </template>
        </BasicTable>
        <!--字典弹窗-->
        <DryHerbTypeModal @register="registerModal" @success="handleSuccess" />
    </div>
</template>
<script lang="ts" name="dry-dryHerbType" setup>
//ts语法
import { ref, unref } from 'vue'
import { batchDeleteDryHerbType, deleteDryHerbType, getChildList, getChildListBatch, getExportUrl, getImportUrl, list } from './api/DryHerbType.api'
import DryHerbTypeModal from './components/DryHerbTypeModal.vue'
import { columns, searchFormSchema } from './dataDefine/DryHerbType.data'
import { useModal } from '/@/components/Modal'
import { BasicTable, TableAction } from '/@/components/Table'
import { useListPage } from '/@/hooks/system/useListPage'
import { downloadFile } from '/@/utils/common/renderUtils'
const expandedRowKeys = ref([])
//字典model
const [registerModal, { openModal }] = useModal()
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
  tableProps: {
    api: list,
    title: '药材分类',
    columns,
    canResize: false,
    formConfig: {
      //labelWidth: 120,
      schemas: searchFormSchema,
      autoSubmitOnEnter: true,
      showAdvancedButton: true,
      fieldMapToNumber: [],
      fieldMapToTime: [],
    },
    actionColumn: {
      width: 240,
      fixed: 'right',
    },
  },
  exportConfig: {
    name: '药材分类',
    url: getExportUrl,
  },
  importConfig: {
    url: getImportUrl,
    success: importSuccess,
  },
})
    //ts语法
    import { ref, unref } from 'vue'
    import { batchDeleteDryHerbType, deleteDryHerbType, getChildList, getChildListBatch, getExportUrl, getImportUrl, list } from './api/DryHerbType.api'
    import DryHerbTypeModal from './components/DryHerbTypeModal.vue'
    import { columns, searchFormSchema } from './dataDefine/DryHerbType.data'
    import { useModal } from '/@/components/Modal'
    import { BasicTable, TableAction } from '/@/components/Table'
    import { useListPage } from '/@/hooks/system/useListPage'
    import { downloadFile } from '/@/utils/common/renderUtils'
    const expandedRowKeys = ref([])
    //字典model
    const [registerModal, { openModal }] = useModal()
    //注册table数据
    const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
        tableProps: {
            api: list,
            title: '药材分类',
            columns,
            canResize: false,
            formConfig: {
                //labelWidth: 120,
                schemas: searchFormSchema,
                autoSubmitOnEnter: true,
                showAdvancedButton: true,
                fieldMapToNumber: [],
                fieldMapToTime: [],
            },
            actionColumn: {
                width: 240,
                fixed: 'right',
            },
        },
        exportConfig: {
            name: '药材分类',
            url: getExportUrl,
        },
        importConfig: {
            url: getImportUrl,
            success: importSuccess,
        },
    })
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
  tableContext
    const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
        tableContext
/**
 * æ–°å¢žäº‹ä»¶
 */
function handleCreate() {
  openModal(true, {
    isUpdate: false,
  })
}
    /**
     * æ–°å¢žäº‹ä»¶
     */
    function handleCreate() {
        openModal(true, {
            isUpdate: false,
        })
    }
/**
 * ç¼–辑事件
 */
async function handleEdit(record) {
  openModal(true, {
    record,
    isUpdate: true,
  })
}
    /**
     * ç¼–辑事件
     */
    async function handleEdit(record) {
        openModal(true, {
            record,
            isUpdate: true,
        })
    }
/**
 * è¯¦æƒ…
 */
async function handleDetail(record) {
  openModal(true, {
    record,
    isUpdate: true,
    hideFooter: true,
  })
}
    /**
     * è¯¦æƒ…
     */
    async function handleDetail(record) {
        openModal(true, {
            record,
            isUpdate: true,
            hideFooter: true,
        })
    }
/**
 * åˆ é™¤äº‹ä»¶
 */
async function handleDelete(record) {
  await deleteDryHerbType({ id: record.id }, importSuccess)
}
    /**
     * åˆ é™¤äº‹ä»¶
     */
    async function handleDelete(record) {
        await deleteDryHerbType({ id: record.id }, importSuccess)
    }
/**
 * æ‰¹é‡åˆ é™¤äº‹ä»¶
 */
async function batchHandleDelete() {
  const ids = selectedRowKeys.value.filter((item) => !item.includes('loading'))
  await batchDeleteDryHerbType({ id: ids }, importSuccess)
}
/**
 * å¯¼å…¥
 */
function importSuccess() {
  ; (selectedRowKeys.value = []) && reload()
}
/**
 * æ·»åŠ ä¸‹çº§
 */
function handleAddSub(record) {
  openModal(true, {
    record,
    isUpdate: false,
  })
}
/**
 * æˆåŠŸå›žè°ƒ
 */
async function handleSuccess({ isUpdate, values, expandedArr, changeParent }) {
  if (isUpdate) {
    if (changeParent) {
      reload()
    } else {
      // ç¼–辑回调
      updateTableDataRecord(values.id, values)
    }
  } else {
    if (!values['id'] || !values['pid']) {
      //新增根节点
      reload()
    } else {
      //新增子集
      expandedRowKeys.value = []
      for (let key of unref(expandedArr)) {
        await expandTreeNode(key)
      }
    }
  }
}
    /**
     * æ‰¹é‡åˆ é™¤äº‹ä»¶
     */
    async function batchHandleDelete() {
        const ids = selectedRowKeys.value.filter((item) => !item.includes('loading'))
        await batchDeleteDryHerbType({ id: ids }, importSuccess)
    }
    /**
     * å¯¼å…¥
     */
    function importSuccess() {
        ;(selectedRowKeys.value = []) && reload()
    }
    /**
     * æ·»åŠ ä¸‹çº§
     */
    function handleAddSub(record) {
        openModal(true, {
            record,
            isUpdate: false,
        })
    }
    /**
     * æˆåŠŸå›žè°ƒ
     */
    async function handleSuccess({ isUpdate, values, expandedArr, changeParent }) {
        if (isUpdate) {
            if (changeParent) {
                reload()
            } else {
                // ç¼–辑回调
                updateTableDataRecord(values.id, values)
            }
        } else {
            if (!values['id'] || !values['pid']) {
                //新增根节点
                reload()
            } else {
                //新增子集
                expandedRowKeys.value = []
                for (let key of unref(expandedArr)) {
                    await expandTreeNode(key)
                }
            }
        }
    }
/**
 * æŽ¥å£è¯·æ±‚成功后回调
 */
function onFetchSuccess(result) {
  getDataByResult(result.items) && loadDataByExpandedRows()
}
/**
 * æ ¹æ®å·²å±•开的行查询数据(用于保存后刷新时异步加载子级的数据)
 */
async function loadDataByExpandedRows() {
  if (unref(expandedRowKeys).length > 0) {
    const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',') })
    if (res.success && res.result.records.length > 0) {
      //已展开的数据批量子节点
      let records = res.result.records
      const listMap = new Map()
      for (let item of records) {
        let pid = item['pid']
        if (unref(expandedRowKeys).includes(pid)) {
          let mapList = listMap.get(pid)
          if (mapList == null) {
            mapList = []
          }
          mapList.push(item)
          listMap.set(pid, mapList)
        }
      }
      let childrenMap = listMap
      let fn = (list) => {
        if (list) {
          list.forEach((data) => {
            if (unref(expandedRowKeys).includes(data.id)) {
              data.children = getDataByResult(childrenMap.get(data.id))
              fn(data.children)
            }
          })
        }
      }
      fn(getDataSource())
    }
  }
}
/**
 * å¤„理数据集
 */
function getDataByResult(result) {
  if (result && result.length > 0) {
    return result.map((item) => {
      //判断是否标记了带有子节点
      if (item['hasChild'] == '1') {
        let loadChild = { id: item.id + '_loadChild', name: 'loading...', isLoading: true }
        item.children = [loadChild]
      }
      return item
    })
  }
}
/**
 *树节点展开合并
 * */
async function handleExpand(expanded, record) {
  // åˆ¤æ–­æ˜¯å¦æ˜¯å±•开状态,展开状态(expanded)并且存在子集(children)并且未加载过(isLoading)的就去查询子节点数据
  if (expanded) {
    expandedRowKeys.value.push(record.id)
    if (record.children.length > 0 && !!record.children[0].isLoading) {
      let result = await getChildList({ pid: record.id })
      result = result.records ? result.records : result
      if (result && result.length > 0) {
        record.children = getDataByResult(result)
      } else {
        record.children = null
        record.hasChild = '0'
      }
    }
  } else {
    let keyIndex = expandedRowKeys.value.indexOf(record.id)
    if (keyIndex >= 0) {
      expandedRowKeys.value.splice(keyIndex, 1)
    }
  }
}
/**
 *操作表格后处理树节点展开合并
 * */
async function expandTreeNode(key) {
  let record = findTableDataRecord(key)
  expandedRowKeys.value.push(key)
  let result = await getChildList({ pid: key })
  if (result && result.length > 0) {
    record.children = getDataByResult(result)
  } else {
    record.children = null
    record.hasChild = '0'
  }
  updateTableDataRecord(key, record)
}
/**
 * æ“ä½œæ 
 */
function getTableAction(record) {
  return [
    {
      label: '编辑',
      onClick: handleEdit.bind(null, record),
    },
    {
      label: '添加下级',
      onClick: handleAddSub.bind(null, { pid: record.id }),
    },
  ]
}
/**
 * ä¸‹æ‹‰æ“ä½œæ 
 */
function getDropDownAction(record) {
  return [
    {
      label: '详情',
      onClick: handleDetail.bind(null, record),
    },
    {
      label: '删除',
      popConfirm: {
        title: '确定删除吗?',
        confirm: handleDelete.bind(null, record),
      },
    },
  ]
}
    /**
     * æŽ¥å£è¯·æ±‚成功后回调
     */
    function onFetchSuccess(result) {
        getDataByResult(result.items) && loadDataByExpandedRows()
    }
    /**
     * æ ¹æ®å·²å±•开的行查询数据(用于保存后刷新时异步加载子级的数据)
     */
    async function loadDataByExpandedRows() {
        if (unref(expandedRowKeys).length > 0) {
            const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',') })
            if (res.success && res.result.records.length > 0) {
                //已展开的数据批量子节点
                let records = res.result.records
                const listMap = new Map()
                for (let item of records) {
                    let pid = item['pid']
                    if (unref(expandedRowKeys).includes(pid)) {
                        let mapList = listMap.get(pid)
                        if (mapList == null) {
                            mapList = []
                        }
                        mapList.push(item)
                        listMap.set(pid, mapList)
                    }
                }
                let childrenMap = listMap
                let fn = (list) => {
                    if (list) {
                        list.forEach((data) => {
                            if (unref(expandedRowKeys).includes(data.id)) {
                                data.children = getDataByResult(childrenMap.get(data.id))
                                fn(data.children)
                            }
                        })
                    }
                }
                fn(getDataSource())
            }
        }
    }
    /**
     * å¤„理数据集
     */
    function getDataByResult(result) {
        if (result && result.length > 0) {
            return result.map((item) => {
                //判断是否标记了带有子节点
                if (item['hasChild'] == '1') {
                    let loadChild = { id: item.id + '_loadChild', name: 'loading...', isLoading: true }
                    item.children = [loadChild]
                }
                return item
            })
        }
    }
    /**
     *树节点展开合并
     * */
    async function handleExpand(expanded, record) {
        // åˆ¤æ–­æ˜¯å¦æ˜¯å±•开状态,展开状态(expanded)并且存在子集(children)并且未加载过(isLoading)的就去查询子节点数据
        if (expanded) {
            expandedRowKeys.value.push(record.id)
            if (record.children.length > 0 && !!record.children[0].isLoading) {
                let result = await getChildList({ pid: record.id })
                result = result.records ? result.records : result
                if (result && result.length > 0) {
                    record.children = getDataByResult(result)
                } else {
                    record.children = null
                    record.hasChild = '0'
                }
            }
        } else {
            let keyIndex = expandedRowKeys.value.indexOf(record.id)
            if (keyIndex >= 0) {
                expandedRowKeys.value.splice(keyIndex, 1)
            }
        }
    }
    /**
     *操作表格后处理树节点展开合并
     * */
    async function expandTreeNode(key) {
        let record = findTableDataRecord(key)
        expandedRowKeys.value.push(key)
        let result = await getChildList({ pid: key })
        if (result && result.length > 0) {
            record.children = getDataByResult(result)
        } else {
            record.children = null
            record.hasChild = '0'
        }
        updateTableDataRecord(key, record)
    }
    /**
     * æ“ä½œæ 
     */
    function getTableAction(record) {
        return [
            {
                label: '编辑',
                onClick: handleEdit.bind(null, record),
            },
            {
                label: '添加下级',
                onClick: handleAddSub.bind(null, { pid: record.id }),
            },
        ]
    }
    /**
     * ä¸‹æ‹‰æ“ä½œæ 
     */
    function getDropDownAction(record) {
        return [
            {
                label: '详情',
                onClick: handleDetail.bind(null, record),
            },
            {
                label: '删除',
                popConfirm: {
                    title: '确定删除吗?',
                    confirm: handleDelete.bind(null, record),
                },
            },
        ]
    }
</script>
<style scoped></style>
src/views/dry/DryOrderList.vue
@@ -1,169 +1,169 @@
<template>
  <div>
    <!--引用表格-->
    <BasicTable @register="registerTable" :rowSelection="rowSelection">
      <!--插槽:table标题-->
      <template #tableTitle>
        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
        <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
        <a-dropdown v-if="selectedRowKeys.length > 0">
          <template #overlay>
            <a-menu>
              <a-menu-item key="1" @click="batchHandleDelete">
                <Icon icon="ant-design:delete-outlined" />
                åˆ é™¤
              </a-menu-item>
            </a-menu>
          </template>
          <a-button>批量操作
            <Icon icon="mdi:chevron-down" />
          </a-button>
        </a-dropdown>
      </template>
      <!--操作栏-->
      <template #action="{ record }">
        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
      </template>
      <!--字段回显插槽-->
      <template #htmlSlot="{ text }">
        <div v-html="text"></div>
      </template>
      <!--省市区字段回显插槽-->
      <template #pcaSlot="{ text }">
        {{ getAreaTextByCode(text) }}
      </template>
      <template #fileSlot="{ text }">
        <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
        <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small"
          @click="downloadFile(text)">下载</a-button>
      </template>
    </BasicTable>
    <!-- è¡¨å•区域 -->
    <DryOrderModal @register="registerModal" @success="handleSuccess" />
  </div>
    <div>
        <!--引用表格-->
        <BasicTable @register="registerTable" :rowSelection="rowSelection">
            <!--插槽:table标题-->
            <template #tableTitle>
                <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
                <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
                <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
                <a-dropdown v-if="selectedRowKeys.length > 0">
                    <template #overlay>
                        <a-menu>
                            <a-menu-item key="1" @click="batchHandleDelete">
                                <Icon icon="ant-design:delete-outlined" />
                                åˆ é™¤
                            </a-menu-item>
                        </a-menu>
                    </template>
                    <a-button
                        >批量操作
                        <Icon icon="mdi:chevron-down" />
                    </a-button>
                </a-dropdown>
            </template>
            <!--操作栏-->
            <template #action="{ record }">
                <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
            </template>
            <!--字段回显插槽-->
            <template #htmlSlot="{ text }">
                <div v-html="text"></div>
            </template>
            <!--省市区字段回显插槽-->
            <template #pcaSlot="{ text }">
                {{ getAreaTextByCode(text) }}
            </template>
            <template #fileSlot="{ text }">
                <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
                <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
            </template>
        </BasicTable>
        <!-- è¡¨å•区域 -->
        <DryOrderModal @register="registerModal" @success="handleSuccess" />
    </div>
</template>
<script lang="ts" name="dry-dryOrder" setup>
import { ref } from 'vue'
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryOrder.api'
import DryOrderModal from './components/DryOrderModal.vue'
import { columns, searchFormSchema } from './dataDefine/DryOrder.data'
import { useModal } from '/@/components/Modal'
import { BasicTable, TableAction } from '/@/components/Table'
import { useListPage } from '/@/hooks/system/useListPage'
import { downloadFile } from '/@/utils/common/renderUtils'
const checkedKeys = ref<Array<string | number>>([])
//注册model
const [registerModal, { openModal }] = useModal()
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
  tableProps: {
    title: '干燥工单',
    api: list,
    columns,
    canResize: false,
    formConfig: {
      //labelWidth: 120,
      schemas: searchFormSchema,
      autoSubmitOnEnter: true,
      showAdvancedButton: true,
      fieldMapToNumber: [],
      fieldMapToTime: [],
    },
    actionColumn: {
      width: 120,
      fixed: 'right',
    },
  },
  exportConfig: {
    name: '干燥工单',
    url: getExportUrl,
  },
  importConfig: {
    url: getImportUrl,
    success: handleSuccess,
  },
})
    import { ref } from 'vue'
    import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryOrder.api'
    import DryOrderModal from './components/DryOrderModal.vue'
    import { columns, searchFormSchema } from './dataDefine/DryOrder.data'
    import { useModal } from '/@/components/Modal'
    import { BasicTable, TableAction } from '/@/components/Table'
    import { useListPage } from '/@/hooks/system/useListPage'
    import { downloadFile } from '/@/utils/common/renderUtils'
    const checkedKeys = ref<Array<string | number>>([])
    //注册model
    const [registerModal, { openModal }] = useModal()
    //注册table数据
    const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
        tableProps: {
            title: '干燥工单',
            api: list,
            columns,
            canResize: false,
            formConfig: {
                //labelWidth: 120,
                schemas: searchFormSchema,
                autoSubmitOnEnter: true,
                showAdvancedButton: true,
                fieldMapToNumber: [],
                fieldMapToTime: [],
            },
            actionColumn: {
                width: 120,
                fixed: 'right',
            },
        },
        exportConfig: {
            name: '干燥工单',
            url: getExportUrl,
        },
        importConfig: {
            url: getImportUrl,
            success: handleSuccess,
        },
    })
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
    const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
/**
 * æ–°å¢žäº‹ä»¶
 */
function handleAdd() {
  openModal(true, {
    isUpdate: false,
    showFooter: true,
  })
}
/**
 * ç¼–辑事件
 */
function handleEdit(record: Recordable) {
  openModal(true, {
    record,
    isUpdate: true,
    showFooter: true,
  })
}
/**
 * è¯¦æƒ…
 */
function handleDetail(record: Recordable) {
  openModal(true, {
    record,
    isUpdate: true,
    showFooter: false,
  })
}
/**
 * åˆ é™¤äº‹ä»¶
 */
async function handleDelete(record) {
  await deleteOne({ id: record.id }, handleSuccess)
}
/**
 * æ‰¹é‡åˆ é™¤äº‹ä»¶
 */
async function batchHandleDelete() {
  await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
}
/**
 * æˆåŠŸå›žè°ƒ
 */
function handleSuccess() {
  ; (selectedRowKeys.value = []) && reload()
}
/**
 * æ“ä½œæ 
 */
function getTableAction(record) {
  return [
    {
      label: '编辑',
      onClick: handleEdit.bind(null, record),
    },
  ]
}
/**
 * ä¸‹æ‹‰æ“ä½œæ 
 */
function getDropDownAction(record) {
  return [
    {
      label: '详情',
      onClick: handleDetail.bind(null, record),
    },
    {
      label: '删除',
      popConfirm: {
        title: '是否确认删除',
        confirm: handleDelete.bind(null, record),
      },
    },
  ]
}
    /**
     * æ–°å¢žäº‹ä»¶
     */
    function handleAdd() {
        openModal(true, {
            isUpdate: false,
            showFooter: true,
        })
    }
    /**
     * ç¼–辑事件
     */
    function handleEdit(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: true,
        })
    }
    /**
     * è¯¦æƒ…
     */
    function handleDetail(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: false,
        })
    }
    /**
     * åˆ é™¤äº‹ä»¶
     */
    async function handleDelete(record) {
        await deleteOne({ id: record.id }, handleSuccess)
    }
    /**
     * æ‰¹é‡åˆ é™¤äº‹ä»¶
     */
    async function batchHandleDelete() {
        await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
    }
    /**
     * æˆåŠŸå›žè°ƒ
     */
    function handleSuccess() {
        ;(selectedRowKeys.value = []) && reload()
    }
    /**
     * æ“ä½œæ 
     */
    function getTableAction(record) {
        return [
            {
                label: '编辑',
                onClick: handleEdit.bind(null, record),
            },
        ]
    }
    /**
     * ä¸‹æ‹‰æ“ä½œæ 
     */
    function getDropDownAction(record) {
        return [
            {
                label: '详情',
                onClick: handleDetail.bind(null, record),
            },
            {
                label: '删除',
                popConfirm: {
                    title: '是否确认删除',
                    confirm: handleDelete.bind(null, record),
                },
            },
        ]
    }
</script>
<style scoped></style>
src/views/dry/DryOrderTrendList.vue
@@ -1,169 +1,169 @@
<template>
  <div>
    <!--引用表格-->
    <BasicTable @register="registerTable" :rowSelection="rowSelection">
      <!--插槽:table标题-->
      <template #tableTitle>
        <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
        <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
        <a-dropdown v-if="selectedRowKeys.length > 0">
          <template #overlay>
            <a-menu>
              <a-menu-item key="1" @click="batchHandleDelete">
                <Icon icon="ant-design:delete-outlined" />
                åˆ é™¤
              </a-menu-item>
            </a-menu>
          </template>
          <a-button>批量操作
            <Icon icon="mdi:chevron-down" />
          </a-button>
        </a-dropdown>
      </template>
      <!--操作栏-->
      <template #action="{ record }">
        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
      </template>
      <!--字段回显插槽-->
      <template #htmlSlot="{ text }">
        <div v-html="text"></div>
      </template>
      <!--省市区字段回显插槽-->
      <template #pcaSlot="{ text }">
        {{ getAreaTextByCode(text) }}
      </template>
      <template #fileSlot="{ text }">
        <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
        <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small"
          @click="downloadFile(text)">下载</a-button>
      </template>
    </BasicTable>
    <!-- è¡¨å•区域 -->
    <DryOrderTrendModal @register="registerModal" @success="handleSuccess" />
  </div>
    <div>
        <!--引用表格-->
        <BasicTable @register="registerTable" :rowSelection="rowSelection">
            <!--插槽:table标题-->
            <template #tableTitle>
                <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
                <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
                <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
                <a-dropdown v-if="selectedRowKeys.length > 0">
                    <template #overlay>
                        <a-menu>
                            <a-menu-item key="1" @click="batchHandleDelete">
                                <Icon icon="ant-design:delete-outlined" />
                                åˆ é™¤
                            </a-menu-item>
                        </a-menu>
                    </template>
                    <a-button
                        >批量操作
                        <Icon icon="mdi:chevron-down" />
                    </a-button>
                </a-dropdown>
            </template>
            <!--操作栏-->
            <template #action="{ record }">
                <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
            </template>
            <!--字段回显插槽-->
            <template #htmlSlot="{ text }">
                <div v-html="text"></div>
            </template>
            <!--省市区字段回显插槽-->
            <template #pcaSlot="{ text }">
                {{ getAreaTextByCode(text) }}
            </template>
            <template #fileSlot="{ text }">
                <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
                <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
            </template>
        </BasicTable>
        <!-- è¡¨å•区域 -->
        <DryOrderTrendModal @register="registerModal" @success="handleSuccess" />
    </div>
</template>
<script lang="ts" name="dry-dryOrderTrend" setup>
import { ref } from 'vue'
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryOrderTrend.api'
import DryOrderTrendModal from './components/DryOrderTrendModal.vue'
import { columns, searchFormSchema } from './dataDefine/DryOrderTrend.data'
import { useModal } from '/@/components/Modal'
import { BasicTable, TableAction } from '/@/components/Table'
import { useListPage } from '/@/hooks/system/useListPage'
import { downloadFile } from '/@/utils/common/renderUtils'
const checkedKeys = ref<Array<string | number>>([])
//注册model
const [registerModal, { openModal }] = useModal()
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
  tableProps: {
    title: '工单过程趋势',
    api: list,
    columns,
    canResize: false,
    formConfig: {
      //labelWidth: 120,
      schemas: searchFormSchema,
      autoSubmitOnEnter: true,
      showAdvancedButton: true,
      fieldMapToNumber: [],
      fieldMapToTime: [],
    },
    actionColumn: {
      width: 120,
      fixed: 'right',
    },
  },
  exportConfig: {
    name: '工单过程趋势',
    url: getExportUrl,
  },
  importConfig: {
    url: getImportUrl,
    success: handleSuccess,
  },
})
    import { ref } from 'vue'
    import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './api/DryOrderTrend.api'
    import DryOrderTrendModal from './components/DryOrderTrendModal.vue'
    import { columns, searchFormSchema } from './dataDefine/DryOrderTrend.data'
    import { useModal } from '/@/components/Modal'
    import { BasicTable, TableAction } from '/@/components/Table'
    import { useListPage } from '/@/hooks/system/useListPage'
    import { downloadFile } from '/@/utils/common/renderUtils'
    const checkedKeys = ref<Array<string | number>>([])
    //注册model
    const [registerModal, { openModal }] = useModal()
    //注册table数据
    const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
        tableProps: {
            title: '工单过程趋势',
            api: list,
            columns,
            canResize: false,
            formConfig: {
                //labelWidth: 120,
                schemas: searchFormSchema,
                autoSubmitOnEnter: true,
                showAdvancedButton: true,
                fieldMapToNumber: [],
                fieldMapToTime: [],
            },
            actionColumn: {
                width: 120,
                fixed: 'right',
            },
        },
        exportConfig: {
            name: '工单过程趋势',
            url: getExportUrl,
        },
        importConfig: {
            url: getImportUrl,
            success: handleSuccess,
        },
    })
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
    const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext
/**
 * æ–°å¢žäº‹ä»¶
 */
function handleAdd() {
  openModal(true, {
    isUpdate: false,
    showFooter: true,
  })
}
/**
 * ç¼–辑事件
 */
function handleEdit(record: Recordable) {
  openModal(true, {
    record,
    isUpdate: true,
    showFooter: true,
  })
}
/**
 * è¯¦æƒ…
 */
function handleDetail(record: Recordable) {
  openModal(true, {
    record,
    isUpdate: true,
    showFooter: false,
  })
}
/**
 * åˆ é™¤äº‹ä»¶
 */
async function handleDelete(record) {
  await deleteOne({ id: record.id }, handleSuccess)
}
/**
 * æ‰¹é‡åˆ é™¤äº‹ä»¶
 */
async function batchHandleDelete() {
  await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
}
/**
 * æˆåŠŸå›žè°ƒ
 */
function handleSuccess() {
  ; (selectedRowKeys.value = []) && reload()
}
/**
 * æ“ä½œæ 
 */
function getTableAction(record) {
  return [
    {
      label: '编辑',
      onClick: handleEdit.bind(null, record),
    },
  ]
}
/**
 * ä¸‹æ‹‰æ“ä½œæ 
 */
function getDropDownAction(record) {
  return [
    {
      label: '详情',
      onClick: handleDetail.bind(null, record),
    },
    {
      label: '删除',
      popConfirm: {
        title: '是否确认删除',
        confirm: handleDelete.bind(null, record),
      },
    },
  ]
}
    /**
     * æ–°å¢žäº‹ä»¶
     */
    function handleAdd() {
        openModal(true, {
            isUpdate: false,
            showFooter: true,
        })
    }
    /**
     * ç¼–辑事件
     */
    function handleEdit(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: true,
        })
    }
    /**
     * è¯¦æƒ…
     */
    function handleDetail(record: Recordable) {
        openModal(true, {
            record,
            isUpdate: true,
            showFooter: false,
        })
    }
    /**
     * åˆ é™¤äº‹ä»¶
     */
    async function handleDelete(record) {
        await deleteOne({ id: record.id }, handleSuccess)
    }
    /**
     * æ‰¹é‡åˆ é™¤äº‹ä»¶
     */
    async function batchHandleDelete() {
        await batchDelete({ ids: selectedRowKeys.value }, handleSuccess)
    }
    /**
     * æˆåŠŸå›žè°ƒ
     */
    function handleSuccess() {
        ;(selectedRowKeys.value = []) && reload()
    }
    /**
     * æ“ä½œæ 
     */
    function getTableAction(record) {
        return [
            {
                label: '编辑',
                onClick: handleEdit.bind(null, record),
            },
        ]
    }
    /**
     * ä¸‹æ‹‰æ“ä½œæ 
     */
    function getDropDownAction(record) {
        return [
            {
                label: '详情',
                onClick: handleDetail.bind(null, record),
            },
            {
                label: '删除',
                popConfirm: {
                    title: '是否确认删除',
                    confirm: handleDelete.bind(null, record),
                },
            },
        ]
    }
</script>
<style scoped></style>
src/views/dry/api/DryEquipment.api.ts
@@ -4,13 +4,14 @@
const { createConfirm } = useMessage()
enum Api {
  list = '/dry/dryEquipment/list',
  save = '/dry/dryEquipment/add',
  edit = '/dry/dryEquipment/edit',
  deleteOne = '/dry/dryEquipment/delete',
  deleteBatch = '/dry/dryEquipment/deleteBatch',
  importExcel = '/dry/dryEquipment/importExcel',
  exportXls = '/dry/dryEquipment/exportXls',
    list = '/dry/dryEquipment/list',
    listAll = '/dry/dryEquipment/listAll',
    save = '/dry/dryEquipment/add',
    edit = '/dry/dryEquipment/edit',
    deleteOne = '/dry/dryEquipment/delete',
    deleteBatch = '/dry/dryEquipment/deleteBatch',
    importExcel = '/dry/dryEquipment/importExcel',
    exportXls = '/dry/dryEquipment/exportXls',
}
/**
 * å¯¼å‡ºapi
@@ -28,36 +29,42 @@
export const list = (params) => defHttp.get({ url: Api.list, params })
/**
 * æ‰€æœ‰æœºå°
 * @param params
 */
export const listAll = (params) => defHttp.get({ url: Api.listAll, params })
/**
 * åˆ é™¤å•个
 */
export const deleteOne = (params, handleSuccess) => {
  return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
    handleSuccess()
  })
    return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
        handleSuccess()
    })
}
/**
 * æ‰¹é‡åˆ é™¤
 * @param params
 */
export const batchDelete = (params, handleSuccess) => {
  createConfirm({
    iconType: 'warning',
    title: '确认删除',
    content: '是否删除选中数据',
    okText: '确认',
    cancelText: '取消',
    onOk: () => {
      return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
        handleSuccess()
      })
    },
  })
    createConfirm({
        iconType: 'warning',
        title: '确认删除',
        content: '是否删除选中数据',
        okText: '确认',
        cancelText: '取消',
        onOk: () => {
            return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
                handleSuccess()
            })
        },
    })
}
/**
 * ä¿å­˜æˆ–者更新
 * @param params
 */
export const saveOrUpdate = (params, isUpdate) => {
  const url = isUpdate ? Api.edit : Api.save
  return defHttp.post({ url: url, params })
    const url = isUpdate ? Api.edit : Api.save
    return defHttp.post({ url: url, params })
}
src/views/dry/api/DryHerbFormula.api.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
import {defHttp} from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
  list = '/dry/dryHerbFormula/list',
  save='/dry/dryHerbFormula/add',
  edit='/dry/dryHerbFormula/edit',
  deleteOne = '/dry/dryHerbFormula/delete',
  deleteBatch = '/dry/dryHerbFormula/deleteBatch',
  importExcel = '/dry/dryHerbFormula/importExcel',
  exportXls = '/dry/dryHerbFormula/exportXls',
}
/**
 * å¯¼å‡ºapi
 * @param params
 */
export const getExportUrl = Api.exportXls;
/**
 * å¯¼å…¥api
 */
export const getImportUrl = Api.importExcel;
/**
 * åˆ—表接口
 * @param params
 */
export const list = (params) =>
  defHttp.get({url: Api.list, params});
/**
 * åˆ é™¤å•个
 */
export const deleteOne = (params,handleSuccess) => {
  return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
    handleSuccess();
  });
}
/**
 * æ‰¹é‡åˆ é™¤
 * @param params
 */
export const batchDelete = (params, handleSuccess) => {
  createConfirm({
    iconType: 'warning',
    title: '确认删除',
    content: '是否删除选中数据',
    okText: '确认',
    cancelText: '取消',
    onOk: () => {
      return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
        handleSuccess();
      });
    }
  });
}
/**
 * ä¿å­˜æˆ–者更新
 * @param params
 */
export const saveOrUpdate = (params, isUpdate) => {
  let url = isUpdate ? Api.edit : Api.save;
  return defHttp.post({url: url, params});
}
src/views/dry/components/DryEquipmentForm.vue
@@ -1,70 +1,70 @@
<template>
  <div style="min-height: 400px">
    <BasicForm @register="registerForm" />
    <div style="width: 100%; text-align: center" v-if="!formDisabled">
      <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
    </div>
  </div>
    <div style="min-height: 400px">
        <BasicForm @register="registerForm" />
        <div style="width: 100%; text-align: center" v-if="!formDisabled">
            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
        </div>
    </div>
</template>
<script lang="ts">
  import { computed, defineComponent } from 'vue';
import { saveOrUpdate } from '../api/DryEquipment.api';
import { getBpmFormSchema } from '../dataDefine/DryEquipment.data';
import { BasicForm, useForm } from '/@/components/Form/index';
import { defHttp } from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
    import { computed, defineComponent } from 'vue'
    import { saveOrUpdate } from '../api/DryEquipment.api'
    import { getBpmFormSchema } from '../dataDefine/DryEquipment.data'
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { defHttp } from '/@/utils/http/axios'
    import { propTypes } from '/@/utils/propTypes'
  export default defineComponent({
    name: 'DryEquipmentForm',
    components: {
      BasicForm,
    },
    props: {
      formData: propTypes.object.def({}),
      formBpm: propTypes.bool.def(true),
    },
    setup(props) {
      const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
        labelWidth: 150,
        schemas: getBpmFormSchema(props.formData),
        showActionButtonGroup: false,
        baseColProps: { span: 24 },
      });
    export default defineComponent({
        name: 'DryEquipmentForm',
        components: {
            BasicForm,
        },
        props: {
            formData: propTypes.object.def({}),
            formBpm: propTypes.bool.def(true),
        },
        setup(props) {
            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
                labelWidth: 150,
                schemas: getBpmFormSchema(props.formData),
                showActionButtonGroup: false,
                baseColProps: { span: 24 },
            })
      const formDisabled = computed(() => {
        if (props.formData.disabled === false) {
          return false;
        }
        return true;
      });
            const formDisabled = computed(() => {
                if (props.formData.disabled === false) {
                    return false
                }
                return true
            })
      let formData = {};
      const queryByIdUrl = '/dry/dryEquipment/queryById';
      async function initFormData() {
        let params = { id: props.formData.dataId };
        const data = await defHttp.get({ url: queryByIdUrl, params });
        formData = { ...data };
        //设置表单的值
        await setFieldsValue(formData);
        //默认是禁用
        await setProps({ disabled: formDisabled.value });
      }
            let formData = {}
            const queryByIdUrl = '/dry/dryEquipment/queryById'
            async function initFormData() {
                let params = { id: props.formData.dataId }
                const data = await defHttp.get({ url: queryByIdUrl, params })
                formData = { ...data }
                //设置表单的值
                await setFieldsValue(formData)
                //默认是禁用
                await setProps({ disabled: formDisabled.value })
            }
      async function submitForm() {
        let data = getFieldsValue();
        let params = Object.assign({}, formData, data);
        console.log('表单数据', params);
        await saveOrUpdate(params, true);
      }
            async function submitForm() {
                let data = getFieldsValue()
                let params = Object.assign({}, formData, data)
                console.log('表单数据', params)
                await saveOrUpdate(params, true)
            }
      initFormData();
            initFormData()
      return {
        registerForm,
        formDisabled,
        submitForm,
      };
    },
  });
            return {
                registerForm,
                formDisabled,
                submitForm,
            }
        },
    })
</script>
src/views/dry/components/DryEquipmentModal.vue
@@ -1,66 +1,66 @@
<template>
  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
      <BasicForm @register="registerForm"/>
  </BasicModal>
    <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
        <BasicForm @register="registerForm" />
    </BasicModal>
</template>
<script lang="ts" setup>
    import {ref, computed, unref} from 'vue';
    import {BasicModal, useModalInner} from '/@/components/Modal';
    import {BasicForm, useForm} from '/@/components/Form/index';
    import {formSchema} from '../dataDefine/DryEquipment.data';
    import {saveOrUpdate} from '../api/DryEquipment.api';
    // Emits声明
    const emit = defineEmits(['register','success']);
    const isUpdate = ref(true);
    //表单配置
    const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
        //labelWidth: 150,
        schemas: formSchema,
        showActionButtonGroup: false,
        baseColProps: {span: 24}
    });
    //表单赋值
    const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
        //重置表单
        await resetFields();
        setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
        isUpdate.value = !!data?.isUpdate;
        if (unref(isUpdate)) {
            //表单赋值
            await setFieldsValue({
                ...data.record,
            });
        }
        // éšè—åº•部时禁用整个表单
       setProps({ disabled: !data?.showFooter })
    });
    //设置标题
    const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
    //表单提交事件
    async function handleSubmit(v) {
        try {
            let values = await validate();
            setModalProps({confirmLoading: true});
            //提交表单
            await saveOrUpdate(values, isUpdate.value);
            //关闭弹窗
            closeModal();
            //刷新列表
            emit('success');
        } finally {
            setModalProps({confirmLoading: false});
        }
    }
    import { ref, computed, unref } from 'vue'
    import { BasicModal, useModalInner } from '/@/components/Modal'
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { formSchema } from '../dataDefine/DryEquipment.data'
    import { saveOrUpdate } from '../api/DryEquipment.api'
    // Emits声明
    const emit = defineEmits(['register', 'success'])
    const isUpdate = ref(true)
    //表单配置
    const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
        //labelWidth: 150,
        schemas: formSchema,
        showActionButtonGroup: false,
        baseColProps: { span: 24 },
    })
    //表单赋值
    const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
        //重置表单
        await resetFields()
        setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter })
        isUpdate.value = !!data?.isUpdate
        if (unref(isUpdate)) {
            //表单赋值
            await setFieldsValue({
                ...data.record,
            })
        }
        // éšè—åº•部时禁用整个表单
        setProps({ disabled: !data?.showFooter })
    })
    //设置标题
    const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'))
    //表单提交事件
    async function handleSubmit(v) {
        try {
            let values = await validate()
            setModalProps({ confirmLoading: true })
            //提交表单
            await saveOrUpdate(values, isUpdate.value)
            //关闭弹窗
            closeModal()
            //刷新列表
            emit('success')
        } finally {
            setModalProps({ confirmLoading: false })
        }
    }
</script>
<style lang="less" scoped>
    /** æ—¶é—´å’Œæ•°å­—输入框样式 */
  :deep(.ant-input-number){
        width: 100%
    :deep(.ant-input-number) {
        width: 100%;
    }
    :deep(.ant-calendar-picker){
        width: 100%
    :deep(.ant-calendar-picker) {
        width: 100%;
    }
</style>
</style>
src/views/dry/components/DryHerbForm.vue
@@ -1,70 +1,70 @@
<template>
  <div style="min-height: 400px">
    <BasicForm @register="registerForm" />
    <div style="width: 100%; text-align: center" v-if="!formDisabled">
      <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
    </div>
  </div>
    <div style="min-height: 400px">
        <BasicForm @register="registerForm" />
        <div style="width: 100%; text-align: center" v-if="!formDisabled">
            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
        </div>
    </div>
</template>
<script lang="ts">
  import { computed, defineComponent } from 'vue';
import { saveOrUpdate } from '../api/DryHerb.api';
import { getBpmFormSchema } from '../dataDefine/DryHerb.data';
import { BasicForm, useForm } from '/@/components/Form/index';
import { defHttp } from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
    import { computed, defineComponent } from 'vue'
    import { saveOrUpdate } from '../api/DryHerb.api'
    import { getBpmFormSchema } from '../dataDefine/DryHerb.data'
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { defHttp } from '/@/utils/http/axios'
    import { propTypes } from '/@/utils/propTypes'
  export default defineComponent({
    name: 'DryHerbForm',
    components: {
      BasicForm,
    },
    props: {
      formData: propTypes.object.def({}),
      formBpm: propTypes.bool.def(true),
    },
    setup(props) {
      const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
        labelWidth: 150,
        schemas: getBpmFormSchema(props.formData),
        showActionButtonGroup: false,
        baseColProps: { span: 24 },
      });
    export default defineComponent({
        name: 'DryHerbForm',
        components: {
            BasicForm,
        },
        props: {
            formData: propTypes.object.def({}),
            formBpm: propTypes.bool.def(true),
        },
        setup(props) {
            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
                labelWidth: 150,
                schemas: getBpmFormSchema(props.formData),
                showActionButtonGroup: false,
                baseColProps: { span: 24 },
            })
      const formDisabled = computed(() => {
        if (props.formData.disabled === false) {
          return false;
        }
        return true;
      });
            const formDisabled = computed(() => {
                if (props.formData.disabled === false) {
                    return false
                }
                return true
            })
      let formData = {};
      const queryByIdUrl = '/dry/dryHerb/queryById';
      async function initFormData() {
        let params = { id: props.formData.dataId };
        const data = await defHttp.get({ url: queryByIdUrl, params });
        formData = { ...data };
        //设置表单的值
        await setFieldsValue(formData);
        //默认是禁用
        await setProps({ disabled: formDisabled.value });
      }
            let formData = {}
            const queryByIdUrl = '/dry/dryHerb/queryById'
            async function initFormData() {
                let params = { id: props.formData.dataId }
                const data = await defHttp.get({ url: queryByIdUrl, params })
                formData = { ...data }
                //设置表单的值
                await setFieldsValue(formData)
                //默认是禁用
                await setProps({ disabled: formDisabled.value })
            }
      async function submitForm() {
        let data = getFieldsValue();
        let params = Object.assign({}, formData, data);
        console.log('表单数据', params);
        await saveOrUpdate(params, true);
      }
            async function submitForm() {
                let data = getFieldsValue()
                let params = Object.assign({}, formData, data)
                console.log('表单数据', params)
                await saveOrUpdate(params, true)
            }
      initFormData();
            initFormData()
      return {
        registerForm,
        formDisabled,
        submitForm,
      };
    },
  });
            return {
                registerForm,
                formDisabled,
                submitForm,
            }
        },
    })
</script>
src/views/dry/components/DryHerbFormulaForm.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
<template>
    <div style="min-height: 400px">
        <BasicForm @register="registerForm" />
        <div style="width: 100%; text-align: center" v-if="!formDisabled">
            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
        </div>
    </div>
</template>
<script lang="ts">
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { computed, defineComponent } from 'vue'
    import { defHttp } from '/@/utils/http/axios'
    import { propTypes } from '/@/utils/propTypes'
    import { getBpmFormSchema } from '../dataDefine/DryHerbFormula.data'
    import { saveOrUpdate } from '../api/DryHerbFormula.api'
    export default defineComponent({
        name: 'DryHerbFormulaForm',
        components: {
            BasicForm,
        },
        props: {
            formData: propTypes.object.def({}),
            formBpm: propTypes.bool.def(true),
        },
        setup(props) {
            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
                labelWidth: 150,
                schemas: getBpmFormSchema(props.formData),
                showActionButtonGroup: false,
                baseColProps: { span: 24 },
            })
            const formDisabled = computed(() => {
                if (props.formData.disabled === false) {
                    return false
                }
                return true
            })
            let formData = {}
            const queryByIdUrl = '/dry/dryHerbFormula/queryById'
            async function initFormData() {
                let params = { id: props.formData.dataId }
                const data = await defHttp.get({ url: queryByIdUrl, params })
                formData = { ...data }
                //设置表单的值
                await setFieldsValue(formData)
                //默认是禁用
                await setProps({ disabled: formDisabled.value })
            }
            async function submitForm() {
                let data = getFieldsValue()
                let params = Object.assign({}, formData, data)
                console.log('表单数据', params)
                await saveOrUpdate(params, true)
            }
            initFormData()
            return {
                registerForm,
                formDisabled,
                submitForm,
            }
        },
    })
</script>
src/views/dry/components/DryHerbFormulaModal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
<template>
    <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
        <BasicForm @register="registerForm" />
    </BasicModal>
</template>
<script lang="ts" setup>
    import { ref, computed, unref } from 'vue'
    import { BasicModal, useModalInner } from '/@/components/Modal'
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { formSchema } from '../dataDefine/DryHerbFormula.data'
    import { saveOrUpdate } from '../api/DryHerbFormula.api'
    // Emits声明
    const emit = defineEmits(['register', 'success'])
    const isUpdate = ref(true)
    //表单配置
    const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
        //labelWidth: 150,
        schemas: formSchema,
        showActionButtonGroup: false,
        baseColProps: { span: 24 },
    })
    //表单赋值
    const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
        //重置表单
        await resetFields()
        setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter })
        isUpdate.value = !!data?.isUpdate
        if (unref(isUpdate)) {
            //表单赋值
            await setFieldsValue({
                ...data.record,
            })
        }
        // éšè—åº•部时禁用整个表单
        setProps({ disabled: !data?.showFooter })
    })
    //设置标题
    const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'))
    //表单提交事件
    async function handleSubmit(v) {
        try {
            let values = await validate()
            setModalProps({ confirmLoading: true })
            //提交表单
            await saveOrUpdate(values, isUpdate.value)
            //关闭弹窗
            closeModal()
            //刷新列表
            emit('success')
        } finally {
            setModalProps({ confirmLoading: false })
        }
    }
</script>
<style lang="less" scoped>
    /** æ—¶é—´å’Œæ•°å­—输入框样式 */
    :deep(.ant-input-number) {
        width: 100%;
    }
    :deep(.ant-calendar-picker) {
        width: 100%;
    }
</style>
src/views/dry/components/DryHerbModal.vue
@@ -1,66 +1,66 @@
<template>
  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
    <BasicForm @register="registerForm" />
  </BasicModal>
    <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
        <BasicForm @register="registerForm" />
    </BasicModal>
</template>
<script lang="ts" setup>
  import { computed, ref, unref } from 'vue';
import { saveOrUpdate } from '../api/DryHerb.api';
import { formSchema } from '../dataDefine/DryHerb.data';
import { BasicForm, useForm } from '/@/components/Form/index';
import { BasicModal, useModalInner } from '/@/components/Modal';
  // Emits声明
  const emit = defineEmits(['register', 'success']);
  const isUpdate = ref(true);
  //表单配置
  const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
    //labelWidth: 150,
    schemas: formSchema,
    showActionButtonGroup: false,
    baseColProps: { span: 24 },
  });
  //表单赋值
  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
    //重置表单
    await resetFields();
    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
    isUpdate.value = !!data?.isUpdate;
    if (unref(isUpdate)) {
      //表单赋值
      await setFieldsValue({
        ...data.record,
      });
    }
    // éšè—åº•部时禁用整个表单
    setProps({ disabled: !data?.showFooter });
  });
  //设置标题
  const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  //表单提交事件
  async function handleSubmit(v) {
    try {
      let values = await validate();
      setModalProps({ confirmLoading: true });
      //提交表单
      await saveOrUpdate(values, isUpdate.value);
      //关闭弹窗
      closeModal();
      //刷新列表
      emit('success');
    } finally {
      setModalProps({ confirmLoading: false });
    }
  }
    import { computed, ref, unref } from 'vue'
    import { saveOrUpdate } from '../api/DryHerb.api'
    import { formSchema } from '../dataDefine/DryHerb.data'
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { BasicModal, useModalInner } from '/@/components/Modal'
    // Emits声明
    const emit = defineEmits(['register', 'success'])
    const isUpdate = ref(true)
    //表单配置
    const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
        //labelWidth: 150,
        schemas: formSchema,
        showActionButtonGroup: false,
        baseColProps: { span: 24 },
    })
    //表单赋值
    const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
        //重置表单
        await resetFields()
        setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter })
        isUpdate.value = !!data?.isUpdate
        if (unref(isUpdate)) {
            //表单赋值
            await setFieldsValue({
                ...data.record,
            })
        }
        // éšè—åº•部时禁用整个表单
        setProps({ disabled: !data?.showFooter })
    })
    //设置标题
    const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'))
    //表单提交事件
    async function handleSubmit(v) {
        try {
            let values = await validate()
            setModalProps({ confirmLoading: true })
            //提交表单
            await saveOrUpdate(values, isUpdate.value)
            //关闭弹窗
            closeModal()
            //刷新列表
            emit('success')
        } finally {
            setModalProps({ confirmLoading: false })
        }
    }
</script>
<style lang="less" scoped>
  /** æ—¶é—´å’Œæ•°å­—输入框样式 */
  :deep(.ant-input-number) {
    width: 100%;
  }
    /** æ—¶é—´å’Œæ•°å­—输入框样式 */
    :deep(.ant-input-number) {
        width: 100%;
    }
  :deep(.ant-calendar-picker) {
    width: 100%;
  }
    :deep(.ant-calendar-picker) {
        width: 100%;
    }
</style>
src/views/dry/components/DryOrderForm.vue
@@ -1,70 +1,70 @@
<template>
  <div style="min-height: 400px">
    <BasicForm @register="registerForm" />
    <div style="width: 100%; text-align: center" v-if="!formDisabled">
      <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
    </div>
  </div>
    <div style="min-height: 400px">
        <BasicForm @register="registerForm" />
        <div style="width: 100%; text-align: center" v-if="!formDisabled">
            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
        </div>
    </div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { saveOrUpdate } from '../api/DryOrder.api';
import { getBpmFormSchema } from '../dataDefine/DryOrder.data';
import { BasicForm, useForm } from '/@/components/Form/index';
import { defHttp } from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
    import { computed, defineComponent } from 'vue'
    import { saveOrUpdate } from '../api/DryOrder.api'
    import { getBpmFormSchema } from '../dataDefine/DryOrder.data'
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { defHttp } from '/@/utils/http/axios'
    import { propTypes } from '/@/utils/propTypes'
export default defineComponent({
  name: 'DryOrderForm',
  components: {
    BasicForm,
  },
  props: {
    formData: propTypes.object.def({}),
    formBpm: propTypes.bool.def(true),
  },
  setup(props) {
    const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
      labelWidth: 150,
      schemas: getBpmFormSchema(props.formData),
      showActionButtonGroup: false,
      baseColProps: { span: 24 },
    });
    export default defineComponent({
        name: 'DryOrderForm',
        components: {
            BasicForm,
        },
        props: {
            formData: propTypes.object.def({}),
            formBpm: propTypes.bool.def(true),
        },
        setup(props) {
            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
                labelWidth: 150,
                schemas: getBpmFormSchema(props.formData),
                showActionButtonGroup: false,
                baseColProps: { span: 24 },
            })
    const formDisabled = computed(() => {
      if (props.formData.disabled === false) {
        return false;
      }
      return true;
    });
            const formDisabled = computed(() => {
                if (props.formData.disabled === false) {
                    return false
                }
                return true
            })
    let formData = {};
    const queryByIdUrl = '/dry/dryOrder/queryById';
    async function initFormData() {
      let params = { id: props.formData.dataId };
      const data = await defHttp.get({ url: queryByIdUrl, params });
      formData = { ...data };
      //设置表单的值
      await setFieldsValue(formData);
      //默认是禁用
      await setProps({ disabled: formDisabled.value });
    }
            let formData = {}
            const queryByIdUrl = '/dry/dryOrder/queryById'
            async function initFormData() {
                let params = { id: props.formData.dataId }
                const data = await defHttp.get({ url: queryByIdUrl, params })
                formData = { ...data }
                //设置表单的值
                await setFieldsValue(formData)
                //默认是禁用
                await setProps({ disabled: formDisabled.value })
            }
    async function submitForm() {
      let data = getFieldsValue();
      let params = Object.assign({}, formData, data);
      console.log('表单数据', params);
      await saveOrUpdate(params, true);
    }
            async function submitForm() {
                let data = getFieldsValue()
                let params = Object.assign({}, formData, data)
                console.log('表单数据', params)
                await saveOrUpdate(params, true)
            }
    initFormData();
            initFormData()
    return {
      registerForm,
      formDisabled,
      submitForm,
    };
  },
});
            return {
                registerForm,
                formDisabled,
                submitForm,
            }
        },
    })
</script>
src/views/dry/components/DryOrderModal.vue
@@ -1,66 +1,66 @@
<template>
  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
    <BasicForm @register="registerForm" />
  </BasicModal>
    <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
        <BasicForm @register="registerForm" />
    </BasicModal>
</template>
<script lang="ts" setup>
import { computed, ref, unref } from 'vue';
import { saveOrUpdate } from '../api/DryOrder.api';
import { formSchema } from '../dataDefine/DryOrder.data';
import { BasicForm, useForm } from '/@/components/Form/index';
import { BasicModal, useModalInner } from '/@/components/Modal';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
  //labelWidth: 150,
  schemas: formSchema,
  showActionButtonGroup: false,
  baseColProps: { span: 24 },
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
  //重置表单
  await resetFields();
  setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
  isUpdate.value = !!data?.isUpdate;
  if (unref(isUpdate)) {
    //表单赋值
    await setFieldsValue({
      ...data.record,
    });
  }
  // éšè—åº•部时禁用整个表单
  setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
  try {
    let values = await validate();
    setModalProps({ confirmLoading: true });
    //提交表单
    await saveOrUpdate(values, isUpdate.value);
    //关闭弹窗
    closeModal();
    //刷新列表
    emit('success');
  } finally {
    setModalProps({ confirmLoading: false });
  }
}
    import { computed, ref, unref } from 'vue'
    import { saveOrUpdate } from '../api/DryOrder.api'
    import { formSchema } from '../dataDefine/DryOrder.data'
    import { BasicForm, useForm } from '/@/components/Form/index'
    import { BasicModal, useModalInner } from '/@/components/Modal'
    // Emits声明
    const emit = defineEmits(['register', 'success'])
    const isUpdate = ref(true)
    //searchFormSchema
    const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
        //labelWidth: 150,
        schemas: formSchema,
        showActionButtonGroup: false,
        baseColProps: { span: 24 },
    })
    //表单赋值
    const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
        //重置表单
        await resetFields()
        setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter })
        isUpdate.value = !!data?.isUpdate
        if (unref(isUpdate)) {
            //表单赋值
            await setFieldsValue({
                ...data.record,
            })
        }
        // éšè—åº•部时禁用整个表单
        setProps({ disabled: !data?.showFooter })
    })
    //设置标题
    const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'))
    //表单提交事件
    async function handleSubmit(v) {
        try {
            let values = await validate()
            setModalProps({ confirmLoading: true })
            //提交表单
            await saveOrUpdate(values, isUpdate.value)
            //关闭弹窗
            closeModal()
            //刷新列表
            emit('success')
        } finally {
            setModalProps({ confirmLoading: false })
        }
    }
</script>
<style lang="less" scoped>
/** æ—¶é—´å’Œæ•°å­—输入框样式 */
:deep(.ant-input-number) {
  width: 100%;
}
    /** æ—¶é—´å’Œæ•°å­—输入框样式 */
    :deep(.ant-input-number) {
        width: 100%;
    }
:deep(.ant-calendar-picker) {
  width: 100%;
}
    :deep(.ant-calendar-picker) {
        width: 100%;
    }
</style>
src/views/dry/dataDefine/DryEquipment.data.ts
@@ -1,140 +1,141 @@
import { dryOrder } from './DryOrder.data'
import { BasicColumn, FormSchema } from '/@/components/Table'
import { getTenantId } from '/@/utils/auth'
import { render } from '/@/utils/common/renderUtils'
import { rules } from '/@/utils/helper/validator'
//列表数据
export const columns: BasicColumn[] = [
  {
    title: '设备编号',
    align: 'center',
    dataIndex: 'code',
  },
  {
    title: '设备名称',
    align: 'center',
    dataIndex: 'name',
  },
  {
    title: '设备类型',
    align: 'center',
    dataIndex: 'type_dictText',
  },
  {
    title: '设备描述',
    align: 'center',
    dataIndex: 'remark',
  },
  {
    title: '车间id',
    align: 'center',
    dataIndex: 'shopId_dictText',
  },
  {
    title: '启用状态',
    align: 'center',
    dataIndex: 'enable',
    customRender: ({ text }) => {
      return render.renderSwitch(text, [
        { text: '是', value: 'Y' },
        { text: '否', value: 'N' },
      ])
    },
  },
    {
        title: '设备编号',
        align: 'center',
        dataIndex: 'code',
    },
    {
        title: '设备名称',
        align: 'center',
        dataIndex: 'name',
    },
    {
        title: '设备类型',
        align: 'center',
        dataIndex: 'type_dictText',
    },
    {
        title: '设备描述',
        align: 'center',
        dataIndex: 'remark',
    },
    {
        title: '车间id',
        align: 'center',
        dataIndex: 'shopId_dictText',
    },
    {
        title: '启用状态',
        align: 'center',
        dataIndex: 'enable',
        customRender: ({ text }) => {
            return render.renderSwitch(text, [
                { text: '是', value: 'Y' },
                { text: '否', value: 'N' },
            ])
        },
    },
]
//查询数据
export const searchFormSchema: FormSchema[] = [
  {
    label: '设备编号',
    field: 'code',
    component: 'Input',
    colProps: { span: 6 },
  },
  {
    label: '设备名称',
    field: 'name',
    component: 'Input',
    colProps: { span: 6 },
  },
  {
    label: '设备类型',
    field: 'type',
    component: 'JDictSelectTag',
    componentProps: {
      dictCode: 'dry_eqp_type,name,id,tenant_id=' + getTenantId(),
    },
    colProps: { span: 6 },
  },
  {
    label: '车间id',
    field: 'shopId',
    component: 'JDictSelectTag',
    componentProps: {
      dictCode: 'dry_shop,name,id,tenant_id=' + getTenantId(),
    },
    colProps: { span: 6 },
  },
    {
        label: '设备编号',
        field: 'code',
        component: 'Input',
        colProps: { span: 6 },
    },
    {
        label: '设备名称',
        field: 'name',
        component: 'Input',
        colProps: { span: 6 },
    },
    {
        label: '设备类型',
        field: 'type',
        component: 'JDictSelectTag',
        componentProps: {
            dictCode: 'dry_eqp_type,name,id,tenant_id=' + getTenantId(),
        },
        colProps: { span: 6 },
    },
    {
        label: '车间id',
        field: 'shopId',
        component: 'JDictSelectTag',
        componentProps: {
            dictCode: 'dry_shop,name,id,tenant_id=' + getTenantId(),
        },
        colProps: { span: 6 },
    },
]
//表单数据
export const formSchema: FormSchema[] = [
  {
    label: '设备编号',
    field: 'code',
    component: 'Input',
    dynamicRules: ({ model, schema }) => {
      return [{ required: true, message: '请输入设备编号!' }, { ...rules.duplicateCheckRule('dry_equipment', 'code', model, schema)[0] }]
    },
  },
  {
    label: '设备名称',
    field: 'name',
    component: 'Input',
    dynamicRules: ({ model, schema }) => {
      return [{ required: true, message: '请输入设备名称!' }, { ...rules.duplicateCheckRule('dry_equipment', 'name', model, schema)[0] }]
    },
  },
  {
    label: '设备类型',
    field: 'type',
    component: 'JDictSelectTag',
    componentProps: {
      dictCode: 'dry_eqp_type,name,id,tenant_id=' + getTenantId(),
    },
    dynamicRules: ({ model, schema }) => {
      return [{ required: true, message: '请输入设备类型!' }]
    },
  },
  {
    label: '设备描述',
    field: 'remark',
    component: 'Input',
  },
  {
    label: '车间id',
    field: 'shopId',
    component: 'JDictSelectTag',
    componentProps: {
      dictCode: 'dry_shop,name,id,tenant_id=' + getTenantId(),
    },
    dynamicRules: ({ model, schema }) => {
      return [{ required: true, message: '请输入车间id!' }]
    },
  },
  {
    label: '启用状态',
    field: 'enable',
    component: 'JSwitch',
    componentProps: {},
    dynamicRules: ({ model, schema }) => {
      return [{ required: true, message: '请输入启用状态!' }]
    },
  },
  // TODO ä¸»é”®éšè—å­—段,目前写死为ID
  {
    label: '',
    field: 'id',
    component: 'Input',
    show: false,
  },
    {
        label: '设备编号',
        field: 'code',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: true, message: '请输入设备编号!' }, { ...rules.duplicateCheckRule('dry_equipment', 'code', model, schema)[0] }]
        },
    },
    {
        label: '设备名称',
        field: 'name',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: true, message: '请输入设备名称!' }, { ...rules.duplicateCheckRule('dry_equipment', 'name', model, schema)[0] }]
        },
    },
    {
        label: '设备类型',
        field: 'type',
        component: 'JDictSelectTag',
        componentProps: {
            dictCode: 'dry_eqp_type,name,id,tenant_id=' + getTenantId(),
        },
        dynamicRules: ({ model, schema }) => {
            return [{ required: true, message: '请输入设备类型!' }]
        },
    },
    {
        label: '设备描述',
        field: 'remark',
        component: 'Input',
    },
    {
        label: '车间id',
        field: 'shopId',
        component: 'JDictSelectTag',
        componentProps: {
            dictCode: 'dry_shop,name,id,tenant_id=' + getTenantId(),
        },
        dynamicRules: ({ model, schema }) => {
            return [{ required: true, message: '请输入车间id!' }]
        },
    },
    {
        label: '启用状态',
        field: 'enable',
        component: 'JSwitch',
        componentProps: {},
        dynamicRules: ({ model, schema }) => {
            return [{ required: true, message: '请输入启用状态!' }]
        },
    },
    // TODO ä¸»é”®éšè—å­—段,目前写死为ID
    {
        label: '',
        field: 'id',
        component: 'Input',
        show: false,
    },
]
/**
@@ -142,6 +143,22 @@
 * @param param
 */
export function getBpmFormSchema(_formData): FormSchema[] {
  // é»˜è®¤å’ŒåŽŸå§‹è¡¨å•ä¿æŒä¸€è‡´ å¦‚果流程中配置了权限数据,这里需要单独处理formSchema
  return formSchema
    // é»˜è®¤å’ŒåŽŸå§‹è¡¨å•ä¿æŒä¸€è‡´ å¦‚果流程中配置了权限数据,这里需要单独处理formSchema
    return formSchema
}
export interface dryEquipment {
    id: string
    code: string
    name: string
    type: string
    remark: string
    shop_id: string
    enable: string
    create_by: string
    create_time: Date
    update_by: string
    update_time: Date
    tenant_id: number
    order: dryOrder
}
src/views/dry/dataDefine/DryHerbFormula.data.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,226 @@
import { BasicColumn, FormSchema } from '/@/components/Table'
//列表数据
export const columns: BasicColumn[] = [
    {
        title: '药材',
        align: 'center',
        dataIndex: 'herbId_dictText',
    },
    // {
    //     title: '药材名称',
    //     align: 'center',
    //     dataIndex: 'herbName',
    // },
    {
        title: '适用设备',
        align: 'center',
        dataIndex: 'eqpType_dictText',
    },
    {
        title: '目标含水率',
        align: 'center',
        dataIndex: 'target',
    },
    {
        title: '投料量',
        align: 'center',
        dataIndex: 'feed',
    },
    {
        title: '预计干燥时间',
        align: 'center',
        dataIndex: 'et',
    },
    {
        title: '热风温度',
        align: 'center',
        dataIndex: 'windTemp',
    },
    {
        title: '环境温度',
        align: 'center',
        dataIndex: 'envTemp',
    },
    {
        title: '环境湿度',
        align: 'center',
        dataIndex: 'envHum',
    },
    {
        title: '荡料延时ms',
        align: 'center',
        dataIndex: 'delay',
    },
    {
        title: '翻料次数',
        align: 'center',
        dataIndex: 'turn',
    },
    // {
    //     title: '租户id',
    //     align: 'center',
    //     dataIndex: 'tenantId',
    // },
]
//查询数据
export const searchFormSchema: FormSchema[] = [
    {
        label: '药材',
        field: 'herbId',
        component: 'JSearchSelect',
        componentProps: {
            dict: 'dry_herb,name,id',
        },
        colProps: { span: 6 },
    },
]
//表单数据
export const formSchema: FormSchema[] = [
    {
        label: '药材',
        field: 'herbId',
        component: 'JSearchSelect',
        componentProps: {
            dict: 'dry_herb,name,id',
        },
        dynamicRules: ({ model, schema }) => {
            return [{ required: true, message: '请选择药材!' }]
        },
    },
    // {
    //     label: '药材名称',
    //     field: 'herbName',
    //     component: 'Input',
    // },
    {
        label: '设备类型',
        field: 'eqpType',
        component: 'JSearchSelect',
        componentProps: {
            dict: 'dry_eqp_type,name,id',
        },
        dynamicRules: ({ model, schema }) => {
            return [{ required: true, message: '请设备设备类型!' }]
        },
    },
    {
        label: '目标含水率',
        field: 'target',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => '%',
            }
        },
    },
    {
        label: '投料量',
        field: 'feed',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => '筐',
            }
        },
    },
    {
        label: '预计干燥时间',
        field: 'et',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => '分钟',
            }
        },
    },
    {
        label: '热风温度',
        field: 'windTemp',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => '℃',
            }
        },
    },
    {
        label: '环境温度',
        field: 'envTemp',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => '℃',
            }
        },
    },
    {
        label: '环境湿度',
        field: 'envHum',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => 'rh',
            }
        },
    },
    {
        label: '荡料延时',
        field: 'delay',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => 'ms',
            }
        },
    },
    {
        label: '翻料次数',
        field: 'turn',
        component: 'Input',
        dynamicRules: ({ model, schema }) => {
            return [{ required: false }, { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' }]
        },
        renderComponentContent: () => {
            return {
                suffix: () => '次',
            }
        },
    },
    // TODO ä¸»é”®éšè—å­—段,目前写死为ID
    {
        label: '',
        field: 'id',
        component: 'Input',
        show: false,
    },
]
/**
 * æµç¨‹è¡¨å•调用这个方法获取formSchema
 * @param param
 */
export function getBpmFormSchema(_formData): FormSchema[] {
    // é»˜è®¤å’ŒåŽŸå§‹è¡¨å•ä¿æŒä¸€è‡´ å¦‚果流程中配置了权限数据,这里需要单独处理formSchema
    return formSchema
}
src/views/dry/dataDefine/DryOrder.data.ts
@@ -37,11 +37,11 @@
        align: 'center',
        dataIndex: 'feed',
    },
  {
    title: '初始重量',
    align: 'center',
    dataIndex: 'originWeight',
  },
    {
        title: '初始重量',
        align: 'center',
        dataIndex: 'originWeight',
    },
    {
        title: '预计干燥时间',
        align: 'center',
@@ -57,31 +57,31 @@
        align: 'center',
        dataIndex: 'dryTime',
    },
  {
    title: '热风温度',
    align: 'center',
    dataIndex: 'windTemp',
  },
  {
    title: '环境温度',
    align: 'center',
    dataIndex: 'envTemp',
  },
  {
    title: '环境湿度',
    align: 'center',
    dataIndex: 'envHum',
  },
  {
    title: '荡料延时(ms)',
    align: 'center',
    dataIndex: 'delay',
  },
  {
    title: '翻料次数',
    align: 'center',
    dataIndex: 'turn',
  },
    {
        title: '热风温度',
        align: 'center',
        dataIndex: 'windTemp',
    },
    {
        title: '环境温度',
        align: 'center',
        dataIndex: 'envTemp',
    },
    {
        title: '环境湿度',
        align: 'center',
        dataIndex: 'envHum',
    },
    {
        title: '荡料延时(ms)',
        align: 'center',
        dataIndex: 'delay',
    },
    {
        title: '翻料次数',
        align: 'center',
        dataIndex: 'turn',
    },
    {
        title: '预计剩余时间',
        align: 'center',
@@ -132,10 +132,10 @@
        label: '工单时间',
        field: 'orderTime',
        component: 'DatePicker',
    componentProps: {
      showTime: true,
      valueFormat: 'YYYY-MM-DD HH:mm:ss'
    },
        componentProps: {
            showTime: true,
            valueFormat: 'YYYY-MM-DD HH:mm:ss',
        },
    },
    {
        label: '工单号',
@@ -168,11 +168,11 @@
        field: 'feed',
        component: 'InputNumber',
    },
  {
    label: '原始重量',
    field: 'originWeight',
    component: 'InputNumber',
  },
    {
        label: '原始重量',
        field: 'originWeight',
        component: 'InputNumber',
    },
    {
        label: '预计干燥时间',
        field: 'et',
@@ -183,41 +183,41 @@
        field: 'yield',
        component: 'InputNumber',
    },
  {
    label: '干燥时间',
    field: 'dryTime',
    component: 'InputNumber',
  },
  {
    label: '热风温度',
    field: 'windTemp',
    component: 'InputNumber',
  },
  {
    label: '环境温度',
    field: 'envTemp',
    component: 'InputNumber',
  },
  {
    label: '环境湿度',
    field: 'envHum',
    component: 'InputNumber',
  },
  {
    label: '荡料延时(ms)',
    field: 'delay',
    component: 'InputNumber',
  },
  {
    label: '翻料次数',
    field: 'turn',
    component: 'InputNumber',
  },
  {
    label: '预计剩余时间',
    field: 'remain',
    component: 'InputNumber',
  },
    {
        label: '干燥时间',
        field: 'dryTime',
        component: 'InputNumber',
    },
    {
        label: '热风温度',
        field: 'windTemp',
        component: 'InputNumber',
    },
    {
        label: '环境温度',
        field: 'envTemp',
        component: 'InputNumber',
    },
    {
        label: '环境湿度',
        field: 'envHum',
        component: 'InputNumber',
    },
    {
        label: '荡料延时(ms)',
        field: 'delay',
        component: 'InputNumber',
    },
    {
        label: '翻料次数',
        field: 'turn',
        component: 'InputNumber',
    },
    {
        label: '预计剩余时间',
        field: 'remain',
        component: 'InputNumber',
    },
    {
        label: '设备',
        field: 'equId',
@@ -264,3 +264,34 @@
    // é»˜è®¤å’ŒåŽŸå§‹è¡¨å•ä¿æŒä¸€è‡´ å¦‚果流程中配置了权限数据,这里需要单独处理formSchema
    return formSchema
}
export interface dryOrder {
    id: string
    order_time: Date
    code: string
    herb_id: string
    herb_name: string
    initial: number
    target: number
    feed: number
    origin_weight: number
    et: number
    yield: number
    dry_time: number
    wind_temp: number
    env_temp: number
    env_hum: number
    delay: number
    turn: number
    remain: number
    equ_id: string
    shop_id: string
    tenant_id: string
    create_by: string
    create_time: Date
    update_by: string
    update_time: Date
    order_status: number
    operator: string
    temps: string
}
src/views/dry/monitor/WorkShop.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,513 @@
<template>
    <div class="workshop">
        <div class="eqp-row">
            <div v-for="(item, index) in eqps" :key="index">
                <div class="eqp-card">
                    <div class="eqp-content">
                        <div class="chart">
                                <div>
                                <div class="progress">
                                    <div style="">
                                        <div>加热</div>
                                        <div></div>
                                    <span class="info-text">36</span> åˆ†é’Ÿ
                                    </div>
                                    <div style=" padding: 0 10px;width: 200px;height: 40px;">
                                        <div style="padding-top: 10px">
                                                <Progress
                                                    :stroke-color="{
                                                        from: '#108ee9',
                                                        to: '#87d068',
                                                    }"
                                                    :percent="55.9"
                                                    status="active"
                                                    :show-info="false">
                                            </Progress>
                                        </div>
                                    </div>
                                    <div>
                                        <div>预计 </div>
                                        <div><span class="info-text">90</span>  åˆ†é’Ÿ
                                        </div>
                                    </div>
                            </div>
                            <div style="height:50px; text-align: left; padding-left: 25px; display: flex;">
                                <div class="herbInfo">
                                    <div>药材:<span class="info-text">当归</span></div>
                                    <div>投料:<span class="info-text">16</span> ç­</div>
                                </div>
                                <div class="herbInfo">
                                    <div>原始重量:<span class="info-text">160</span> Kg</div>
                                    <div>实时重量:<span class="info-text">70</span> Kg</div>
                                </div>
                            </div>
                        </div>
                            <div class="eqpStatus">
                                <div > <span class="info-text">运行</span>  </div>
                            </div>
                        </div>
                        <!-- <div :id="'chartDom' + item.id" class="chart"> </div> -->
                        <div class="info">
                            <div class="leftInfo">
                                <!-- <div class="herbName"> å½“å½’ </div> -->
                                <div class="eqpName">{{ item.name }}</div>
                            </div>
                            <div class="rightInfo">
                                <div style="width: 120px" >
                                    <div style="    height: 1px;
    font-size: 10px;
    text-align: center;
    margin-top: 10px;
    margin-bottom: -10px; ">初始:{{ (mois[0] * 100).toFixed(2) }}%</div>
                                    <div :id="'moisture' + item.id" style="width: 110px; height: 187px"></div>
                                    <div style="height: 1px;
    font-size: 10px;
    text-align: center;
    margin-top: -25px;">目标:{{ (mois[2] * 100).toFixed(2) }}%</div>
                                    <div style="    width: 110px;
    text-align: center;
    margin-top: 23px;">含水率</div>
                                </div>
                                <div class="tempChart"    style="pointer-events: none; cursor: none;"  :id="'tempDom' + item.id">
                                    <a-slider v-model:value="tempValue"
                                     :min="0" range :max="100" :marks="marks" vertical />
                                     <div>热风:<span class="info-text">{{ tempValue[0] }}</span> Â°C</div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
    import { onMounted, ref } from 'vue'
    import { Progress } from 'ant-design-vue'
    import * as echarts from 'echarts'
    import 'echarts-liquidfill'
    import { listAll } from '../api/DryEquipment.api'
    import { dryEquipment } from '../dataDefine/DryEquipment.data'
    const eqps = ref([] as dryEquipment[])
    const mois = ref([0.3939,0.2112,0.11])
    const tempValue = ref(<Record<number, number>>([70, 100]))
    const marks = ref<Record<number, any>>({
        0: '0°C',
        1: '',
        2: '',
        3: '',
        4: '',
        5: '',
        6: '',
        7: '',
        8: '',
        9: '',
        10: '',
        11: '',
        12: '',
        13: '',
        14: '',
        15: '',
        16: '',
        17: '',
        18: '',
        19: '',
        20: '',
        21: '',
        22: '',
        23: '',
        24: '',
        25: '25°C',
        26: '',
        27: '',
        28: '',
        29: '',
        30: '',
        31: '',
        32: '',
        33: '',
        34: '',
        35: '',
        36: '',
        37: '',
        38: '',
        39: '',
        40: '',
        41: '',
        42: '',
        43: '',
        44: '',
        45: '',
        46: '',
        47: '',
        48: '',
        49: '',
        50: '50°C',
        51: '',
        52: '',
        53: '',
        54: '',
        55: '',
        56: '',
        57: '',
        58: '',
        59: '',
        60: '',
        61: '',
        62: '',
        63: '',
        64: '',
        65: '',
        66: '',
        67: '',
        68: '',
        69: '',
        70: '',
        71: '',
        72: '',
        73: '',
        74: '',
        75: '75°C',
        76: '',
        77: '',
        78: '',
        79: '',
        80: '',
        81: '',
        82: '',
        83: '',
        84: '',
        85: '',
        86: '',
        87: '',
        88: '',
        89: '',
        90: '',
        91: '',
        92: '',
        93: '',
        94: '',
        95: '',
        96: '',
        97: '',
        98: '',
        99: '',
        100: '100°C',
    })
    function listAllEqp() {
        listAll({ enable: 'Y' })
            .then((result) => {
                console.log(`output->result`, result)
                eqps.value = result
                setTimeout(initCharts, 1000)
            })
            .catch((err) => {
                console.log(`output->err`, err)
            })
    }
    // var wetCharts: Map<string, echarts.ECharts> = new Map()
        var moistureCharts: Map<String, echarts.ECharts> = new Map()
     function initCharts() {
    //     console.log(`output->initChart`)
         eqps.value.forEach((item) => {
    //         console.log(`output->item.id`, item.id)
             let domId = 'moisture' + item.id
    //         console.log(`output->domId`, domId)
             var chartDom: HTMLElement = document.getElementById(domId) as HTMLElement
    //         console.log(`output->chartDom`, chartDom)
             let myChart = echarts.init(chartDom)
         //    var option
//              option = {
//                 grid: {
//                     left: 30,
//                     top: 15,
//                     bottom: 13,
//                     right: 45
//                 },
//   xAxis: {
//     type: 'category',
//         show: false,
//     data: ['含水率'],
//         axisLine: {
//             show: false,
//         },
//         axisTick: {
//             show: false,
//         }
//   },
//   yAxis: {
//     type: 'value',
//         axisLine: {
//             show: false,
//         },
//         min: 0,
//         max: 100,
//         axisTick: {
//             show: true,
//         },
//         splitLine: {
//             show: false
//         }
//   },
//   series: [
//     {
//       data: [20],
//       type: 'bar',
//       showBackground: true,
//       backgroundStyle: {
//         color: 'rgba(180, 180, 180, 0.2)'
//       },
//             label: {
//                 show: true,
//             },
//             barWidth: 20,
//             markLine: {
//                 symbol: 'none',
//                 data: [
//                     {symbol: 'none',
//                         xAxis:0,
//                         x:60,
//                         yAxis:60,
//                         lineStyle:{
//                             color: '#000',
//                             width:1,
//                         },
//                         label: {
//                             formatter: '初始\n'+ '{c}%'
//                         }
//                     },
//                     {symbol: 'none',
//                         xAxis:0,
//                         x:60,
//                         yAxis:11,
//                         lineStyle:{
//                             color: '#000',
//                             width:1,
//                         },
//                         label: {
//                             formatter: '目标\n'+ '{c}%'
//                         }
//                     },
//                     // {yAxis: 0},
//                     // {yAxis: 100}
//                 ]
//             }
//     },
//   ]
// };
const option = {
    series: [{
        type: 'liquidFill',
                radius: '100%',
                //waveAnimation: false,
                amplitude: 3,
        animationDuration: 5,
        //animationDurationUpdate: 0,
        data: mois.value,
                shape: 'path://M828.817,706.209C828.817,881.725,686.98,1024,512,1024c-174.98,0-316.817-142.275-316.817-317.791C195.183,530.74,512,0,512,0s316.817,530.74,316.817,706.209z',
                outline: {
            show: false
        },
                label: {
                    formatter: function(params) {
                        console.log(`output->params`,params,mois.value)
                        return ''
                        // +'初始'+(mois.value[0]*100).toFixed(2) + '%\n\n\n'
                        + (mois.value[1]*100).toFixed(2) + '%'
                        // + '\n\n\n目标'+(mois.value[2]*100).toFixed(2) + '%'
                        ;
                    },
                    fontSize: 10,
                    position: ['50%',(100-mois.value[1]*100).toFixed(2) + '%'],
                }
    }]
};
             option && myChart.setOption(option)
             moistureCharts.set(item.id, myChart)
         })
     }
    listAllEqp()
    // DOM挂载完成后渲染图表
    onMounted(() => {})
</script>
<style>
    .workshop {
    }
    .eqp-row {
        display: flex;
        flex-wrap: wrap;
    }
    .eqp-card {
        width: 566px;
        height: 400px;
        padding: 6px 6px;
    }
    .eqp-content {
        height: 100%;
        background-color: white;
        background-image: url('../../../assets/images/dry/ganzaoji-x.png');
        background-repeat: no-repeat;
        background-size: 60% 60%;
        background-position: 10px 150px;
        border-radius: 8px;
    }
    .chart {
        width: 550px;
        height: 160px;
        display: flex;
        text-align: center;
    }
    .progress {
        padding:25px 25px;
        width: 360px;
        display: flex;
    }
    .ant-progress-bg {
        height: 25px !important;
    }
    .eqpStatus {
        width:170px;
        background-position: 0 -10px;
        background-image: url('../../../assets/images/dry/ganzaoji.gif');
        background-size: 100% 100%;
        display: inline-flex;
        flex-direction: column-reverse;
        padding: 15px;
    }
    .info {
        display: flex;
    }
    .leftInfo {
        width: 335px;
        /* background: gray; */
    }
    .rightInfo {
        flex: 1;
        height: 210px;
        display: flex;
    }
    .info-text {
        font-size: 16px;
        font-weight: bold;
    }
    .herbName {
        margin-top: -10px;
        width: 180px;
        height: 35px;
        background: rgb(56, 56, 56);
        margin-left: 120px;
        text-align: center;
        color: white;
        font-size: 16px;
        font-weight: bold;
        line-height: 35px;
    }
    .eqpName {
        margin-top: 68px;
        margin-left: 174px;
        width: 95px;
        font-weight: bold;
        text-align: center;
        background-color: white;
    }
    .tempChart {
        padding-top: 10px;
        height: 150px;
        width: 100px;
    }
    .herbInfo {
    width: 160px;
    }
    .ant-slider-mark-text {
        padding-left: 15px;
        font-size: 10px;
    }
    .ant-slider-mark-text::before {
        content: '';
        display: block;
        width: 6px;
        height: 1px;
        background-color: #1890ff;
        position: absolute;
        top: 10px;
        left: 0px;
    }
    .ant-slider-rail {
        width: 6px !important;
        border-radius: 6px 6px 0 0;
        background: linear-gradient(to top, #ce0000 0%, #ce0000 40%, #ce0000 75%, rgb(160, 160, 160) 100%);
    }
    .ant-slider-track {
        background: rgb(175, 175, 175);
        height: 20px;
        width: 6px !important;
        border-radius: 6px 6px 0 0;
    }
    .ant-slider-track:hover {
    }
    .ant-slider-handle {
        display: none;
    }
    .ant-slider-dot {
        display: none;
    }
    .ant-slider-step {
        width: 10px !important;
    }
    .ant-slider-step > :first-child {
        display: block !important;
        width: 22px !important;
        height: 22px !important;
        bottom: -17px !important;
        left: -4px;
        border: none;
        background: #ce0000;
    }
</style>
src/views/dry/sql/DryHerbFormula_menu_insert.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
-- æ³¨æ„ï¼šè¯¥é¡µé¢å¯¹åº”的前台目录为views/dry文件夹下
-- å¦‚果你想更改到其他目录,请修改sql中component字段对应的值
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
VALUES ('2023051608547290510', NULL, '干燥配方', '/dry/dryHerbFormulaList', 'dry/DryHerbFormulaList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-05-16 08:54:51', NULL, NULL, 0);
-- æƒé™æŽ§åˆ¶sql
-- æ–°å¢ž
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023051608547300511', '2023051608547290510', '添加干燥配方', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_herb_formula:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-05-16 08:54:51', NULL, NULL, 0, 0, '1', 0);
-- ç¼–辑
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023051608547300512', '2023051608547290510', '编辑干燥配方', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_herb_formula:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-05-16 08:54:51', NULL, NULL, 0, 0, '1', 0);
-- åˆ é™¤
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023051608547300513', '2023051608547290510', '删除干燥配方', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_herb_formula:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-05-16 08:54:51', NULL, NULL, 0, 0, '1', 0);
-- æ‰¹é‡åˆ é™¤
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023051608547300514', '2023051608547290510', '批量删除干燥配方', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_herb_formula:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-05-16 08:54:51', NULL, NULL, 0, 0, '1', 0);
-- å¯¼å‡ºexcel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023051608547300515', '2023051608547290510', '导出excel_干燥配方', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_herb_formula:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-05-16 08:54:51', NULL, NULL, 0, 0, '1', 0);
-- å¯¼å…¥excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023051608547300516', '2023051608547290510', '导入excel_干燥配方', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_herb_formula:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-05-16 08:54:51', NULL, NULL, 0, 0, '1', 0);
src/views/system/departUser/components/DepartRoleAuthDrawer.vue
@@ -1,144 +1,158 @@
<template>
  <BasicDrawer title="部门角色权限配置"
               :width="650"
               :loading="loading"
               showFooter
               okText="保存并关闭"
               @ok="onSubmit(true)"
    @close="onClose" @register="registerDrawer">
    <div>
      <a-spin :spinning="loading">
        <template v-if="treeData.length > 0">
          <BasicTree title="所拥有的部门权限" toolbar checkable :treeData="treeData" :checkedKeys="checkedKeys"
            :selectedKeys="selectedKeys" :expandedKeys="expandedKeys" :checkStrictly="checkStrictly"
            :clickRowToExpand="false" @check="onCheck" @expand="onExpand" @select="onSelect">
            <template #title="{ slotTitle, ruleFlag }">
              <span>{{ slotTitle }}</span>
              <Icon v-if="ruleFlag" icon="ant-design:align-left-outlined" style="margin-left: 5px; color: red" />
            </template>
          </BasicTree>
        </template>
        <a-empty v-else description="无可配置部门权限" />
      </a-spin>
    </div>
    <BasicDrawer
        title="部门角色权限配置"
        :width="650"
        :loading="loading"
        showFooter
        okText="保存并关闭"
        @ok="onSubmit(true)"
        @close="onClose"
        @register="registerDrawer"
    >
        <div>
            <a-spin :spinning="loading">
                <template v-if="treeData.length > 0">
                    <BasicTree
                        title="所拥有的部门权限"
                        toolbar
                        checkable
                        :treeData="treeData"
                        :checkedKeys="checkedKeys"
                        :selectedKeys="selectedKeys"
                        :expandedKeys="expandedKeys"
                        :checkStrictly="checkStrictly"
                        :clickRowToExpand="false"
                        @check="onCheck"
                        @expand="onExpand"
                        @select="onSelect"
                    >
                        <template #title="{ slotTitle, ruleFlag }">
                            <span>{{ slotTitle }}</span>
                            <Icon v-if="ruleFlag" icon="ant-design:align-left-outlined" style="margin-left: 5px; color: red" />
                        </template>
                    </BasicTree>
                </template>
                <a-empty v-else description="无可配置部门权限" />
            </a-spin>
        </div>
    <template #centerFooter>
      <a-button type="primary" :loading="loading" ghost @click="onSubmit(false)">仅保存</a-button>
    </template>
  </BasicDrawer>
  <DepartRoleDataRuleDrawer @register="registerDataRuleDrawer" />
        <template #centerFooter>
            <a-button type="primary" :loading="loading" ghost @click="onSubmit(false)">仅保存</a-button>
        </template>
    </BasicDrawer>
    <DepartRoleDataRuleDrawer @register="registerDataRuleDrawer" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
    import { ref } from 'vue'
import { BasicTree } from '/@/components/Tree/index';
import { BasicDrawer, useDrawer, useDrawerInner } from '/@/components/Drawer';
import { useMessage } from '/@/hooks/web/useMessage';
    import { BasicDrawer, useDrawer, useDrawerInner } from '/@/components/Drawer'
    import { BasicTree } from '/@/components/Tree/index'
    import { useMessage } from '/@/hooks/web/useMessage'
import DepartRoleDataRuleDrawer from './DepartRoleDataRuleDrawer.vue';
import { queryTreeListForDeptRole, queryDeptRolePermission, saveDeptRolePermission } from '../depart.user.api';
    import { queryDeptRolePermission, queryTreeListForDeptRole, saveDeptRolePermission } from '../depart.user.api'
    import DepartRoleDataRuleDrawer from './DepartRoleDataRuleDrawer.vue'
defineEmits(['register']);
const { createMessage } = useMessage();
const loading = ref(false);
const departId = ref('');
const roleId = ref('');
const treeData = ref<Array<any>>([]);
const checkedKeys = ref<Array<any>>([]);
const lastCheckedKeys = ref<Array<any>>([]);
const expandedKeys = ref<Array<any>>([]);
const selectedKeys = ref<Array<any>>([]);
const allTreeKeys = ref<Array<any>>([]);
const checkStrictly = ref(true);
    defineEmits(['register'])
    const { createMessage } = useMessage()
    const loading = ref(false)
    const departId = ref('')
    const roleId = ref('')
    const treeData = ref<Array<any>>([])
    const checkedKeys = ref<Array<any>>([])
    const lastCheckedKeys = ref<Array<any>>([])
    const expandedKeys = ref<Array<any>>([])
    const selectedKeys = ref<Array<any>>([])
    const allTreeKeys = ref<Array<any>>([])
    const checkStrictly = ref(true)
// æ³¨å†ŒæŠ½å±‰ç»„ä»¶
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
  roleId.value = data.record.id;
  departId.value = data.record.departId;
  loadData();
});
// æ³¨å†Œæ•°æ®è§„则授权弹窗抽屉
const [registerDataRuleDrawer, dataRuleDrawer] = useDrawer();
    // æ³¨å†ŒæŠ½å±‰ç»„ä»¶
    const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
        roleId.value = data.record.id
        departId.value = data.record.departId
        loadData()
    })
    // æ³¨å†Œæ•°æ®è§„则授权弹窗抽屉
    const [registerDataRuleDrawer, dataRuleDrawer] = useDrawer()
async function loadData() {
  try {
    loading.value = true;
    // ç”¨æˆ·è§’色授权功能,查询菜单权限树
    const { ids, treeList } = await queryTreeListForDeptRole({ departId: departId.value });
    if (ids.length > 0) {
      allTreeKeys.value = ids;
      expandedKeys.value = ids;
      treeData.value = treeList;
      // æŸ¥è¯¢è§’色授权
      checkedKeys.value = await queryDeptRolePermission({ roleId: roleId.value });
      lastCheckedKeys.value = [checkedKeys.value];
    } else {
      reset();
    }
  } finally {
    loading.value = false;
  }
}
    async function loadData() {
        try {
            loading.value = true
            // ç”¨æˆ·è§’色授权功能,查询菜单权限树
            const { ids, treeList } = await queryTreeListForDeptRole({ departId: departId.value })
            if (ids.length > 0) {
                allTreeKeys.value = ids
                expandedKeys.value = ids
                treeData.value = treeList
                // æŸ¥è¯¢è§’色授权
                checkedKeys.value = await queryDeptRolePermission({ roleId: roleId.value })
                lastCheckedKeys.value = [checkedKeys.value]
            } else {
                reset()
            }
        } finally {
            loading.value = false
        }
    }
// é‡ç½®é¡µé¢
function reset() {
  treeData.value = [];
  expandedKeys.value = [];
  checkedKeys.value = [];
  lastCheckedKeys.value = [];
  loading.value = false;
}
    // é‡ç½®é¡µé¢
    function reset() {
        treeData.value = []
        expandedKeys.value = []
        checkedKeys.value = []
        lastCheckedKeys.value = []
        loading.value = false
    }
// tree勾选复选框事件
function onCheck(event) {
  if (checkStrictly.value) {
    checkedKeys.value = event.checked;
  } else {
    checkedKeys.value = event;
  }
}
    // tree勾选复选框事件
    function onCheck(event) {
        if (checkStrictly.value) {
            checkedKeys.value = event.checked
        } else {
            checkedKeys.value = event
        }
    }
// tree展开事件
function onExpand($expandedKeys) {
  expandedKeys.value = $expandedKeys;
}
    // tree展开事件
    function onExpand($expandedKeys) {
        expandedKeys.value = $expandedKeys
    }
// tree选中事件
function onSelect($selectedKeys, { selectedNodes }) {
  if (selectedNodes[0]?.ruleFlag) {
    let functionId = $selectedKeys[0];
    dataRuleDrawer.openDrawer(true, { roleId, departId, functionId });
  }
  selectedKeys.value = [];
}
    // tree选中事件
    function onSelect($selectedKeys, { selectedNodes }) {
        if (selectedNodes[0]?.ruleFlag) {
            let functionId = $selectedKeys[0]
            dataRuleDrawer.openDrawer(true, { roleId, departId, functionId })
        }
        selectedKeys.value = []
    }
function doClose() {
  reset();
  closeDrawer();
}
    function doClose() {
        reset()
        closeDrawer()
    }
function onClose() {
  reset();
}
    function onClose() {
        reset()
    }
async function onSubmit(exit) {
  try {
    loading.value = true;
    let params = {
      roleId: roleId.value,
      permissionIds: checkedKeys.value.join(','),
      lastpermissionIds: lastCheckedKeys.value.join(','),
    };
    await saveDeptRolePermission(params);
    if (exit) {
      doClose();
    }
  } finally {
    loading.value = false;
    if (!exit) {
      loadData();
    }
  }
}
    async function onSubmit(exit) {
        try {
            loading.value = true
            let params = {
                roleId: roleId.value,
                permissionIds: checkedKeys.value.join(','),
                lastpermissionIds: lastCheckedKeys.value.join(','),
            }
            await saveDeptRolePermission(params)
            if (exit) {
                doClose()
            }
        } finally {
            loading.value = false
            if (!exit) {
                loadData()
            }
        }
    }
</script>
src/views/system/loginmini/MiniLogin.vue
@@ -1,559 +1,563 @@
<template>
  <div :class="prefixCls" class="login-background-img">
    <AppLocalePicker class="absolute top-4 right-4 enter-x xl:text-gray-600" :showText="false"/>
    <AppDarkModeToggle class="absolute top-3 right-7 enter-x" />
    <div class="aui-logo" v-if="!getIsMobile">
      <div>
        <h3>
          <img :src="logoImg" alt="jeecg" />
        </h3>
      </div>
    </div>
    <div v-else class="aui-phone-logo">
      <img :src="logoImg" alt="jeecg" />
    </div>
    <div v-show="type === 'login'">
      <div class="aui-content">
        <div class="aui-container">
          <div class="aui-form">
            <div class="aui-image">
              <div>Lanpu</div>
<div>兰浦智能中草药干燥系统</div>
<!--              <div class="aui-image-text">-->
<!--                <img :src="adTextImg" />-->
<!--              </div>-->
            </div>
            <div class="aui-formBox">
              <div class="aui-formWell">
                <div class="aui-flex aui-form-nav investment_title">
                  <div class="aui-flex-box" :class="activeIndex === 'accountLogin' ? 'activeNav on' : ''" @click="loginClick('accountLogin')"
                    >{{ t('sys.login.signInFormTitle') }}
                  </div>
                  <div class="aui-flex-box" :class="activeIndex === 'phoneLogin' ? 'activeNav on' : ''" @click="loginClick('phoneLogin')"
                    >{{ t('sys.login.mobileSignInFormTitle') }}
                  </div>
                </div>
                <div class="aui-form-box" style="height: 180px">
                  <a-form ref="loginRef" :model="formData" v-if="activeIndex === 'accountLogin'" @keyup.enter.native="loginHandleClick">
                    <div class="aui-account">
                      <div class="aui-inputClear">
                        <i class="icon icon-code"></i>
                        <a-form-item>
                          <a-input class="fix-auto-fill" :placeholder="t('sys.login.userName')" v-model:value="formData.username" />
                        </a-form-item>
                      </div>
                      <div class="aui-inputClear">
                        <i class="icon icon-password"></i>
                        <a-form-item>
                          <a-input class="fix-auto-fill" type="password" :placeholder="t('sys.login.password')" v-model:value="formData.password" />
                        </a-form-item>
                      </div>
                      <div class="aui-inputClear">
                        <i class="icon icon-code"></i>
                        <a-form-item>
                          <a-input class="fix-auto-fill" type="text" :placeholder="t('sys.login.inputCode')" v-model:value="formData.inputCode" />
                        </a-form-item>
                        <div class="aui-code">
                          <img v-if="randCodeData.requestCodeSuccess" :src="randCodeData.randCodeImage" @click="handleChangeCheckCode" />
                          <img v-else style="margin-top: 2px; max-width: initial" :src="codeImg" @click="handleChangeCheckCode" />
                        </div>
                      </div>
                      <div class="aui-flex">
                        <div class="aui-flex-box">
                          <div class="aui-choice">
                            <a-input class="fix-auto-fill" type="checkbox" v-model:value="rememberMe" />
                            <span style="margin-left: 5px">{{ t('sys.login.rememberMe') }}</span>
                          </div>
                        </div>
                        <div class="aui-forget">
                          <a @click="forgetHandelClick"> {{ t('sys.login.forgetPassword') }}</a>
                        </div>
                      </div>
                    </div>
                  </a-form>
                  <a-form v-else ref="phoneFormRef" :model="phoneFormData" @keyup.enter.native="loginHandleClick">
                    <div class="aui-account phone">
                      <div class="aui-inputClear phoneClear">
                        <a-input class="fix-auto-fill" :placeholder="t('sys.login.mobile')" v-model:value="phoneFormData.mobile" />
                      </div>
                      <div class="aui-inputClear">
                        <a-input class="fix-auto-fill" :maxlength="6" :placeholder="t('sys.login.smsCode')" v-model:value="phoneFormData.smscode" />
                        <div v-if="showInterval" class="aui-code" @click="getLoginCode">
                          <a>{{ t('component.countdown.normalText') }}</a>
                        </div>
                        <div v-else class="aui-code">
                          <span class="aui-get-code code-shape">{{ t('component.countdown.sendText', [unref(timeRuning)]) }}</span>
                        </div>
                      </div>
                    </div>
                  </a-form>
                </div>
                <div class="aui-formButton">
                  <div class="aui-flex">
                    <a-button :loading="loginLoading" class="aui-link-login aui-flex-box" type="primary" @click="loginHandleClick">
                      {{ t('sys.login.loginButton') }}</a-button>
                  </div>
                  <div class="aui-flex">
                    <a class="aui-linek-code aui-flex-box" @click="codeHandleClick">{{ t('sys.login.qrSignInFormTitle') }}</a>
                  </div>
                  <div class="aui-flex">
                    <a class="aui-linek-code aui-flex-box" @click="registerHandleClick">{{ t('sys.login.registerButton') }}</a>
                  </div>
                </div>
              </div>
              <a-form @keyup.enter.native="loginHandleClick">
                <div class="aui-flex aui-third-text">
                  <div class="aui-flex-box aui-third-border">
                    <span>{{ t('sys.login.otherSignIn') }}</span>
                  </div>
                </div>
                <div class="aui-flex" :class="`${prefixCls}-sign-in-way`">
                  <div class="aui-flex-box">
                    <div class="aui-third-login">
                      <a title="github" @click="onThirdLogin('github')"><GithubFilled /></a>
                    </div>
                  </div>
                  <div class="aui-flex-box">
                    <div class="aui-third-login">
                      <a title="企业微信" @click="onThirdLogin('wechat_enterprise')"><icon-font class="item-icon" type="icon-qiyeweixin3" /></a>
                    </div>
                  </div>
                  <div class="aui-flex-box">
                    <div class="aui-third-login">
                      <a title="钉钉" @click="onThirdLogin('dingtalk')"><DingtalkCircleFilled /></a>
                    </div>
                  </div>
                  <div class="aui-flex-box">
                    <div class="aui-third-login">
                      <a title="微信" @click="onThirdLogin('wechat_open')"><WechatFilled /></a>
                    </div>
                  </div>
                </div>
              </a-form>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div v-show="type === 'forgot'" :class="`${prefixCls}-form`">
      <MiniForgotpad ref="forgotRef" @go-back="goBack" @success="handleSuccess" />
    </div>
    <div v-show="type === 'register'" :class="`${prefixCls}-form`">
      <MiniRegister ref="registerRef" @go-back="goBack" @success="handleSuccess" />
    </div>
    <div v-show="type === 'codeLogin'" :class="`${prefixCls}-form`">
      <MiniCodelogin ref="codeRef" @go-back="goBack" @success="handleSuccess" />
    </div>
    <!-- ç¬¬ä¸‰æ–¹ç™»å½•相关弹框 -->
    <ThirdModal ref="thirdModalRef"></ThirdModal>
  </div>
    <div :class="prefixCls" class="login-background-img">
        <AppLocalePicker class="absolute top-4 right-4 enter-x xl:text-gray-600" :showText="false" />
        <AppDarkModeToggle class="absolute top-3 right-7 enter-x" />
        <div class="aui-logo" v-if="!getIsMobile">
            <div>
                <h3>
                    <img :src="logoImg" alt="jeecg" />
                </h3>
            </div>
        </div>
        <div v-else class="aui-phone-logo">
            <img :src="logoImg" alt="jeecg" />
        </div>
        <div v-show="type === 'login'">
            <div class="aui-content">
                <div class="aui-container">
                    <div class="aui-form">
                        <div class="aui-image">
                            <div>Lanpu</div>
                            <div>兰浦智能中草药干燥系统</div>
                            <!--              <div class="aui-image-text">-->
                            <!--                <img :src="adTextImg" />-->
                            <!--              </div>-->
                        </div>
                        <div class="aui-formBox">
                            <div class="aui-formWell">
                                <div class="aui-flex aui-form-nav investment_title">
                                    <div class="aui-flex-box" :class="activeIndex === 'accountLogin' ? 'activeNav on' : ''" @click="loginClick('accountLogin')"
                                        >{{ t('sys.login.signInFormTitle') }}
                                    </div>
                                    <div class="aui-flex-box" :class="activeIndex === 'phoneLogin' ? 'activeNav on' : ''" @click="loginClick('phoneLogin')"
                                        >{{ t('sys.login.mobileSignInFormTitle') }}
                                    </div>
                                </div>
                                <div class="aui-form-box" style="height: 180px">
                                    <a-form ref="loginRef" :model="formData" v-if="activeIndex === 'accountLogin'" @keyup.enter.native="loginHandleClick">
                                        <div class="aui-account">
                                            <div class="aui-inputClear">
                                                <i class="icon icon-code"></i>
                                                <a-form-item>
                                                    <a-input class="fix-auto-fill" :placeholder="t('sys.login.userName')" v-model:value="formData.username" />
                                                </a-form-item>
                                            </div>
                                            <div class="aui-inputClear">
                                                <i class="icon icon-password"></i>
                                                <a-form-item>
                                                    <a-input class="fix-auto-fill" type="password" :placeholder="t('sys.login.password')" v-model:value="formData.password" />
                                                </a-form-item>
                                            </div>
                                            <div class="aui-inputClear">
                                                <i class="icon icon-code"></i>
                                                <a-form-item>
                                                    <a-input class="fix-auto-fill" type="text" :placeholder="t('sys.login.inputCode')" v-model:value="formData.inputCode" />
                                                </a-form-item>
                                                <div class="aui-code">
                                                    <img v-if="randCodeData.requestCodeSuccess" :src="randCodeData.randCodeImage" @click="handleChangeCheckCode" />
                                                    <img v-else style="margin-top: 2px; max-width: initial" :src="codeImg" @click="handleChangeCheckCode" />
                                                </div>
                                            </div>
                                            <div class="aui-flex">
                                                <div class="aui-flex-box">
                                                    <div class="aui-choice">
                                                        <a-input class="fix-auto-fill" type="checkbox" v-model:value="rememberMe" />
                                                        <span style="margin-left: 5px">{{ t('sys.login.rememberMe') }}</span>
                                                    </div>
                                                </div>
                                                <div class="aui-forget">
                                                    <a @click="forgetHandelClick"> {{ t('sys.login.forgetPassword') }}</a>
                                                </div>
                                            </div>
                                        </div>
                                    </a-form>
                                    <a-form v-else ref="phoneFormRef" :model="phoneFormData" @keyup.enter.native="loginHandleClick">
                                        <div class="aui-account phone">
                                            <div class="aui-inputClear phoneClear">
                                                <a-input class="fix-auto-fill" :placeholder="t('sys.login.mobile')" v-model:value="phoneFormData.mobile" />
                                            </div>
                                            <div class="aui-inputClear">
                                                <a-input class="fix-auto-fill" :maxlength="6" :placeholder="t('sys.login.smsCode')" v-model:value="phoneFormData.smscode" />
                                                <div v-if="showInterval" class="aui-code" @click="getLoginCode">
                                                    <a>{{ t('component.countdown.normalText') }}</a>
                                                </div>
                                                <div v-else class="aui-code">
                                                    <span class="aui-get-code code-shape">{{ t('component.countdown.sendText', [unref(timeRuning)]) }}</span>
                                                </div>
                                            </div>
                                        </div>
                                    </a-form>
                                </div>
                                <div class="aui-formButton">
                                    <div class="aui-flex">
                                        <a-button :loading="loginLoading" class="aui-link-login aui-flex-box" type="primary" @click="loginHandleClick">
                                            {{ t('sys.login.loginButton') }}</a-button
                                        >
                                    </div>
                                    <div class="aui-flex">
                                        <a class="aui-linek-code aui-flex-box" @click="codeHandleClick">{{ t('sys.login.qrSignInFormTitle') }}</a>
                                    </div>
                                    <div class="aui-flex">
                                        <a class="aui-linek-code aui-flex-box" @click="registerHandleClick">{{ t('sys.login.registerButton') }}</a>
                                    </div>
                                </div>
                            </div>
                            <a-form @keyup.enter.native="loginHandleClick">
                                <div class="aui-flex aui-third-text">
                                    <div class="aui-flex-box aui-third-border">
                                        <span>{{ t('sys.login.otherSignIn') }}</span>
                                    </div>
                                </div>
                                <div class="aui-flex" :class="`${prefixCls}-sign-in-way`">
                                    <div class="aui-flex-box">
                                        <div class="aui-third-login">
                                            <a title="github" @click="onThirdLogin('github')"><GithubFilled /></a>
                                        </div>
                                    </div>
                                    <div class="aui-flex-box">
                                        <div class="aui-third-login">
                                            <a title="企业微信" @click="onThirdLogin('wechat_enterprise')"><icon-font class="item-icon" type="icon-qiyeweixin3" /></a>
                                        </div>
                                    </div>
                                    <div class="aui-flex-box">
                                        <div class="aui-third-login">
                                            <a title="钉钉" @click="onThirdLogin('dingtalk')"><DingtalkCircleFilled /></a>
                                        </div>
                                    </div>
                                    <div class="aui-flex-box">
                                        <div class="aui-third-login">
                                            <a title="微信" @click="onThirdLogin('wechat_open')"><WechatFilled /></a>
                                        </div>
                                    </div>
                                </div>
                            </a-form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div v-show="type === 'forgot'" :class="`${prefixCls}-form`">
            <MiniForgotpad ref="forgotRef" @go-back="goBack" @success="handleSuccess" />
        </div>
        <div v-show="type === 'register'" :class="`${prefixCls}-form`">
            <MiniRegister ref="registerRef" @go-back="goBack" @success="handleSuccess" />
        </div>
        <div v-show="type === 'codeLogin'" :class="`${prefixCls}-form`">
            <MiniCodelogin ref="codeRef" @go-back="goBack" @success="handleSuccess" />
        </div>
        <!-- ç¬¬ä¸‰æ–¹ç™»å½•相关弹框 -->
        <ThirdModal ref="thirdModalRef" />
    </div>
</template>
<script lang="ts" setup name="login-mini">
  import { getCaptcha, getCodeInfo } from '/@/api/sys/user';
  import { computed, onMounted, reactive, ref, toRaw, unref } from 'vue';
  import codeImg from '/@/assets/images/checkcode.png';
  import { Rule } from '/@/components/Form';
  import { useUserStore } from '/@/store/modules/user';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { SmsEnum } from '/@/views/sys/login/useLogin';
  import ThirdModal from '/@/views/sys/login/ThirdModal.vue';
  import MiniForgotpad from './MiniForgotpad.vue';
  import MiniRegister from './MiniRegister.vue';
  import MiniCodelogin from './MiniCodelogin.vue';
  import logoImg from '/@/assets/loginmini/icon/lanpu_logo.png';
  import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
  import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application';
  import { useLocaleStore } from '/@/store/modules/locale';
  import { useDesign } from "/@/hooks/web/useDesign";
  import { useAppInject } from "/@/hooks/web/useAppInject";
  import { GithubFilled, WechatFilled, DingtalkCircleFilled, createFromIconfontCN } from '@ant-design/icons-vue';
    import { getCaptcha, getCodeInfo } from '/@/api/sys/user'
    import { computed, onMounted, reactive, ref, toRaw, unref } from 'vue'
    import codeImg from '/@/assets/images/checkcode.png'
    import { Rule } from '/@/components/Form'
    import { useUserStore } from '/@/store/modules/user'
    import { useMessage } from '/@/hooks/web/useMessage'
    import { useI18n } from '/@/hooks/web/useI18n'
    import { SmsEnum } from '/@/views/sys/login/useLogin'
    import ThirdModal from '/@/views/sys/login/ThirdModal.vue'
    import MiniForgotpad from './MiniForgotpad.vue'
    import MiniRegister from './MiniRegister.vue'
    import MiniCodelogin from './MiniCodelogin.vue'
    import logoImg from '/@/assets/loginmini/icon/lanpu_logo.png'
    import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png'
    import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application'
    import { useLocaleStore } from '/@/store/modules/locale'
    import { useDesign } from '/@/hooks/web/useDesign'
    import { useAppInject } from '/@/hooks/web/useAppInject'
    import { GithubFilled, WechatFilled, DingtalkCircleFilled, createFromIconfontCN } from '@ant-design/icons-vue'
  const IconFont = createFromIconfontCN({
    scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
  });
  const { prefixCls } = useDesign('mini-login');
  const { notification, createMessage } = useMessage();
  const userStore = useUserStore();
  const { t } = useI18n();
  const localeStore = useLocaleStore();
  const showLocale = localeStore.getShowPicker;
  const randCodeData = reactive<any>({
    randCodeImage: '',
    requestCodeSuccess: false,
    checkKey: null,
  });
  const rememberMe = ref<string>('0');
  //手机号登录还是账号登录
  const activeIndex = ref<string>('accountLogin');
  const type = ref<string>('login');
  //账号登录表单字段
  const formData = reactive<any>({
    inputCode: '',
    username: '',
    password: '',
  });
  //手机登录表单字段
  const phoneFormData = reactive<any>({
    mobile: '',
    smscode: '',
  });
  const loginRef = ref();
  //第三方登录弹窗
  const thirdModalRef = ref();
  //扫码登录
  const codeRef = ref();
  //是否显示获取验证码
  const showInterval = ref<boolean>(true);
  //60s
  const timeRuning = ref<number>(60);
  //定时器
  const timer = ref<any>(null);
  //忘记密码
  const forgotRef = ref();
  //注册
  const registerRef = ref();
  const loginLoading = ref<boolean>(false);
  const { getIsMobile } = useAppInject();
    const IconFont = createFromIconfontCN({
        scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
    })
    const { prefixCls } = useDesign('mini-login')
    const { notification, createMessage } = useMessage()
    const userStore = useUserStore()
    const { t } = useI18n()
    const localeStore = useLocaleStore()
    const showLocale = localeStore.getShowPicker
    const randCodeData = reactive<any>({
        randCodeImage: '',
        requestCodeSuccess: false,
        checkKey: null,
    })
    const rememberMe = ref<string>('0')
    //手机号登录还是账号登录
    const activeIndex = ref<string>('accountLogin')
    const type = ref<string>('login')
    //账号登录表单字段
    const formData = reactive<any>({
        inputCode: '',
        username: '',
        password: '',
    })
    //手机登录表单字段
    const phoneFormData = reactive<any>({
        mobile: '',
        smscode: '',
    })
    const loginRef = ref()
    //第三方登录弹窗
    const thirdModalRef = ref()
    //扫码登录
    const codeRef = ref()
    //是否显示获取验证码
    const showInterval = ref<boolean>(true)
    //60s
    const timeRuning = ref<number>(60)
    //定时器
    const timer = ref<any>(null)
    //忘记密码
    const forgotRef = ref()
    //注册
    const registerRef = ref()
    const loginLoading = ref<boolean>(false)
    const { getIsMobile } = useAppInject()
  defineProps({
    sessionTimeout: {
      type: Boolean,
    },
  });
    defineProps({
        sessionTimeout: {
            type: Boolean,
        },
    })
  /**
   * èŽ·å–éªŒè¯ç 
   */
  function handleChangeCheckCode() {
    formData.inputCode = '';
    /**
     * èŽ·å–éªŒè¯ç 
     */
    function handleChangeCheckCode() {
        formData.inputCode = ''
    randCodeData.checkKey = 1629428467008;
    getCodeInfo(randCodeData.checkKey).then((res) => {
      randCodeData.randCodeImage = res;
      randCodeData.requestCodeSuccess = true;
    });
  }
        randCodeData.checkKey = 1629428467008
        getCodeInfo(randCodeData.checkKey).then((res) => {
            randCodeData.randCodeImage = res
            randCodeData.requestCodeSuccess = true
        })
    }
  /**
   * åˆ‡æ¢ç™»å½•方式
   */
  function loginClick(type) {
    activeIndex.value = type;
  }
    /**
     * åˆ‡æ¢ç™»å½•方式
     */
    function loginClick(type) {
        activeIndex.value = type
    }
  /**
   * è´¦å·æˆ–者手机登录
   */
  async function loginHandleClick() {
    if (unref(activeIndex) === 'accountLogin') {
      accountLogin();
    } else {
      //手机号登录
      phoneLogin();
    }
  }
    /**
     * è´¦å·æˆ–者手机登录
     */
    async function loginHandleClick() {
        if (unref(activeIndex) === 'accountLogin') {
            accountLogin()
        } else {
            //手机号登录
            phoneLogin()
        }
    }
  async function accountLogin() {
    if (!formData.username) {
      createMessage.warn(t('sys.login.accountPlaceholder'));
      return;
    }
    if (!formData.password) {
      createMessage.warn(t('sys.login.passwordPlaceholder'));
      return;
    }
    try {
      loginLoading.value = true;
      const { userInfo } = await userStore.login(
        toRaw({
          password: formData.password,
          username: formData.username,
          captcha: formData.inputCode,
          checkKey: randCodeData.checkKey,
          mode: 'none', //不要默认的错误提示
        })
      );
      if (userInfo) {
        notification.success({
          message: t('sys.login.loginSuccessTitle'),
          description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
          duration: 3,
        });
      }
    } catch (error) {
      notification.error({
        message: t('sys.api.errorTip'),
        description: error.message || t('sys.login.networkExceptionMsg'),
        duration: 3,
      });
      handleChangeCheckCode();
    } finally {
      loginLoading.value = false;
    }
  }
    async function accountLogin() {
        if (!formData.username) {
            createMessage.warn(t('sys.login.accountPlaceholder'))
            return
        }
        if (!formData.password) {
            createMessage.warn(t('sys.login.passwordPlaceholder'))
            return
        }
        try {
            loginLoading.value = true
            const { userInfo } = await userStore.login(
                toRaw({
                    password: formData.password,
                    username: formData.username,
                    captcha: formData.inputCode,
                    checkKey: randCodeData.checkKey,
                    mode: 'none', //不要默认的错误提示
                })
            )
            if (userInfo) {
                notification.success({
                    message: t('sys.login.loginSuccessTitle'),
                    description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
                    duration: 3,
                })
            }
        } catch (error) {
            notification.error({
                message: t('sys.api.errorTip'),
                description: error.message || t('sys.login.networkExceptionMsg'),
                duration: 3,
            })
            handleChangeCheckCode()
        } finally {
            loginLoading.value = false
        }
    }
  /**
   * æ‰‹æœºå·ç™»å½•
   */
  async function phoneLogin() {
    if (!phoneFormData.mobile) {
      createMessage.warn(t('sys.login.mobilePlaceholder'));
      return;
    }
    if (!phoneFormData.smscode) {
      createMessage.warn(t('sys.login.smsPlaceholder'));
      return;
    }
    try {
      loginLoading.value = true;
      const { userInfo }: any = await userStore.phoneLogin({
        mobile: phoneFormData.mobile,
        captcha: phoneFormData.smscode,
        mode: 'none', //不要默认的错误提示
      });
      if (userInfo) {
        notification.success({
          message: t('sys.login.loginSuccessTitle'),
          description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
          duration: 3,
        });
      }
    } catch (error) {
      notification.error({
        message: t('sys.api.errorTip'),
        description: error.message || t('sys.login.networkExceptionMsg'),
        duration: 3,
      });
    } finally {
      loginLoading.value = false;
    }
  }
    /**
     * æ‰‹æœºå·ç™»å½•
     */
    async function phoneLogin() {
        if (!phoneFormData.mobile) {
            createMessage.warn(t('sys.login.mobilePlaceholder'))
            return
        }
        if (!phoneFormData.smscode) {
            createMessage.warn(t('sys.login.smsPlaceholder'))
            return
        }
        try {
            loginLoading.value = true
            const { userInfo }: any = await userStore.phoneLogin({
                mobile: phoneFormData.mobile,
                captcha: phoneFormData.smscode,
                mode: 'none', //不要默认的错误提示
            })
            if (userInfo) {
                notification.success({
                    message: t('sys.login.loginSuccessTitle'),
                    description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
                    duration: 3,
                })
            }
        } catch (error) {
            notification.error({
                message: t('sys.api.errorTip'),
                description: error.message || t('sys.login.networkExceptionMsg'),
                duration: 3,
            })
        } finally {
            loginLoading.value = false
        }
    }
  /**
   * èŽ·å–æ‰‹æœºéªŒè¯ç 
   */
  async function getLoginCode() {
    if (!phoneFormData.mobile) {
      createMessage.warn(t('sys.login.mobilePlaceholder'));
      return;
    }
    const result = await getCaptcha({ mobile: phoneFormData.mobile, smsmode: SmsEnum.FORGET_PASSWORD });
    if (result) {
      const TIME_COUNT = 60;
      if (!unref(timer)) {
        timeRuning.value = TIME_COUNT;
        showInterval.value = false;
        timer.value = setInterval(() => {
          if (unref(timeRuning) > 0 && unref(timeRuning) <= TIME_COUNT) {
            timeRuning.value = timeRuning.value - 1;
          } else {
            showInterval.value = true;
            clearInterval(unref(timer));
            timer.value = null;
          }
        }, 1000);
      }
    }
  }
    /**
     * èŽ·å–æ‰‹æœºéªŒè¯ç 
     */
    async function getLoginCode() {
        if (!phoneFormData.mobile) {
            createMessage.warn(t('sys.login.mobilePlaceholder'))
            return
        }
        const result = await getCaptcha({ mobile: phoneFormData.mobile, smsmode: SmsEnum.FORGET_PASSWORD })
        if (result) {
            const TIME_COUNT = 60
            if (!unref(timer)) {
                timeRuning.value = TIME_COUNT
                showInterval.value = false
                timer.value = setInterval(() => {
                    if (unref(timeRuning) > 0 && unref(timeRuning) <= TIME_COUNT) {
                        timeRuning.value = timeRuning.value - 1
                    } else {
                        showInterval.value = true
                        clearInterval(unref(timer))
                        timer.value = null
                    }
                }, 1000)
            }
        }
    }
  /**
   * ç¬¬ä¸‰æ–¹ç™»å½•
   * @param type
   */
  function onThirdLogin(type) {
    thirdModalRef.value.onThirdLogin(type);
  }
    /**
     * ç¬¬ä¸‰æ–¹ç™»å½•
     * @param type
     */
    function onThirdLogin(type) {
        thirdModalRef.value.onThirdLogin(type)
    }
  /**
   * å¿˜è®°å¯†ç 
   */
  function forgetHandelClick() {
    type.value = 'forgot';
    setTimeout(() => {
      forgotRef.value.initForm();
    }, 300);
  }
    /**
     * å¿˜è®°å¯†ç 
     */
    function forgetHandelClick() {
        type.value = 'forgot'
        setTimeout(() => {
            forgotRef.value.initForm()
        }, 300)
    }
  /**
   * è¿”回登录页面
   */
  function goBack() {
    activeIndex.value = 'accountLogin';
    type.value = 'login';
  }
    /**
     * è¿”回登录页面
     */
    function goBack() {
        activeIndex.value = 'accountLogin'
        type.value = 'login'
    }
  /**
   * å¿˜è®°å¯†ç /注册账号回调事件
   * @param value
   */
  function handleSuccess(value) {
    Object.assign(formData, value);
    Object.assign(phoneFormData, { mobile: "", smscode: "" });
    type.value = 'login';
    activeIndex.value = 'accountLogin';
    handleChangeCheckCode();
  }
    /**
     * å¿˜è®°å¯†ç /注册账号回调事件
     * @param value
     */
    function handleSuccess(value) {
        Object.assign(formData, value)
        Object.assign(phoneFormData, { mobile: '', smscode: '' })
        type.value = 'login'
        activeIndex.value = 'accountLogin'
        handleChangeCheckCode()
    }
  /**
   * æ³¨å†Œ
   */
  function registerHandleClick() {
    type.value = 'register';
    setTimeout(() => {
      registerRef.value.initForm();
    }, 300);
  }
    /**
     * æ³¨å†Œ
     */
    function registerHandleClick() {
        type.value = 'register'
        setTimeout(() => {
            registerRef.value.initForm()
        }, 300)
    }
  /**
   * æ³¨å†Œ
   */
  function codeHandleClick() {
    type.value = 'codeLogin';
    setTimeout(() => {
      codeRef.value.initFrom();
    }, 300);
  }
    /**
     * æ³¨å†Œ
     */
    function codeHandleClick() {
        type.value = 'codeLogin'
        setTimeout(() => {
            codeRef.value.initFrom()
        }, 300)
    }
  onMounted(() => {
    //加载验证码
    handleChangeCheckCode();
  });
    onMounted(() => {
        //加载验证码
        handleChangeCheckCode()
    })
</script>
<style lang="less" scoped>
  @import '/@/assets/loginmini/style/home.less';
  @import '/@/assets/loginmini/style/base.less';
    @import '/@/assets/loginmini/style/home.less';
    @import '/@/assets/loginmini/style/base.less';
  :deep(.ant-input:focus) {
    box-shadow: none;
  }
  .aui-get-code {
    float: right;
    position: relative;
    z-index: 3;
    background: #ffffff;
    color: #1573e9;
    border-radius: 100px;
    padding: 5px 16px;
    margin: 7px;
    border: 1px solid #1573e9;
    top: 12px;
  }
    :deep(.ant-input:focus) {
        box-shadow: none;
    }
    .aui-get-code {
        float: right;
        position: relative;
        z-index: 3;
        background: #ffffff;
        color: #1573e9;
        border-radius: 100px;
        padding: 5px 16px;
        margin: 7px;
        border: 1px solid #1573e9;
        top: 12px;
    }
  .aui-get-code:hover {
    color: #1573e9;
  }
    .aui-get-code:hover {
        color: #1573e9;
    }
  .code-shape {
    border-color: #dadada !important;
    color: #aaa !important;
  }
    .code-shape {
        border-color: #dadada !important;
        color: #aaa !important;
    }
  :deep(.jeecg-dark-switch){
    position:absolute;
    margin-right: 10px;
  }
  .aui-link-login{
    height: 42px;
    padding: 10px 15px;
    font-size: 14px;
    border-radius: 8px;
    margin-top: 15px;
    margin-bottom: 8px;
  }
  .aui-phone-logo{
    position: absolute;
    margin-left: 10px;
    width: 60px;
    top:2px;
    z-index: 4;
  }
  .top-3{
    top: 0.45rem;
  }
    :deep(.jeecg-dark-switch) {
        position: absolute;
        margin-right: 10px;
    }
    .aui-link-login {
        height: 42px;
        padding: 10px 15px;
        font-size: 14px;
        border-radius: 8px;
        margin-top: 15px;
        margin-bottom: 8px;
    }
    .aui-phone-logo {
        position: absolute;
        margin-left: 10px;
        width: 60px;
        top: 2px;
        z-index: 4;
    }
    .top-3 {
        top: 0.45rem;
    }
</style>
<style lang="less">
@prefix-cls: ~'@{namespace}-mini-login';
@dark-bg: #293146;
    @prefix-cls: ~'@{namespace}-mini-login';
    @dark-bg: #293146;
html[data-theme='dark'] {
  .@{prefix-cls} {
    background-color: @dark-bg !important;
    background-image: none;
    html[data-theme='dark'] {
        .@{prefix-cls} {
            background-color: @dark-bg !important;
            background-image: none;
    &::before {
      background-image: url(/@/assets/svg/login-bg-dark.svg);
    }
    .aui-inputClear{
      background-color: #232a3b !important;
    }
    .ant-input,
    .ant-input-password {
      background-color: #232a3b !important;
    }
            &::before {
                background-image: url(/@/assets/svg/login-bg-dark.svg);
            }
            .aui-inputClear {
                background-color: #232a3b !important;
            }
            .ant-input,
            .ant-input-password {
                background-color: #232a3b !important;
            }
    .ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
      border: 1px solid #4a5569 !important;
    }
            .ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
                border: 1px solid #4a5569 !important;
            }
    &-form {
      background: @dark-bg !important;
    }
            &-form {
                background: @dark-bg !important;
            }
    .app-iconify {
      color: #fff !important;
    }
    .aui-inputClear input,.aui-input-line input,.aui-choice{
      color: #c9d1d9 !important;
    }
            .app-iconify {
                color: #fff !important;
            }
            .aui-inputClear input,
            .aui-input-line input,
            .aui-choice {
                color: #c9d1d9 !important;
            }
    .aui-formBox{
      background-color: @dark-bg !important;
    }
    .aui-third-text span{
      background-color: @dark-bg !important;
    }
    .aui-form-nav .aui-flex-box{
      color: #c9d1d9 !important;
    }
            .aui-formBox {
                background-color: @dark-bg !important;
            }
            .aui-third-text span {
                background-color: @dark-bg !important;
            }
            .aui-form-nav .aui-flex-box {
                color: #c9d1d9 !important;
            }
    .aui-formButton .aui-linek-code{
      background:  @dark-bg !important;
      color: white !important;
    }
    .aui-code-line{
      border-left: none !important;
    }
    .ant-checkbox-inner,.aui-success h3{
      border-color: #c9d1d9;
    }
  }
            .aui-formButton .aui-linek-code {
                background: @dark-bg !important;
                color: white !important;
            }
            .aui-code-line {
                border-left: none !important;
            }
            .ant-checkbox-inner,
            .aui-success h3 {
                border-color: #c9d1d9;
            }
        }
  input.fix-auto-fill,
  .fix-auto-fill input {
    -webkit-text-fill-color: #c9d1d9 !important;
    box-shadow: inherit !important;
  }
        input.fix-auto-fill,
        .fix-auto-fill input {
            -webkit-text-fill-color: #c9d1d9 !important;
            box-shadow: inherit !important;
        }
  &-sign-in-way {
    .anticon {
      font-size: 22px !important;
      color: #888 !important;
      cursor: pointer !important;
        &-sign-in-way {
            .anticon {
                font-size: 22px !important;
                color: #888 !important;
                cursor: pointer !important;
      &:hover {
        color: @primary-color !important;
      }
    }
  }
  .ant-divider-inner-text {
    font-size: 12px !important;
    color: @text-color-secondary !important;
  }
  .aui-third-login a{
    background: transparent;
  }
}
                &:hover {
                    color: @primary-color !important;
                }
            }
        }
        .ant-divider-inner-text {
            font-size: 12px !important;
            color: @text-color-secondary !important;
        }
        .aui-third-login a {
            background: transparent;
        }
    }
</style>
stylelint.config.js
@@ -1,71 +1,60 @@
module.exports = {
  root: true,
  plugins: ['stylelint-order'],
  extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
  rules: {
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['global'],
      },
    ],
    'selector-pseudo-element-no-unknown': [
      true,
      {
        ignorePseudoElements: ['v-deep'],
        ignorePseudoElements: ['deep'],
      },
    ],
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: [
          'tailwind',
          'apply',
          'variants',
          'responsive',
          'screen',
          'function',
          'if',
          'each',
          'include',
          'mixin',
        ],
      },
    ],
    'no-empty-source': null,
    'named-grid-areas-no-invalid': null,
    'unicode-bom': 'never',
    'no-descending-specificity': null,
    'font-family-no-missing-generic-family-keyword': null,
    'declaration-colon-space-after': 'always-single-line',
    'declaration-colon-space-before': 'never',
    // 'declaration-block-trailing-semicolon': 'always',
    'rule-empty-line-before': [
      'always',
      {
        ignore: ['after-comment', 'first-nested'],
      },
    ],
    'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
    'order/order': [
      [
        'dollar-variables',
        'custom-properties',
        'at-rules',
        'declarations',
        {
          type: 'at-rule',
          name: 'supports',
        },
        {
          type: 'at-rule',
          name: 'media',
        },
        'rules',
      ],
      { severity: 'warning' },
    ],
  },
  ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
};
    root: true,
    plugins: ['stylelint-order'],
    extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
    rules: {
        'selector-pseudo-class-no-unknown': [
            true,
            {
                ignorePseudoClasses: ['global'],
            },
        ],
        'selector-pseudo-element-no-unknown': [
            true,
            {
                ignorePseudoElements: ['v-deep'],
                ignorePseudoElements: ['deep'],
            },
        ],
        'at-rule-no-unknown': [
            true,
            {
                ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen', 'function', 'if', 'each', 'include', 'mixin'],
            },
        ],
        'no-empty-source': null,
        'named-grid-areas-no-invalid': null,
        'unicode-bom': 'never',
        'no-descending-specificity': null,
        'font-family-no-missing-generic-family-keyword': null,
        'declaration-colon-space-after': 'always-single-line',
        'declaration-colon-space-before': 'never',
        // 'declaration-block-trailing-semicolon': 'always',
        'rule-empty-line-before': [
            'always',
            {
                ignore: ['after-comment', 'first-nested'],
            },
        ],
        'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
        'order/order': [
            [
                'dollar-variables',
                'custom-properties',
                'at-rules',
                'declarations',
                {
                    type: 'at-rule',
                    name: 'supports',
                },
                {
                    type: 'at-rule',
                    name: 'media',
                },
                'rules',
            ],
            { severity: 'warning' },
        ],
    },
    ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
}
vite.config.ts
@@ -1,112 +1,106 @@
import type { UserConfig, ConfigEnv } from 'vite';
import pkg from './package.json';
import dayjs from 'dayjs';
import { loadEnv } from 'vite';
import { resolve } from 'path';
import { generateModifyVars } from './build/generate/generateModifyVars';
import { createProxy } from './build/vite/proxy';
import { wrapperEnv } from './build/utils';
import { createVitePlugins } from './build/vite/plugin';
import { OUTPUT_DIR } from './build/constant';
import type { UserConfig, ConfigEnv } from 'vite'
import pkg from './package.json'
import dayjs from 'dayjs'
import { loadEnv } from 'vite'
import { resolve } from 'path'
import { generateModifyVars } from './build/generate/generateModifyVars'
import { createProxy } from './build/vite/proxy'
import { wrapperEnv } from './build/utils'
import { createVitePlugins } from './build/vite/plugin'
import { OUTPUT_DIR } from './build/constant'
function pathResolve(dir: string) {
  return resolve(process.cwd(), '.', dir);
    return resolve(process.cwd(), '.', dir)
}
const { dependencies, devDependencies, name, version } = pkg;
const { dependencies, devDependencies, name, version } = pkg
const __APP_INFO__ = {
  pkg: { dependencies, devDependencies, name, version },
  lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
};
    pkg: { dependencies, devDependencies, name, version },
    lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
}
export default ({ command, mode }: ConfigEnv): UserConfig => {
  const root = process.cwd();
    const root = process.cwd()
  const env = loadEnv(mode, root);
    const env = loadEnv(mode, root)
  // The boolean type read by loadEnv is a string. This function can be converted to boolean type
  const viteEnv = wrapperEnv(env);
    // The boolean type read by loadEnv is a string. This function can be converted to boolean type
    const viteEnv = wrapperEnv(env)
  const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY, VITE_DROP_CONSOLE } = viteEnv;
    const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY, VITE_DROP_CONSOLE } = viteEnv
  const isBuild = command === 'build';
    const isBuild = command === 'build'
  return {
    base: VITE_PUBLIC_PATH,
    root,
    resolve: {
      alias: [
        {
          find: 'vue-i18n',
          replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
        },
        // /@/xxxx => src/xxxx
        {
          find: /\/@\//,
          replacement: pathResolve('src') + '/',
        },
        // /#/xxxx => types/xxxx
        {
          find: /\/#\//,
          replacement: pathResolve('types') + '/',
        },
      ],
    },
    server: {
      // Listening on all local IPs
      host: true,
      https: false,
      port: VITE_PORT,
      // Load proxy configuration from .env
      proxy: createProxy(VITE_PROXY),
    },
    build: {
      minify: 'esbuild',
      target: 'es2015',
      cssTarget: 'chrome80',
      outDir: OUTPUT_DIR,
      terserOptions: {
        compress: {
          keep_infinity: true,
          // Used to delete console in production environment
          drop_console: VITE_DROP_CONSOLE,
          drop_debugger: true,
        },
      },
      // Turning off brotliSize display can slightly reduce packaging time
      reportCompressedSize: false,
      chunkSizeWarningLimit: 2000,
    },
    define: {
      // setting vue-i18-next
      // Suppress warning
      __INTLIFY_PROD_DEVTOOLS__: false,
      __APP_INFO__: JSON.stringify(__APP_INFO__),
    },
    css: {
      preprocessorOptions: {
        less: {
          modifyVars: generateModifyVars(),
          javascriptEnabled: true,
        },
      },
    },
    return {
        base: VITE_PUBLIC_PATH,
        root,
        resolve: {
            alias: [
                {
                    find: 'vue-i18n',
                    replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
                },
                // /@/xxxx => src/xxxx
                {
                    find: /\/@\//,
                    replacement: pathResolve('src') + '/',
                },
                // /#/xxxx => types/xxxx
                {
                    find: /\/#\//,
                    replacement: pathResolve('types') + '/',
                },
            ],
        },
        server: {
            // Listening on all local IPs
            host: true,
            https: false,
            port: VITE_PORT,
            // Load proxy configuration from .env
            proxy: createProxy(VITE_PROXY),
        },
        build: {
            minify: 'esbuild',
            target: 'es2015',
            cssTarget: 'chrome80',
            outDir: OUTPUT_DIR,
            terserOptions: {
                compress: {
                    keep_infinity: true,
                    // Used to delete console in production environment
                    drop_console: VITE_DROP_CONSOLE,
                    drop_debugger: true,
                },
            },
            // Turning off brotliSize display can slightly reduce packaging time
            reportCompressedSize: false,
            chunkSizeWarningLimit: 2000,
        },
        define: {
            // setting vue-i18-next
            // Suppress warning
            __INTLIFY_PROD_DEVTOOLS__: false,
            __APP_INFO__: JSON.stringify(__APP_INFO__),
        },
        css: {
            preprocessorOptions: {
                less: {
                    modifyVars: generateModifyVars(),
                    javascriptEnabled: true,
                },
            },
        },
    // The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
    plugins: createVitePlugins(viteEnv, isBuild),
        // The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
        plugins: createVitePlugins(viteEnv, isBuild),
    optimizeDeps: {
      esbuildOptions: {
        target: 'es2020',
      },
      // @iconify/iconify: The dependency is dynamically and virtually loaded by @purge-icons/generated, so it needs to be specified explicitly
      include: [
        '@vue/runtime-core',
        '@vue/shared',
        '@iconify/iconify',
        'ant-design-vue/es/locale/zh_CN',
        'ant-design-vue/es/locale/en_US',
      ],
    },
  };
};
        optimizeDeps: {
            esbuildOptions: {
                target: 'es2020',
            },
            // @iconify/iconify: The dependency is dynamically and virtually loaded by @purge-icons/generated, so it needs to be specified explicitly
            include: ['@vue/runtime-core', '@vue/shared', '@iconify/iconify', 'ant-design-vue/es/locale/zh_CN', 'ant-design-vue/es/locale/en_US'],
        },
    }
}
windi.config.ts
@@ -1,74 +1,74 @@
import { defineConfig } from 'vite-plugin-windicss';
import { primaryColor } from './build/config/themeConfig';
import { defineConfig } from 'vite-plugin-windicss'
import { primaryColor } from './build/config/themeConfig'
export default defineConfig({
  darkMode: 'class',
  plugins: [createEnterPlugin()],
  theme: {
    extend: {
      zIndex: {
        '-1': '-1',
      },
      colors: {
        primary: primaryColor,
      },
      screens: {
        sm: '576px',
        md: '768px',
        lg: '992px',
        xl: '1200px',
        '2xl': '1600px',
      },
    },
  },
});
    darkMode: 'class',
    plugins: [createEnterPlugin()],
    theme: {
        extend: {
            zIndex: {
                '-1': '-1',
            },
            colors: {
                primary: primaryColor,
            },
            screens: {
                sm: '576px',
                md: '768px',
                lg: '992px',
                xl: '1200px',
                '2xl': '1600px',
            },
        },
    },
})
/**
 * Used for animation when the element is displayed
 * @param maxOutput The larger the maxOutput output, the larger the generated css volume
 */
function createEnterPlugin(maxOutput = 8) {
  const createCss = (index: number, d = 'x') => {
    const upd = d.toUpperCase();
    return {
      [`*> .enter-${d}:nth-child(${index})`]: {
        transform: `translate${upd}(50px)`,
      },
      [`*> .-enter-${d}:nth-child(${index})`]: {
        transform: `translate${upd}(-50px)`,
      },
      [`* > .enter-${d}:nth-child(${index}),* > .-enter-${d}:nth-child(${index})`]: {
        'z-index': `${10 - index}`,
        opacity: '0',
        animation: `enter-${d}-animation 0.4s ease-in-out 0.3s`,
        'animation-fill-mode': 'forwards',
        'animation-delay': `${(index * 1) / 10}s`,
      },
    };
  };
  const handler = ({ addBase }) => {
    const addRawCss = {};
    for (let index = 1; index < maxOutput; index++) {
      Object.assign(addRawCss, {
        ...createCss(index, 'x'),
        ...createCss(index, 'y'),
      });
    }
    addBase({
      ...addRawCss,
      [`@keyframes enter-x-animation`]: {
        to: {
          opacity: '1',
          transform: 'translateX(0)',
        },
      },
      [`@keyframes enter-y-animation`]: {
        to: {
          opacity: '1',
          transform: 'translateY(0)',
        },
      },
    });
  };
  return { handler };
    const createCss = (index: number, d = 'x') => {
        const upd = d.toUpperCase()
        return {
            [`*> .enter-${d}:nth-child(${index})`]: {
                transform: `translate${upd}(50px)`,
            },
            [`*> .-enter-${d}:nth-child(${index})`]: {
                transform: `translate${upd}(-50px)`,
            },
            [`* > .enter-${d}:nth-child(${index}),* > .-enter-${d}:nth-child(${index})`]: {
                'z-index': `${10 - index}`,
                opacity: '0',
                animation: `enter-${d}-animation 0.4s ease-in-out 0.3s`,
                'animation-fill-mode': 'forwards',
                'animation-delay': `${(index * 1) / 10}s`,
            },
        }
    }
    const handler = ({ addBase }) => {
        const addRawCss = {}
        for (let index = 1; index < maxOutput; index++) {
            Object.assign(addRawCss, {
                ...createCss(index, 'x'),
                ...createCss(index, 'y'),
            })
        }
        addBase({
            ...addRawCss,
            [`@keyframes enter-x-animation`]: {
                to: {
                    opacity: '1',
                    transform: 'translateX(0)',
                },
            },
            [`@keyframes enter-y-animation`]: {
                to: {
                    opacity: '1',
                    transform: 'translateY(0)',
                },
            },
        })
    }
    return { handler }
}
yarn.lock
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó