mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 04:08:44 +08:00 
			
		
		
		
	Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3
This commit is contained in:
		
							
								
								
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@@ -102,6 +102,7 @@
 | 
			
		||||
    "codemirror",
 | 
			
		||||
    "commitlint",
 | 
			
		||||
    "cropperjs",
 | 
			
		||||
    "echart",
 | 
			
		||||
    "echarts",
 | 
			
		||||
    "esnext",
 | 
			
		||||
    "esno",
 | 
			
		||||
@@ -116,10 +117,12 @@
 | 
			
		||||
    "sider",
 | 
			
		||||
    "sortablejs",
 | 
			
		||||
    "stylelint",
 | 
			
		||||
    "svgs",
 | 
			
		||||
    "unocss",
 | 
			
		||||
    "unplugin",
 | 
			
		||||
    "unref",
 | 
			
		||||
    "videojs",
 | 
			
		||||
    "VITE",
 | 
			
		||||
    "vitejs",
 | 
			
		||||
    "vueuse",
 | 
			
		||||
    "wangeditor",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								package.json
									
									
									
									
									
								
							@@ -36,20 +36,20 @@
 | 
			
		||||
    "@wangeditor/editor-for-vue": "^5.1.10",
 | 
			
		||||
    "@zxcvbn-ts/core": "^3.0.4",
 | 
			
		||||
    "animate.css": "^4.1.1",
 | 
			
		||||
    "axios": "^1.5.1",
 | 
			
		||||
    "axios": "^1.6.0",
 | 
			
		||||
    "benz-amr-recorder": "^1.1.5",
 | 
			
		||||
    "bpmn-js-token-simulation": "^0.10.0",
 | 
			
		||||
    "camunda-bpmn-moddle": "^7.0.1",
 | 
			
		||||
    "cropperjs": "^1.6.1",
 | 
			
		||||
    "crypto-js": "^4.2.0",
 | 
			
		||||
    "dayjs": "^1.11.10",
 | 
			
		||||
    "diagram-js": "^12.5.0",
 | 
			
		||||
    "diagram-js": "^12.6.0",
 | 
			
		||||
    "driver.js": "^1.3.0",
 | 
			
		||||
    "echarts": "^5.4.3",
 | 
			
		||||
    "echarts-wordcloud": "^2.1.0",
 | 
			
		||||
    "element-plus": "2.4.1",
 | 
			
		||||
    "fast-xml-parser": "^4.3.2",
 | 
			
		||||
    "highlight.js": "^11.9.0",
 | 
			
		||||
    "intro.js": "^7.2.0",
 | 
			
		||||
    "jsencrypt": "^3.3.2",
 | 
			
		||||
    "lodash-es": "^4.17.21",
 | 
			
		||||
    "min-dash": "^4.1.1",
 | 
			
		||||
@@ -64,7 +64,7 @@
 | 
			
		||||
    "video.js": "^7.21.5",
 | 
			
		||||
    "vue": "^3.3.7",
 | 
			
		||||
    "vue-dompurify-html": "^4.1.4",
 | 
			
		||||
    "vue-i18n": "^9.5.0",
 | 
			
		||||
    "vue-i18n": "^9.6.2",
 | 
			
		||||
    "vue-router": "^4.2.5",
 | 
			
		||||
    "vue-types": "^5.1.1",
 | 
			
		||||
    "vuedraggable": "^4.1.0",
 | 
			
		||||
@@ -72,20 +72,19 @@
 | 
			
		||||
    "xml-js": "^1.6.11"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@commitlint/cli": "^18.0.0",
 | 
			
		||||
    "@commitlint/config-conventional": "^18.0.0",
 | 
			
		||||
    "@iconify/json": "^2.2.132",
 | 
			
		||||
    "@commitlint/cli": "^18.2.0",
 | 
			
		||||
    "@commitlint/config-conventional": "^18.1.0",
 | 
			
		||||
    "@iconify/json": "^2.2.135",
 | 
			
		||||
    "@intlify/unplugin-vue-i18n": "^1.4.0",
 | 
			
		||||
    "@purge-icons/generated": "^0.9.0",
 | 
			
		||||
    "@types/intro.js": "^5.1.3",
 | 
			
		||||
    "@types/lodash-es": "^4.17.10",
 | 
			
		||||
    "@types/node": "^20.8.8",
 | 
			
		||||
    "@types/node": "^20.8.9",
 | 
			
		||||
    "@types/nprogress": "^0.2.2",
 | 
			
		||||
    "@types/qrcode": "^1.5.4",
 | 
			
		||||
    "@types/qs": "^6.9.9",
 | 
			
		||||
    "@types/sortablejs": "^1.15.4",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^6.9.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^6.9.0",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^6.9.1",
 | 
			
		||||
    "@typescript-eslint/parser": "^6.9.1",
 | 
			
		||||
    "@unocss/transformer-variant-group": "^0.57.1",
 | 
			
		||||
    "@unocss/eslint-config": "^0.57.1",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^4.1.1",
 | 
			
		||||
@@ -99,24 +98,24 @@
 | 
			
		||||
    "eslint-config-prettier": "^9.0.0",
 | 
			
		||||
    "eslint-define-config": "^1.24.1",
 | 
			
		||||
    "eslint-plugin-prettier": "^5.0.1",
 | 
			
		||||
    "eslint-plugin-vue": "^9.18.0",
 | 
			
		||||
    "eslint-plugin-vue": "^9.18.1",
 | 
			
		||||
    "lint-staged": "^15.0.2",
 | 
			
		||||
    "postcss": "^8.4.31",
 | 
			
		||||
    "postcss-html": "^1.5.0",
 | 
			
		||||
    "postcss-scss": "^4.0.9",
 | 
			
		||||
    "prettier": "^3.0.3",
 | 
			
		||||
    "rimraf": "^5.0.5",
 | 
			
		||||
    "rollup": "^4.1.4",
 | 
			
		||||
    "sass": "^1.69.4",
 | 
			
		||||
    "rollup": "^4.1.5",
 | 
			
		||||
    "sass": "^1.69.5",
 | 
			
		||||
    "stylelint": "^15.11.0",
 | 
			
		||||
    "stylelint-config-html": "^1.1.0",
 | 
			
		||||
    "stylelint-config-recommended": "^13.0.0",
 | 
			
		||||
    "stylelint-config-standard": "^34.0.0",
 | 
			
		||||
    "stylelint-order": "^6.0.3",
 | 
			
		||||
    "terser": "^5.22.0",
 | 
			
		||||
    "terser": "^5.23.0",
 | 
			
		||||
    "typescript": "5.2.2",
 | 
			
		||||
    "unocss": "^0.57.1",
 | 
			
		||||
    "unplugin-auto-import": "^0.16.6",
 | 
			
		||||
    "unplugin-auto-import": "^0.16.7",
 | 
			
		||||
    "unplugin-element-plus": "^0.8.0",
 | 
			
		||||
    "unplugin-vue-components": "^0.25.2",
 | 
			
		||||
    "vite": "4.5.0",
 | 
			
		||||
@@ -128,7 +127,7 @@
 | 
			
		||||
    "vite-plugin-svg-icons": "^2.0.1",
 | 
			
		||||
    "vite-plugin-top-level-await": "^1.3.1",
 | 
			
		||||
    "vue-eslint-parser": "^9.3.2",
 | 
			
		||||
    "vue-tsc": "^1.8.20"
 | 
			
		||||
    "vue-tsc": "^1.8.22"
 | 
			
		||||
  },
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "repository": {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ defineComponent({
 | 
			
		||||
  name: 'CardTitle'
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const { title } = defineProps({
 | 
			
		||||
defineProps({
 | 
			
		||||
  title: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    required: true
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,19 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { provide, computed, watch, onMounted } from 'vue'
 | 
			
		||||
import { propTypes } from '@/utils/propTypes'
 | 
			
		||||
import { ComponentSize, ElConfigProvider } from 'element-plus'
 | 
			
		||||
import { useLocaleStore } from '@/store/modules/locale'
 | 
			
		||||
import { useWindowSize } from '@vueuse/core'
 | 
			
		||||
import { useAppStore } from '@/store/modules/app'
 | 
			
		||||
import { setCssVar } from '@/utils'
 | 
			
		||||
import { useDesign } from '@/hooks/web/useDesign'
 | 
			
		||||
import { ElementPlusSize } from '@/types/elementPlus'
 | 
			
		||||
import { useWindowSize } from '@vueuse/core'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'ConfigGlobal' })
 | 
			
		||||
 | 
			
		||||
const { variables } = useDesign()
 | 
			
		||||
 | 
			
		||||
const appStore = useAppStore()
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  size: propTypes.oneOf<ElementPlusSize>(['default', 'small', 'large']).def('default')
 | 
			
		||||
  size: propTypes.oneOf<ComponentSize>(['default', 'small', 'large']).def('default')
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
provide('configGlobal', props)
 | 
			
		||||
@@ -53,9 +52,9 @@ const currentLocale = computed(() => localeStore.currentLocale)
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <ElConfigProvider
 | 
			
		||||
    :namespace="variables.elNamespace"
 | 
			
		||||
    :locale="currentLocale.elLocale"
 | 
			
		||||
    :message="{ max: 1 }"
 | 
			
		||||
    :namespace="variables.elNamespace"
 | 
			
		||||
    :size="size"
 | 
			
		||||
  >
 | 
			
		||||
    <slot></slot>
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ import * as DateUtil from '@/utils/formatTime'
 | 
			
		||||
defineOptions({ name: 'ShortcutDateRangePicker' })
 | 
			
		||||
 | 
			
		||||
const shortcutDays = ref(7) // 日期快捷天数(单选按钮组), 默认7天
 | 
			
		||||
const times = ref<[dayjs.ConfigType, dayjs.ConfigType]>(['', '']) // 时间范围参数
 | 
			
		||||
const times = ref<[string, string]>(['', '']) // 时间范围参数
 | 
			
		||||
defineExpose({ times }) // 暴露时间范围参数
 | 
			
		||||
/** 日期快捷选择 */
 | 
			
		||||
const shortcuts = [
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ onMounted(() => {
 | 
			
		||||
 | 
			
		||||
  scrollContainer.value = getScrollContainer(refSticky.value!, true)
 | 
			
		||||
  useEventListener(scrollContainer, 'scroll', handleScroll)
 | 
			
		||||
  useEventListener('resize', handleReize)
 | 
			
		||||
  useEventListener('resize', handleResize)
 | 
			
		||||
  handleScroll()
 | 
			
		||||
})
 | 
			
		||||
onActivated(() => {
 | 
			
		||||
@@ -103,7 +103,7 @@ const handleScroll = () => {
 | 
			
		||||
    reset()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const handleReize = () => {
 | 
			
		||||
const handleResize = () => {
 | 
			
		||||
  if (isSticky.value && refSticky.value) {
 | 
			
		||||
    width.value = refSticky.value.getBoundingClientRect().width + 'px'
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,13 @@ import { propTypes } from '@/utils/propTypes'
 | 
			
		||||
defineOptions({ name: 'Tooltip' })
 | 
			
		||||
 | 
			
		||||
defineProps({
 | 
			
		||||
  titel: propTypes.string.def(''),
 | 
			
		||||
  title: propTypes.string.def(''),
 | 
			
		||||
  message: propTypes.string.def(''),
 | 
			
		||||
  icon: propTypes.string.def('ep:question-filled')
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
  <span>{{ titel }}</span>
 | 
			
		||||
  <span>{{ title }}</span>
 | 
			
		||||
  <ElTooltip :content="message" placement="top">
 | 
			
		||||
    <Icon :icon="icon" class="relative top-1px ml-1px" />
 | 
			
		||||
  </ElTooltip>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								src/hooks/web/useGuide.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/hooks/web/useGuide.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
import { Config, driver } from 'driver.js'
 | 
			
		||||
import 'driver.js/dist/driver.css'
 | 
			
		||||
import { useDesign } from '@/hooks/web/useDesign'
 | 
			
		||||
import { useI18n } from '@/hooks/web/useI18n'
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
const { variables } = useDesign()
 | 
			
		||||
 | 
			
		||||
export const useGuide = (options?: Config) => {
 | 
			
		||||
  const driverObj = driver(
 | 
			
		||||
    options || {
 | 
			
		||||
      showProgress: true,
 | 
			
		||||
      nextBtnText: t('common.nextLabel'),
 | 
			
		||||
      prevBtnText: t('common.prevLabel'),
 | 
			
		||||
      doneBtnText: t('common.doneLabel'),
 | 
			
		||||
      steps: [
 | 
			
		||||
        {
 | 
			
		||||
          element: `#${variables.namespace}-menu`,
 | 
			
		||||
          popover: {
 | 
			
		||||
            title: t('common.menu'),
 | 
			
		||||
            description: t('common.menuDes'),
 | 
			
		||||
            side: 'right'
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          element: `#${variables.namespace}-tool-header`,
 | 
			
		||||
          popover: {
 | 
			
		||||
            title: t('common.tool'),
 | 
			
		||||
            description: t('common.toolDes'),
 | 
			
		||||
            side: 'left'
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          element: `#${variables.namespace}-tags-view`,
 | 
			
		||||
          popover: {
 | 
			
		||||
            title: t('common.tagsView'),
 | 
			
		||||
            description: t('common.tagsViewDes'),
 | 
			
		||||
            side: 'bottom'
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    ...driverObj
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
import introJs from 'intro.js'
 | 
			
		||||
import { IntroJs, Step, Options } from 'intro.js'
 | 
			
		||||
import 'intro.js/introjs.css'
 | 
			
		||||
 | 
			
		||||
import { useDesign } from '@/hooks/web/useDesign'
 | 
			
		||||
 | 
			
		||||
export const useIntro = (setps?: Step[], options?: Options) => {
 | 
			
		||||
  const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
  const { variables } = useDesign()
 | 
			
		||||
 | 
			
		||||
  const defaultSetps: Step[] = setps || [
 | 
			
		||||
    {
 | 
			
		||||
      element: `#${variables.namespace}-menu`,
 | 
			
		||||
      title: t('common.menu'),
 | 
			
		||||
      intro: t('common.menuDes'),
 | 
			
		||||
      position: 'right'
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      element: `#${variables.namespace}-tool-header`,
 | 
			
		||||
      title: t('common.tool'),
 | 
			
		||||
      intro: t('common.toolDes'),
 | 
			
		||||
      position: 'left'
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      element: `#${variables.namespace}-tags-view`,
 | 
			
		||||
      title: t('common.tagsView'),
 | 
			
		||||
      intro: t('common.tagsViewDes'),
 | 
			
		||||
      position: 'bottom'
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
  const defaultOptions: Options = options || {
 | 
			
		||||
    prevLabel: t('common.prevLabel'),
 | 
			
		||||
    nextLabel: t('common.nextLabel'),
 | 
			
		||||
    skipLabel: t('common.skipLabel'),
 | 
			
		||||
    doneLabel: t('common.doneLabel')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const introRef: IntroJs = introJs()
 | 
			
		||||
 | 
			
		||||
  introRef.addSteps(defaultSetps).setOptions(defaultOptions)
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    introRef
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								src/hooks/web/useNetwork.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/hooks/web/useNetwork.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
import { ref, onBeforeUnmount } from 'vue'
 | 
			
		||||
 | 
			
		||||
const useNetwork = () => {
 | 
			
		||||
  const online = ref(true)
 | 
			
		||||
 | 
			
		||||
  const updateNetwork = () => {
 | 
			
		||||
    online.value = navigator.onLine
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  window.addEventListener('online', updateNetwork)
 | 
			
		||||
  window.addEventListener('offline', updateNetwork)
 | 
			
		||||
 | 
			
		||||
  onBeforeUnmount(() => {
 | 
			
		||||
    window.removeEventListener('online', updateNetwork)
 | 
			
		||||
    window.removeEventListener('offline', updateNetwork)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return { online }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { useNetwork }
 | 
			
		||||
							
								
								
									
										60
									
								
								src/hooks/web/useNow.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/hooks/web/useNow.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
import { dateUtil } from '@/utils/dateUtil'
 | 
			
		||||
import { reactive, toRefs } from 'vue'
 | 
			
		||||
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core'
 | 
			
		||||
 | 
			
		||||
export const useNow = (immediate = true) => {
 | 
			
		||||
  let timer: IntervalHandle
 | 
			
		||||
 | 
			
		||||
  const state = reactive({
 | 
			
		||||
    year: 0,
 | 
			
		||||
    month: 0,
 | 
			
		||||
    week: '',
 | 
			
		||||
    day: 0,
 | 
			
		||||
    hour: '',
 | 
			
		||||
    minute: '',
 | 
			
		||||
    second: 0,
 | 
			
		||||
    meridiem: ''
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const update = () => {
 | 
			
		||||
    const now = dateUtil()
 | 
			
		||||
 | 
			
		||||
    const h = now.format('HH')
 | 
			
		||||
    const m = now.format('mm')
 | 
			
		||||
    const s = now.get('s')
 | 
			
		||||
 | 
			
		||||
    state.year = now.get('y')
 | 
			
		||||
    state.month = now.get('M') + 1
 | 
			
		||||
    state.week = '星期' + ['日', '一', '二', '三', '四', '五', '六'][now.day()]
 | 
			
		||||
    state.day = now.get('date')
 | 
			
		||||
    state.hour = h
 | 
			
		||||
    state.minute = m
 | 
			
		||||
    state.second = s
 | 
			
		||||
 | 
			
		||||
    state.meridiem = now.format('A')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function start() {
 | 
			
		||||
    update()
 | 
			
		||||
    clearInterval(timer)
 | 
			
		||||
    timer = setInterval(() => update(), 1000)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function stop() {
 | 
			
		||||
    clearInterval(timer)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tryOnMounted(() => {
 | 
			
		||||
    immediate && start()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  tryOnUnmounted(() => {
 | 
			
		||||
    stop()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    ...toRefs(state),
 | 
			
		||||
    start,
 | 
			
		||||
    stop
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								src/hooks/web/useTagsView.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/hooks/web/useTagsView.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
import { useTagsViewStoreWithOut } from '@/store/modules/tagsView'
 | 
			
		||||
import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router'
 | 
			
		||||
import { computed, nextTick, unref } from 'vue'
 | 
			
		||||
 | 
			
		||||
export const useTagsView = () => {
 | 
			
		||||
  const tagsViewStore = useTagsViewStoreWithOut()
 | 
			
		||||
 | 
			
		||||
  const { replace, currentRoute } = useRouter()
 | 
			
		||||
 | 
			
		||||
  const selectedTag = computed(() => tagsViewStore.getSelectedTag)
 | 
			
		||||
 | 
			
		||||
  const closeAll = (callback?: Fn) => {
 | 
			
		||||
    tagsViewStore.delAllViews()
 | 
			
		||||
    callback?.()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const closeLeft = (callback?: Fn) => {
 | 
			
		||||
    tagsViewStore.delLeftViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
 | 
			
		||||
    callback?.()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const closeRight = (callback?: Fn) => {
 | 
			
		||||
    tagsViewStore.delRightViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
 | 
			
		||||
    callback?.()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const closeOther = (callback?: Fn) => {
 | 
			
		||||
    tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
 | 
			
		||||
    callback?.()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const closeCurrent = (view?: RouteLocationNormalizedLoaded, callback?: Fn) => {
 | 
			
		||||
    if (view?.meta?.affix) return
 | 
			
		||||
    tagsViewStore.delView(view || unref(currentRoute))
 | 
			
		||||
 | 
			
		||||
    callback?.()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const refreshPage = async (view?: RouteLocationNormalizedLoaded, callback?: Fn) => {
 | 
			
		||||
    tagsViewStore.delCachedView()
 | 
			
		||||
    const { path, query } = view || unref(currentRoute)
 | 
			
		||||
    await nextTick()
 | 
			
		||||
    replace({
 | 
			
		||||
      path: '/redirect' + path,
 | 
			
		||||
      query: query
 | 
			
		||||
    })
 | 
			
		||||
    callback?.()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const setTitle = (title: string, path?: string) => {
 | 
			
		||||
    tagsViewStore.setTitle(title, path)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    closeAll,
 | 
			
		||||
    closeLeft,
 | 
			
		||||
    closeRight,
 | 
			
		||||
    closeOther,
 | 
			
		||||
    closeCurrent,
 | 
			
		||||
    refreshPage,
 | 
			
		||||
    setTitle
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,54 +1,53 @@
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
import { useI18n } from '@/hooks/web/useI18n'
 | 
			
		||||
import { FormItemRule } from 'element-plus'
 | 
			
		||||
 | 
			
		||||
type Callback = (error?: string | Error | undefined) => void
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
interface LengthRange {
 | 
			
		||||
  min: number
 | 
			
		||||
  max: number
 | 
			
		||||
  message: string
 | 
			
		||||
  message?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useValidator = () => {
 | 
			
		||||
  const required = (message?: string) => {
 | 
			
		||||
  const required = (message?: string): FormItemRule => {
 | 
			
		||||
    return {
 | 
			
		||||
      required: true,
 | 
			
		||||
      message: message || t('common.required')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const lengthRange = (val: any, callback: Callback, options: LengthRange) => {
 | 
			
		||||
  const lengthRange = (options: LengthRange): FormItemRule => {
 | 
			
		||||
    const { min, max, message } = options
 | 
			
		||||
    if (val.length < min || val.length > max) {
 | 
			
		||||
      callback(new Error(message))
 | 
			
		||||
    } else {
 | 
			
		||||
      callback()
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      min,
 | 
			
		||||
      max,
 | 
			
		||||
      message: message || t('common.lengthRange', { min, max })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const notSpace = (val: any, callback: Callback, message: string) => {
 | 
			
		||||
    // 用户名不能有空格
 | 
			
		||||
    if (val.indexOf(' ') !== -1) {
 | 
			
		||||
      callback(new Error(message))
 | 
			
		||||
    } else {
 | 
			
		||||
      callback()
 | 
			
		||||
  const notSpace = (message?: string): FormItemRule => {
 | 
			
		||||
    return {
 | 
			
		||||
      validator: (_, val, callback) => {
 | 
			
		||||
        if (val?.indexOf(' ') !== -1) {
 | 
			
		||||
          callback(new Error(message || t('common.notSpace')))
 | 
			
		||||
        } else {
 | 
			
		||||
          callback()
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const notSpecialCharacters = (val: any, callback: Callback, message: string) => {
 | 
			
		||||
    // 密码不能是特殊字符
 | 
			
		||||
    if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) {
 | 
			
		||||
      callback(new Error(message))
 | 
			
		||||
    } else {
 | 
			
		||||
      callback()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 两个字符串是否想等
 | 
			
		||||
  const isEqual = (val1: string, val2: string, callback: Callback, message: string) => {
 | 
			
		||||
    if (val1 === val2) {
 | 
			
		||||
      callback()
 | 
			
		||||
    } else {
 | 
			
		||||
      callback(new Error(message))
 | 
			
		||||
  const notSpecialCharacters = (message?: string): FormItemRule => {
 | 
			
		||||
    return {
 | 
			
		||||
      validator: (_, val, callback) => {
 | 
			
		||||
        if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) {
 | 
			
		||||
          callback(new Error(message || t('common.notSpecialCharacters')))
 | 
			
		||||
        } else {
 | 
			
		||||
          callback()
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -56,7 +55,6 @@ export const useValidator = () => {
 | 
			
		||||
    required,
 | 
			
		||||
    lengthRange,
 | 
			
		||||
    notSpace,
 | 
			
		||||
    notSpecialCharacters,
 | 
			
		||||
    isEqual
 | 
			
		||||
    notSpecialCharacters
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -334,6 +334,6 @@ export function getLast1Year(): [dayjs.ConfigType, dayjs.ConfigType] {
 | 
			
		||||
export function getDateRange(
 | 
			
		||||
  beginDate: dayjs.ConfigType,
 | 
			
		||||
  endDate: dayjs.ConfigType
 | 
			
		||||
): [dayjs.ConfigType, dayjs.ConfigType] {
 | 
			
		||||
  return [dayjs(beginDate).startOf('d'), dayjs(endDate).endOf('d')]
 | 
			
		||||
): [string, string] {
 | 
			
		||||
  return [dayjs(beginDate).startOf('d').toString(), dayjs(endDate).endOf('d').toString()]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,13 @@ export const underlineToHump = (str: string): string => {
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 驼峰转横杠
 | 
			
		||||
 */
 | 
			
		||||
export const humpToDash = (str: string): string => {
 | 
			
		||||
  return str.replace(/([A-Z])/g, '-$1').toLowerCase()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
 | 
			
		||||
  dom.style.setProperty(prop, val)
 | 
			
		||||
}
 | 
			
		||||
@@ -67,7 +74,7 @@ export const trim = (str: string) => {
 | 
			
		||||
 * @param {Date | number | string} time 需要转换的时间
 | 
			
		||||
 * @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss
 | 
			
		||||
 */
 | 
			
		||||
export const formatTime = (time: Date | number | string, fmt: string) => {
 | 
			
		||||
export function formatTime(time: Date | number | string, fmt: string) {
 | 
			
		||||
  if (!time) return ''
 | 
			
		||||
  else {
 | 
			
		||||
    const date = new Date(time)
 | 
			
		||||
@@ -98,7 +105,7 @@ export const formatTime = (time: Date | number | string, fmt: string) => {
 | 
			
		||||
/**
 | 
			
		||||
 * 生成随机字符串
 | 
			
		||||
 */
 | 
			
		||||
export const toAnyString = () => {
 | 
			
		||||
export function toAnyString() {
 | 
			
		||||
  const str: string = 'xxxxx-xxxxx-4xxxx-yxxxx-xxxxx'.replace(/[xy]/g, (c: string) => {
 | 
			
		||||
    const r: number = (Math.random() * 16) | 0
 | 
			
		||||
    const v: number = c === 'x' ? r : (r & 0x3) | 0x8
 | 
			
		||||
@@ -107,6 +114,13 @@ export const toAnyString = () => {
 | 
			
		||||
  return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 首字母大写
 | 
			
		||||
 */
 | 
			
		||||
export function firstUpperCase(str: string) {
 | 
			
		||||
  return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const generateUUID = () => {
 | 
			
		||||
  if (typeof crypto === 'object') {
 | 
			
		||||
    if (typeof crypto.randomUUID === 'function') {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
                :props="defaultProps2"
 | 
			
		||||
                class="w-1/1"
 | 
			
		||||
                clearable
 | 
			
		||||
                placeholder="请选择商品分类"
 | 
			
		||||
                placeholder="请选择地区"
 | 
			
		||||
                filterable
 | 
			
		||||
                collapse-tags
 | 
			
		||||
              />
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
        <template #label>
 | 
			
		||||
          <Tooltip
 | 
			
		||||
            message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
 | 
			
		||||
            titel="路由地址"
 | 
			
		||||
            title="路由地址"
 | 
			
		||||
          />
 | 
			
		||||
        </template>
 | 
			
		||||
        <el-input v-model="formData.path" clearable placeholder="请输入路由地址" />
 | 
			
		||||
@@ -53,7 +53,7 @@
 | 
			
		||||
        <template #label>
 | 
			
		||||
          <Tooltip
 | 
			
		||||
            message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
 | 
			
		||||
            titel="权限标识"
 | 
			
		||||
            title="权限标识"
 | 
			
		||||
          />
 | 
			
		||||
        </template>
 | 
			
		||||
        <el-input v-model="formData.permission" clearable placeholder="请输入权限标识" />
 | 
			
		||||
@@ -74,7 +74,7 @@
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item v-if="formData.type !== 3" label="显示状态" prop="visible">
 | 
			
		||||
        <template #label>
 | 
			
		||||
          <Tooltip message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问" titel="显示状态" />
 | 
			
		||||
          <Tooltip message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问" title="显示状态" />
 | 
			
		||||
        </template>
 | 
			
		||||
        <el-radio-group v-model="formData.visible">
 | 
			
		||||
          <el-radio key="true" :label="true" border>显示</el-radio>
 | 
			
		||||
@@ -85,7 +85,7 @@
 | 
			
		||||
        <template #label>
 | 
			
		||||
          <Tooltip
 | 
			
		||||
            message="选择不是时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单"
 | 
			
		||||
            titel="总是显示"
 | 
			
		||||
            title="总是显示"
 | 
			
		||||
          />
 | 
			
		||||
        </template>
 | 
			
		||||
        <el-radio-group v-model="formData.alwaysShow">
 | 
			
		||||
@@ -97,7 +97,7 @@
 | 
			
		||||
        <template #label>
 | 
			
		||||
          <Tooltip
 | 
			
		||||
            message="选择缓存时,则会被 `keep-alive` 缓存,必须填写「组件名称」字段"
 | 
			
		||||
            titel="缓存状态"
 | 
			
		||||
            title="缓存状态"
 | 
			
		||||
          />
 | 
			
		||||
        </template>
 | 
			
		||||
        <el-radio-group v-model="formData.keepAlive">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <doc-alert title="功能权限" url="https://doc.iocoder.cn/resource-permission" />
 | 
			
		||||
  <doc-alert title="菜单路由" url="https://doc.iocoder.cn/vue2/route/" />
 | 
			
		||||
  <doc-alert title="菜单路由" url="https://doc.iocoder.cn/vue3/route/" />
 | 
			
		||||
 | 
			
		||||
  <!-- 搜索工作栏 -->
 | 
			
		||||
  <ContentWrap>
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@
 | 
			
		||||
      "@intlify/unplugin-vue-i18n/types",
 | 
			
		||||
      "vite/client",
 | 
			
		||||
      "element-plus/global",
 | 
			
		||||
      "@types/intro.js",
 | 
			
		||||
      "@types/qrcode",
 | 
			
		||||
      "vite-plugin-svg-icons/client"
 | 
			
		||||
    ],
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { defineConfig, toEscapedSelector as e, presetUno } from 'unocss'
 | 
			
		||||
import transformerVariantGroup from '@unocss/transformer-variant-group'
 | 
			
		||||
// import transformerVariantGroup from '@unocss/transformer-variant-group'
 | 
			
		||||
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
  // ...UnoCSS options
 | 
			
		||||
@@ -101,5 +101,8 @@ ${selector}:after {
 | 
			
		||||
    ]
 | 
			
		||||
  ],
 | 
			
		||||
  presets: [presetUno({ dark: 'class', attributify: false })],
 | 
			
		||||
  transformers: [transformerVariantGroup()]
 | 
			
		||||
  // transformers: [transformerVariantGroup()],
 | 
			
		||||
  shortcuts: {
 | 
			
		||||
    'wh-full': 'w-full h-full'
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user