gexinzhineng/gxzn27 2 жил өмнө
parent
commit
b967c4e01e
38 өөрчлөгдсөн 344 нэмэгдсэн , 158 устгасан
  1. 1 1
      .gitignore
  2. 6 5
      build/vite/index.ts
  3. 2 2
      index.html
  4. 33 30
      package.json
  5. 6 1
      src/api/mp/account/index.ts
  6. 1 1
      src/api/mp/message/index.ts
  7. 14 7
      src/api/mp/tag/index.ts
  8. 1 1
      src/components/ContentWrap/src/ContentWrap.vue
  9. 4 1
      src/components/Crontab/src/Crontab.vue
  10. 1 1
      src/components/Descriptions/src/Descriptions.vue
  11. 1 1
      src/components/DictTag/src/DictTag.vue
  12. 1 1
      src/components/Editor/src/Editor.vue
  13. 6 2
      src/components/Form/src/Form.vue
  14. 1 1
      src/components/Pagination/index.vue
  15. 10 2
      src/components/Search/src/Search.vue
  16. 4 2
      src/components/Table/src/Table.vue
  17. 10 0
      src/hooks/web/useCrudSchemas.ts
  18. 3 1
      src/hooks/web/useTable.ts
  19. 2 2
      src/layout/components/Breadcrumb/src/Breadcrumb.vue
  20. 2 2
      src/layout/components/Message/src/Message.vue
  21. 2 1
      src/locales/en.ts
  22. 2 1
      src/locales/zh-CN.ts
  23. 49 8
      src/router/modules/remaining.ts
  24. 3 3
      src/store/modules/dict.ts
  25. 1 2
      src/types/auto-components.d.ts
  26. 2 0
      src/types/auto-imports.d.ts
  27. 2 2
      src/types/descriptions.d.ts
  28. 22 1
      src/utils/dict.ts
  29. 105 49
      src/utils/formatTime.ts
  30. 18 0
      src/utils/index.ts
  31. 5 5
      src/utils/tree.ts
  32. 2 2
      src/views/Profile/components/ProfileUser.vue
  33. 2 2
      src/views/infra/webSocket/index.vue
  34. 2 1
      tsconfig.json
  35. 2 1
      types/components.d.ts
  36. 13 13
      types/global.d.ts
  37. 2 2
      types/router.d.ts
  38. 1 1
      vite.config.ts

+ 1 - 1
.gitignore

@@ -8,4 +8,4 @@ dist-ssr
 pnpm-debug
 
 .idea
-.history
+.history

+ 6 - 5
build/vite/index.ts

@@ -15,7 +15,7 @@ import vueSetupExtend from 'vite-plugin-vue-setup-extend'
 import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
 import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
 
-export function createVitePlugins(VITE_APP_TITLE: string) {
+export function createVitePlugins() {
   const root = process.cwd()
   // 路径查找
   function pathResolve(dir: string) {
@@ -39,11 +39,14 @@ export function createVitePlugins(VITE_APP_TITLE: string) {
       imports: [
         'vue',
         'vue-router',
+        // 可额外添加需要 autoImport 的组件
         {
           '@/hooks/web/useI18n': ['useI18n'],
-          '@/hooks/web/useXTable': ['useXTable'],
           '@/hooks/web/useMessage': ['useMessage'],
+          '@/hooks/web/useXTable': ['useXTable'],
           '@/hooks/web/useVxeCrudSchemas': ['useVxeCrudSchemas'],
+          '@/hooks/web/useTable': ['useTable'],
+          '@/hooks/web/useCrudSchemas': ['useCrudSchemas'],
           '@/utils/formRules': ['required'],
           '@/utils/dict': ['DICT_TYPE']
         }
@@ -92,8 +95,6 @@ export function createVitePlugins(VITE_APP_TITLE: string) {
       ext: '.gz', // 生成的压缩包后缀
       deleteOriginFile: false //压缩后是否删除源文件
     }),
-    ViteEjsPlugin({
-      title: VITE_APP_TITLE
-    })
+    ViteEjsPlugin()
   ]
 }

+ 2 - 2
index.html

@@ -13,7 +13,7 @@
       name="description"
       content="芋道管理系统 基于 vue3 + CompositionAPI + typescript + vite3 + element plus 的后台开源免费管理系统!"
     />
-    <title><%= title %></title>
+    <title>%VITE_APP_TITLE%</title>
   </head>
   <body>
     <div id="app">
@@ -137,7 +137,7 @@
         <div class="app-loading-wrap">
           <div class="app-loading-title">
             <img src="/logo.gif" class="app-loading-logo" alt="Logo" />
-            <div class="app-loading-title"><%= title %></div>
+            <div class="app-loading-title">%VITE_APP_TITLE%</div>
           </div>
           <div class="app-loading-item">
             <div class="app-loading-outter"></div>

+ 33 - 30
package.json

@@ -1,6 +1,6 @@
 {
   "name": "yudao-ui-admin-vue3",
-  "version": "1.7.1-snapshot.1941",
+  "version": "1.7.1-snapshot.1961",
   "description": "基于vue3、vite4、element-plus、typesScript",
   "author": "xingyu",
   "private": false,
@@ -29,12 +29,14 @@
     "@form-create/designer": "^3.1.0",
     "@form-create/element-ui": "^3.1.17",
     "@iconify/iconify": "^3.1.0",
+    "@videojs-player/vue": "^1.0.0",
     "@vueuse/core": "^9.13.0",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.10",
     "@zxcvbn-ts/core": "^2.2.1",
     "animate.css": "^4.1.1",
     "axios": "^1.3.4",
+    "benz-amr-recorder": "^1.1.5",
     "bpmn-js-token-simulation": "^0.10.0",
     "camunda-bpmn-moddle": "^7.0.1",
     "cropperjs": "^1.5.13",
@@ -43,7 +45,7 @@
     "diagram-js": "^11.6.0",
     "echarts": "^5.4.1",
     "echarts-wordcloud": "^2.1.0",
-    "element-plus": "2.2.34",
+    "element-plus": "2.3.1",
     "fast-xml-parser": "^4.1.3",
     "highlight.js": "^11.7.0",
     "intro.js": "^6.0.0",
@@ -57,62 +59,63 @@
     "qs": "^6.11.1",
     "steady-xml": "^0.1.0",
     "url": "^0.11.0",
+    "video.js": "^8.0.4",
     "vue": "3.2.47",
     "vue-i18n": "9.2.2",
     "vue-router": "^4.1.6",
     "vue-types": "^5.0.2",
     "vuedraggable": "^4.1.0",
-    "vxe-table": "^4.3.10",
+    "vxe-table": "^4.3.11",
     "web-storage-cache": "^1.1.1",
     "xe-utils": "^3.5.7",
     "xml-js": "^1.6.11"
   },
   "devDependencies": {
-    "@commitlint/cli": "^17.4.4",
+    "@commitlint/cli": "^17.5.0",
     "@commitlint/config-conventional": "^17.4.4",
-    "@iconify/json": "^2.2.31",
-    "@intlify/unplugin-vue-i18n": "^0.8.2",
+    "@iconify/json": "^2.2.38",
+    "@intlify/unplugin-vue-i18n": "^0.10.0",
     "@purge-icons/generated": "^0.9.0",
     "@types/intro.js": "^5.1.1",
-    "@types/lodash-es": "^4.17.6",
-    "@types/node": "^18.14.6",
+    "@types/lodash-es": "^4.17.7",
+    "@types/node": "^18.15.5",
     "@types/nprogress": "^0.2.0",
     "@types/qrcode": "^1.5.0",
     "@types/qs": "^6.9.7",
-    "@typescript-eslint/eslint-plugin": "^5.54.1",
-    "@typescript-eslint/parser": "^5.54.1",
-    "@vitejs/plugin-legacy": "^4.0.1",
-    "@vitejs/plugin-vue": "^4.0.0",
-    "@vitejs/plugin-vue-jsx": "^3.0.0",
-    "autoprefixer": "^10.4.13",
+    "@typescript-eslint/eslint-plugin": "^5.56.0",
+    "@typescript-eslint/parser": "^5.56.0",
+    "@vitejs/plugin-legacy": "^4.0.2",
+    "@vitejs/plugin-vue": "^4.1.0",
+    "@vitejs/plugin-vue-jsx": "^3.0.1",
+    "autoprefixer": "^10.4.14",
     "bpmn-js": "^8.9.0",
     "bpmn-js-properties-panel": "^0.46.0",
     "consola": "^2.15.3",
-    "eslint": "^8.35.0",
-    "eslint-config-prettier": "^8.7.0",
-    "eslint-define-config": "^1.15.0",
+    "eslint": "^8.36.0",
+    "eslint-config-prettier": "^8.8.0",
+    "eslint-define-config": "^1.17.0",
     "eslint-plugin-prettier": "^4.2.1",
     "eslint-plugin-vue": "^9.9.0",
-    "lint-staged": "^13.1.2",
+    "lint-staged": "^13.2.0",
     "postcss": "^8.4.21",
     "postcss-html": "^1.5.0",
     "postcss-scss": "^4.0.6",
-    "prettier": "^2.8.4",
-    "rimraf": "^4.3.1",
-    "rollup": "^3.18.0",
-    "sass": "^1.58.3",
-    "stylelint": "^15.2.0",
+    "prettier": "^2.8.6",
+    "rimraf": "^4.4.1",
+    "rollup": "^3.20.0",
+    "sass": "^1.59.3",
+    "stylelint": "^15.3.0",
     "stylelint-config-html": "^1.1.0",
     "stylelint-config-prettier": "^9.0.5",
-    "stylelint-config-recommended": "^10.0.1",
-    "stylelint-config-standard": "^30.0.1",
-    "stylelint-order": "^6.0.2",
-    "terser": "^5.16.5",
-    "typescript": "4.9.5",
+    "stylelint-config-recommended": "^11.0.0",
+    "stylelint-config-standard": "^31.0.0",
+    "stylelint-order": "^6.0.3",
+    "terser": "^5.16.6",
+    "typescript": "5.0.2",
     "unplugin-auto-import": "^0.15.1",
     "unplugin-element-plus": "^0.7.0",
     "unplugin-vue-components": "^0.24.1",
-    "vite": "4.1.4",
+    "vite": "4.2.1",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-ejs": "^1.6.4",
     "vite-plugin-eslint": "^1.8.1",
@@ -125,7 +128,7 @@
     "windicss": "^3.5.6"
   },
   "engines": {
-    "node": ">=16.0.0"
+    "node": ">=16.18.0"
   },
   "license": "MIT",
   "repository": {

+ 6 - 1
src/api/mp/account/index.ts

@@ -1,5 +1,10 @@
 import request from '@/config/axios'
 
+export interface AccountVO {
+  id?: number
+  name: string
+}
+
 // 创建公众号账号
 export const createAccount = async (data) => {
   return await request.post({ url: '/mp/account/create', data })
@@ -26,7 +31,7 @@ export const getAccountPage = async (query) => {
 }
 
 // 获取公众号账号精简信息列表
-export const getSimpleAccounts = async () => {
+export const getSimpleAccountList = async () => {
   return request.get({ url: '/mp/account/list-all-simple' })
 }
 

+ 1 - 1
src/api/mp/message/index.ts

@@ -1,7 +1,7 @@
 import request from '@/config/axios'
 
 // 获得公众号消息分页
-export const getMessagePage = (query) => {
+export const getMessagePage = (query: PageParam) => {
   return request.get({
     url: '/mp/message/page',
     params: query

+ 14 - 7
src/api/mp/tag/index.ts

@@ -1,7 +1,14 @@
 import request from '@/config/axios'
 
+export interface TagVO {
+  id?: number
+  name: string
+  accountId: number
+  createTime: Date
+}
+
 // 创建公众号标签
-export const createTag = (data) => {
+export const createTag = (data: TagVO) => {
   return request.post({
     url: '/mp/tag/create',
     data: data
@@ -9,7 +16,7 @@ export const createTag = (data) => {
 }
 
 // 更新公众号标签
-export const updateTag = (data) => {
+export const updateTag = (data: TagVO) => {
   return request.put({
     url: '/mp/tag/update',
     data: data
@@ -17,21 +24,21 @@ export const updateTag = (data) => {
 }
 
 // 删除公众号标签
-export const deleteTag = (id) => {
+export const deleteTag = (id: number) => {
   return request.delete({
     url: '/mp/tag/delete?id=' + id
   })
 }
 
 // 获得公众号标签
-export const getTag = (id) => {
+export const getTag = (id: number) => {
   return request.get({
     url: '/mp/tag/get?id=' + id
   })
 }
 
 // 获得公众号标签分页
-export const getTagPage = (query) => {
+export const getTagPage = (query: PageParam) => {
   return request.get({
     url: '/mp/tag/page',
     params: query
@@ -39,14 +46,14 @@ export const getTagPage = (query) => {
 }
 
 // 获取公众号标签精简信息列表
-export const getSimpleTags = () => {
+export const getSimpleTagList = () => {
   return request.get({
     url: '/mp/tag/list-all-simple'
   })
 }
 
 // 同步公众号标签
-export const syncTag = (accountId) => {
+export const syncTag = (accountId: number) => {
   return request.post({
     url: '/mp/tag/sync?accountId=' + accountId
   })

+ 1 - 1
src/components/ContentWrap/src/ContentWrap.vue

@@ -13,7 +13,7 @@ defineProps({
 </script>
 
 <template>
-  <ElCard :class="[prefixCls, 'mb-20px']" shadow="never">
+  <ElCard :class="[prefixCls, 'mb-15px']" shadow="never">
     <template v-if="title" #header>
       <div class="flex items-center">
         <span class="text-16px font-700">{{ title }}</span>

+ 4 - 1
src/components/Crontab/src/Crontab.vue

@@ -6,7 +6,10 @@ interface shortcutsType {
   value: string
 }
 const props = defineProps({
-  modelValue: { type: String, default: '* * * * * ?' },
+  modelValue: {
+    type: String,
+    default: '* * * * * ?'
+  },
   shortcuts: { type: Array as PropType<shortcutsType[]>, default: () => [] }
 })
 const defaultValue = ref('')

+ 1 - 1
src/components/Descriptions/src/Descriptions.vue

@@ -76,7 +76,7 @@ const toggleClick = () => {
       v-if="title"
       :class="[
         `${prefixCls}-header`,
-        'h-50px flex justify-between items-center mb-10px border-bottom-1 border-solid border-[var(--tags-view-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
+        'h-50px flex justify-between items-center border-bottom-1 border-solid border-[var(--tags-view-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
       ]"
       @click="toggleClick"
     >

+ 1 - 1
src/components/DictTag/src/DictTag.vue

@@ -34,7 +34,7 @@ export default defineComponent({
         return null
       }
       // 解决自定义字典标签值为零时标签不渲染的问题
-      if (props.value === undefined) {
+      if (props.value === undefined || props.value === null) {
         return null
       }
       getDictObj(props.type, props.value.toString())

+ 1 - 1
src/components/Editor/src/Editor.vue

@@ -178,7 +178,7 @@ defineExpose({
 </script>
 
 <template>
-  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-3000">
+  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-99">
     <!-- 工具栏 -->
     <Toolbar
       :editor="editorRef"

+ 6 - 2
src/components/Form/src/Form.vue

@@ -35,7 +35,8 @@ export default defineComponent({
       default: () => []
     },
     // 是否需要栅格布局
-    isCol: propTypes.bool.def(true),
+    // update by 芋艿:将 true 改成 false,因为项目更常用这种方式
+    isCol: propTypes.bool.def(false),
     // 表单数据对象
     model: {
       type: Object as PropType<Recordable>,
@@ -46,7 +47,9 @@ export default defineComponent({
     // 是否自定义内容
     isCustom: propTypes.bool.def(false),
     // 表单label宽度
-    labelWidth: propTypes.oneOfType([String, Number]).def('auto')
+    labelWidth: propTypes.oneOfType([String, Number]).def('auto'),
+    // 是否 loading 数据中 add by 芋艿
+    vLoading: propTypes.bool.def(false)
   },
   emits: ['register'],
   setup(props, { slots, expose, emit }) {
@@ -280,6 +283,7 @@ export default defineComponent({
         {...getFormBindValue()}
         model={props.isCustom ? props.model : formModel}
         class={prefixCls}
+        v-loading={props.vLoading}
       >
         {{
           // 如果需要自定义,就什么都不渲染,而是提供默认插槽

+ 1 - 1
src/components/Pagination/index.vue

@@ -5,7 +5,7 @@
     class="float-right mt-15px mb-15px"
     :background="true"
     layout="total, sizes, prev, pager, next, jumper"
-    :page-sizes="[10, 20, 30, 50]"
+    :page-sizes="[10, 20, 30, 50, 100]"
     v-model:current-page="currentPage"
     v-model:page-size="pageSize"
     :pager-count="pagerCount"

+ 10 - 2
src/components/Search/src/Search.vue

@@ -98,6 +98,7 @@ const setVisible = () => {
 </script>
 
 <template>
+  <!-- update by 芋艿:class="-mb-15px" 用于降低和 ContentWrap 组件的底部距离,避免空隙过大 -->
   <Form
     :is-custom="false"
     :label-width="labelWidth"
@@ -106,21 +107,26 @@ const setVisible = () => {
     :is-col="isCol"
     :schema="newSchema"
     @register="register"
+    class="-mb-15px"
   >
     <template #action>
       <div v-if="layout === 'inline'">
-        <ElButton v-if="showSearch" type="primary" @click="search">
+        <!-- update by 芋艿:去除搜索的 type="primary",颜色变淡一点 -->
+        <ElButton v-if="showSearch" @click="search">
           <Icon icon="ep:search" class="mr-5px" />
           {{ t('common.query') }}
         </ElButton>
+        <!-- update by 芋艿:将 icon="ep:refresh-right" 修改成 icon="ep:refresh",和 ruoyi-vue 搜索保持一致  -->
         <ElButton v-if="showReset" @click="reset">
-          <Icon icon="ep:refresh-right" class="mr-5px" />
+          <Icon icon="ep:refresh" class="mr-5px" />
           {{ t('common.reset') }}
         </ElButton>
         <ElButton v-if="expand" text @click="setVisible">
           {{ t(visible ? 'common.shrink' : 'common.expand') }}
           <Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" />
         </ElButton>
+        <!-- add by 芋艿:补充在搜索后的按钮 -->
+        <slot name="actionMore"></slot>
       </div>
     </template>
     <template #[name] v-for="name in Object.keys($slots)" :key="name"
@@ -142,6 +148,8 @@ const setVisible = () => {
         {{ t(visible ? 'common.shrink' : 'common.expand') }}
         <Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" />
       </ElButton>
+      <!-- add by 芋艿:补充在搜索后的按钮 -->
+      <slot name="actionMore"></slot>
     </div>
   </template>
 </template>

+ 4 - 2
src/components/Table/src/Table.vue

@@ -104,11 +104,12 @@ export default defineComponent({
     })
 
     const pagination = computed(() => {
+      // update by 芋艿:保持和 Pagination 组件的逻辑一致
       return Object.assign(
         {
           small: false,
           background: true,
-          pagerCount: 5,
+          pagerCount: document.body.clientWidth < 992 ? 5 : 7,
           layout: 'total, sizes, prev, pager, next, jumper',
           pageSizes: [10, 20, 30, 50, 100],
           disabled: false,
@@ -283,10 +284,11 @@ export default defineComponent({
           }}
         </ElTable>
         {unref(getProps).pagination ? (
+          // update by 芋艿:保持和 Pagination 组件一致
           <ElPagination
             v-model:pageSize={pageSizeRef.value}
             v-model:currentPage={currentPageRef.value}
-            class="mt-10px"
+            class="float-right mt-15px mb-15px"
             {...unref(pagination)}
           ></ElPagination>
         ) : undefined}

+ 10 - 0
src/hooks/web/useCrudSchemas.ts

@@ -8,6 +8,7 @@ import { FormSchema } from '@/types/form'
 import { TableColumn } from '@/types/table'
 import { DescriptionsSchema } from '@/types/descriptions'
 import { ComponentOptions, ComponentProps } from '@/types/components'
+import { DictTag } from '@/components/DictTag'
 
 export type CrudSchema = Omit<TableColumn, 'children'> & {
   isSearch?: boolean // 是否在查询显示
@@ -151,6 +152,15 @@ const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => {
   const tableColumns = treeMap<CrudSchema>(crudSchema, {
     conversion: (schema: CrudSchema) => {
       if (schema?.isTable !== false && schema?.table?.show !== false) {
+        // add by 芋艿:增加对 dict 字典数据的支持
+        if (!schema.formatter && schema.dictType) {
+          schema.formatter = (_: Recordable, __: TableColumn, cellValue: any) => {
+            return h(DictTag, {
+              type: schema.dictType!, // ! 表示一定不为空
+              value: cellValue
+            })
+          }
+        }
         return {
           ...schema.table,
           ...schema

+ 3 - 1
src/hooks/web/useTable.ts

@@ -218,6 +218,8 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
     register,
     elTableRef,
     tableObject,
-    methods
+    methods,
+    // add by 芋艿:返回 tableMethods 属性,和 tableObject 更统一
+    tableMethods: methods
   }
 }

+ 2 - 2
src/layout/components/Breadcrumb/src/Breadcrumb.vue

@@ -37,7 +37,7 @@ export default defineComponent({
     })
 
     const getBreadcrumb = () => {
-      const currentPath = currentRoute.value.path
+      const currentPath = currentRoute.value.matched.slice(-1)[0].path
 
       levelList.value = filter<AppRouteRecordRaw>(unref(menuRouters), (node: AppRouteRecordRaw) => {
         return node.path === currentPath
@@ -47,7 +47,7 @@ export default defineComponent({
     const renderBreadcrumb = () => {
       const breadcrumbList = treeToList<AppRouteRecordRaw[]>(unref(levelList))
       return breadcrumbList.map((v) => {
-        const disabled = v.redirect === 'noredirect'
+        const disabled = !v.redirect || v.redirect === 'noredirect'
         const meta = v.meta as RouteMeta
         return (
           <ElBreadcrumbItem to={{ path: disabled ? '' : v.path }} key={v.name}>

+ 2 - 2
src/layout/components/Message/src/Message.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import dayjs from 'dayjs'
+import { parseTime } from '@/utils/formatTime'
 import * as NotifyMessageApi from '@/api/system/notify/message'
 
 const { push } = useRouter()
@@ -57,7 +57,7 @@ onMounted(() => {
                     {{ item.templateNickname }}:{{ item.templateContent }}
                   </span>
                   <span class="message-date">
-                    {{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
+                    {{ parseTime(item.createTime) }}
                   </span>
                 </div>
               </div>

+ 2 - 1
src/locales/en.ts

@@ -297,7 +297,8 @@ export default {
     typeCreate: 'Dict Type Create',
     typeUpdate: 'Dict Type Eidt',
     dataCreate: 'Dict Data Create',
-    dataUpdate: 'Dict Data Eidt'
+    dataUpdate: 'Dict Data Eidt',
+    fileUpload: 'File Upload'
   },
   dialog: {
     dialog: 'Dialog',

+ 2 - 1
src/locales/zh-CN.ts

@@ -297,7 +297,8 @@ export default {
     typeCreate: '字典类型新增',
     typeUpdate: '字典类型编辑',
     dataCreate: '字典数据新增',
-    dataUpdate: '字典数据编辑'
+    dataUpdate: '字典数据编辑',
+    fileUpload: '上传文件'
   },
   dialog: {
     dialog: '弹窗',

+ 49 - 8
src/router/modules/remaining.ts

@@ -104,6 +104,31 @@ const remainingRouter: AppRouteRecordRaw[] = [
       }
     ]
   },
+
+  {
+    path: '/dict',
+    component: Layout,
+    name: 'dict',
+    meta: {
+      hidden: true
+    },
+    children: [
+      {
+        path: 'type/data/:dictType',
+        component: () => import('@/views/system/dict/data.vue'),
+        name: 'data',
+        meta: {
+          title: '字典数据',
+          noCache: true,
+          hidden: true,
+          canTo: true,
+          icon: '',
+          activeMenu: 'system/dict/data'
+        }
+      }
+    ]
+  },
+
   {
     path: '/codegen',
     component: Layout,
@@ -137,7 +162,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
     children: [
       {
         path: 'job-log',
-        component: () => import('@/views/infra/job/JobLog.vue'),
+        component: () => import('@/views/infra/job/logger/index.vue'),
         name: 'JobLog',
         meta: {
           noCache: true,
@@ -200,26 +225,26 @@ const remainingRouter: AppRouteRecordRaw[] = [
     children: [
       {
         path: '/manager/form/edit',
-        component: () => import('@/views/bpm/form/formEditor.vue'),
+        component: () => import('@/views/bpm/form/editor/index.vue'),
         name: 'bpmFormEditor',
         meta: {
           noCache: true,
           hidden: true,
           canTo: true,
           title: '设计流程表单',
-          activeMenu: 'bpm/manager/form/formEditor'
+          activeMenu: '/bpm/manager/form'
         }
       },
       {
         path: '/manager/model/edit',
-        component: () => import('@/views/bpm/model/modelEditor.vue'),
+        component: () => import('@/views/bpm/model/editor/index.vue'),
         name: 'modelEditor',
         meta: {
           noCache: true,
           hidden: true,
           canTo: true,
           title: '设计流程',
-          activeMenu: 'bpm/manager/model/design'
+          activeMenu: '/bpm/manager/model'
         }
       },
       {
@@ -231,7 +256,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
           hidden: true,
           canTo: true,
           title: '流程定义',
-          activeMenu: 'bpm/definition/index'
+          activeMenu: '/bpm/manager/model'
         }
       },
       {
@@ -247,7 +272,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
       },
       {
         path: '/process-instance/create',
-        component: () => import('@/views/bpm/processInstance/create.vue'),
+        component: () => import('@/views/bpm/processInstance/create/index.vue'),
         name: 'BpmProcessInstanceCreate',
         meta: {
           noCache: true,
@@ -259,7 +284,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
       },
       {
         path: '/process-instance/detail',
-        component: () => import('@/views/bpm/processInstance/detail.vue'),
+        component: () => import('@/views/bpm/processInstance/detail/index.vue'),
         name: 'BpmProcessInstanceDetail',
         meta: {
           noCache: true,
@@ -294,6 +319,22 @@ const remainingRouter: AppRouteRecordRaw[] = [
         }
       }
     ]
+  },
+  {
+    path: '/property',
+    component: Layout,
+    name: 'property',
+    meta: {
+      hidden: true
+    },
+    children: [
+      {
+        path: 'value/:propertyId(\\d+)',
+        component: () => import('@/views/mall/product/property/value/index.vue'),
+        name: 'PropertyValue',
+        meta: { title: '商品属性值', icon: '', activeMenu: '/product/property' }
+      }
+    ]
   }
 ]
 

+ 3 - 3
src/store/modules/dict.ts

@@ -3,7 +3,7 @@ import { store } from '../index'
 import { DictDataVO } from '@/api/system/dict/types'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 const { wsCache } = useCache('sessionStorage')
-import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
+import { listSimpleDictData } from '@/api/system/dict/dict.data'
 
 export interface DictValueType {
   value: any
@@ -44,7 +44,7 @@ export const useDictStore = defineStore('dict', {
         this.dictMap = dictMap
         this.isSetDict = true
       } else {
-        const res = await listSimpleDictDataApi()
+        const res = await listSimpleDictData()
         // 设置数据
         const dictDataMap = new Map<string, any>()
         res.forEach((dictData: DictDataVO) => {
@@ -74,7 +74,7 @@ export const useDictStore = defineStore('dict', {
     },
     async resetDict() {
       wsCache.delete(CACHE_KEY.DICT_CACHE)
-      const res = await listSimpleDictDataApi()
+      const res = await listSimpleDictData()
       // 设置数据
       const dictDataMap = new Map<string, any>()
       res.forEach((dictData: DictDataVO) => {

+ 1 - 2
src/types/auto-components.d.ts

@@ -24,7 +24,6 @@ declare module '@vue/runtime-core' {
     Echart: typeof import('./../components/Echart/src/Echart.vue')['default']
     Editor: typeof import('./../components/Editor/src/Editor.vue')['default']
     ElAutoResizer: typeof import('element-plus/es')['ElAutoResizer']
-    ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
@@ -69,10 +68,10 @@ declare module '@vue/runtime-core' {
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
-    ElSpace: typeof import('element-plus/es')['ElSpace']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTableV2: typeof import('element-plus/es')['ElTableV2']
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']

+ 2 - 0
src/types/auto-imports.d.ts

@@ -52,6 +52,7 @@ declare global {
   const triggerRef: typeof import('vue')['triggerRef']
   const unref: typeof import('vue')['unref']
   const useAttrs: typeof import('vue')['useAttrs']
+  const useCrudSchemas: typeof import('@/hooks/web/useCrudSchemas')['useCrudSchemas']
   const useCssModule: typeof import('vue')['useCssModule']
   const useCssVars: typeof import('vue')['useCssVars']
   const useI18n: typeof import('@/hooks/web/useI18n')['useI18n']
@@ -60,6 +61,7 @@ declare global {
   const useRoute: typeof import('vue-router')['useRoute']
   const useRouter: typeof import('vue-router')['useRouter']
   const useSlots: typeof import('vue')['useSlots']
+  const useTable: typeof import('@/hooks/web/useTable')['useTable']
   const useVxeCrudSchemas: typeof import('@/hooks/web/useVxeCrudSchemas')['useVxeCrudSchemas']
   const useXTable: typeof import('@/hooks/web/useXTable')['useXTable']
   const watch: typeof import('vue')['watch']

+ 2 - 2
src/types/descriptions.d.ts

@@ -8,6 +8,6 @@ export interface DescriptionsSchema {
   labelAlign?: 'left' | 'center' | 'right'
   className?: string
   labelClassName?: string
-  dateFormat?: string
-  dictType?: string
+  dateFormat?: string // add by 星语:支持时间的格式化
+  dictType?: string // add by 星语:支持 dict 字典数据
 }

+ 22 - 1
src/utils/dict.ts

@@ -70,6 +70,23 @@ export const getDictObj = (dictType: string, value: any) => {
   })
 }
 
+/**
+ * 获得字典数据的文本展示
+ *
+ * @param dictType 字典类型
+ * @param value 字典数据的值
+ */
+export const getDictLabel = (dictType: string, value: any) => {
+  const dictOptions: DictDataType[] = getDictOptions(dictType)
+  const dictLabel = ref('')
+  dictOptions.forEach((dict: DictDataType) => {
+    if (dict.value === value) {
+      dictLabel.value = dict.label
+    }
+  })
+  return dictLabel.value
+}
+
 export enum DICT_TYPE {
   USER_TYPE = 'user_type',
   COMMON_STATUS = 'common_status',
@@ -123,5 +140,9 @@ export enum DICT_TYPE {
   PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
   PAY_ORDER_REFUND_STATUS = 'pay_order_refund_status', // 商户支付订单退款状态
   PAY_REFUND_ORDER_STATUS = 'pay_refund_order_status', // 退款订单状态
-  PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type' // 退款订单类别
+  PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type', // 退款订单类别
+
+  // ========== MP 模块 ==========
+  MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型
+  MP_MESSAGE_TYPE = 'mp_message_type' // 消息类型
 }

+ 105 - 49
src/utils/formatTime.ts

@@ -11,58 +11,63 @@ import dayjs from 'dayjs'
  * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
  * @returns 返回拼接后的时间字符串
  */
-export function formatDate(date: Date, format: string): string {
-  const we = date.getDay() // 星期
-  const z = getWeek(date) // 周
-  const qut = Math.floor((date.getMonth() + 3) / 3).toString() // 季度
-  const opt: { [key: string]: string } = {
-    'Y+': date.getFullYear().toString(), // 年
-    'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
-    'd+': date.getDate().toString(), // 日
-    'H+': date.getHours().toString(), // 时
-    'M+': date.getMinutes().toString(), // 分
-    'S+': date.getSeconds().toString(), // 秒
-    'q+': qut // 季度
+export function formatDate(date: Date, format?: string): string {
+  // 日期不存在,则返回空
+  if (!date) {
+    return ''
   }
-  // 中文数字 (星期)
-  const week: { [key: string]: string } = {
-    '0': '日',
-    '1': '一',
-    '2': '二',
-    '3': '三',
-    '4': '四',
-    '5': '五',
-    '6': '六'
+  // 日期存在,则进行格式化
+  if (format === undefined) {
+    format = 'YYYY-MM-DD HH:mm:ss'
   }
-  // 中文数字(季度)
-  const quarter: { [key: string]: string } = {
-    '1': '一',
-    '2': '二',
-    '3': '三',
-    '4': '四'
+  return dayjs(date).format(format)
+}
+
+// TODO 芋艿:稍后去掉
+// 日期格式化
+export function parseTime(time: any, pattern?: string) {
+  if (arguments.length === 0 || !time) {
+    return null
+  }
+  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
+      time = parseInt(time)
+    } else if (typeof time === 'string') {
+      time = time
+        .replace(new RegExp(/-/gm), '/')
+        .replace('T', ' ')
+        .replace(new RegExp(/\.\d{3}/gm), '')
+    }
+    if (typeof time === 'number' && time.toString().length === 10) {
+      time = time * 1000
+    }
+    date = new Date(time)
   }
-  if (/(W+)/.test(format))
-    format = format.replace(
-      RegExp.$1,
-      RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]
-    )
-  if (/(Q+)/.test(format))
-    format = format.replace(
-      RegExp.$1,
-      RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]
-    )
-  if (/(Z+)/.test(format))
-    format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + '')
-  for (const k in opt) {
-    const r = new RegExp('(' + k + ')').exec(format)
-    // 若输入的长度不为1,则前面补零
-    if (r)
-      format = format.replace(
-        r[1],
-        RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0')
-      )
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
   }
-  return format
+  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
+    let value = formatObj[key]
+    // Note: getDay() returns 0 on Sunday
+    if (key === 'a') {
+      return ['日', '一', '二', '三', '四', '五', '六'][value]
+    }
+    if (result.length > 0 && value < 10) {
+      value = '0' + value
+    }
+    return value || 0
+  })
+  return time_str
 }
 
 /**
@@ -189,5 +194,56 @@ export const dateFormatter = (row, column, cellValue) => {
   if (!cellValue) {
     return
   }
-  return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
+  return formatDate(cellValue)
+}
+
+/**
+ * 设置起始日期,时间为00:00:00
+ * @param param 传入日期
+ * @returns 带时间00:00:00的日期
+ */
+export function beginOfDay(param: Date) {
+  return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 0, 0, 0, 0)
+}
+
+/**
+ * 设置结束日期,时间为23:59:59
+ * @param param 传入日期
+ * @returns 带时间23:59:59的日期
+ */
+export function endOfDay(param: Date) {
+  return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 23, 59, 59, 999)
+}
+
+/**
+ * 计算两个日期间隔天数
+ * @param param1 日期1
+ * @param param2 日期2
+ */
+export function betweenDay(param1: Date, param2: Date) {
+  param1 = convertDate(param1)
+  param2 = convertDate(param2)
+  // 计算差值
+  return Math.floor((param2.getTime() - param1.getTime()) / (24 * 3600 * 1000))
+}
+
+/**
+ * 日期计算
+ * @param param1 日期
+ * @param param2 添加的时间
+ */
+export function addTime(param1: Date, param2: number) {
+  param1 = convertDate(param1)
+  return new Date(param1.getTime() + param2)
+}
+
+/**
+ * 日期转换
+ * @param param 日期
+ */
+export function convertDate(param: Date | string) {
+  if (typeof param === 'string') {
+    return new Date(param)
+  }
+  return param
 }

+ 18 - 0
src/utils/index.ts

@@ -137,3 +137,21 @@ export const generateUUID = () => {
     return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16)
   })
 }
+
+/**
+ * element plus 的文件大小 Formatter 实现
+ *
+ * @param row 行数据
+ * @param column 字段
+ * @param cellValue 字段值
+ */
+// @ts-ignore
+export const fileSizeFormatter = (row, column, cellValue) => {
+  const fileSize = cellValue
+  const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+  const srcSize = parseFloat(fileSize)
+  const index = Math.floor(Math.log(srcSize) / Math.log(1024))
+  const size = srcSize / Math.pow(1024, index)
+  const sizeStr = size.toFixed(2) //保留的小数位数
+  return sizeStr + ' ' + unitArr[index]
+}

+ 5 - 5
src/utils/tree.ts

@@ -276,7 +276,7 @@ export const handleTree = (data: any[], id?: string, parentId?: string, children
 export const handleTree2 = (data, id, parentId, children, rootId) => {
   id = id || 'id'
   parentId = parentId || 'parentId'
-  children = children || 'children'
+  // children = children || 'children'
   rootId =
     rootId ||
     Math.min(
@@ -285,16 +285,16 @@ export const handleTree2 = (data, id, parentId, children, rootId) => {
       })
     ) ||
     0
-  //对源数据深度克隆
+  // 对源数据深度克隆
   const cloneData = JSON.parse(JSON.stringify(data))
-  //循环所有项
+  // 循环所有项
   const treeData = cloneData.filter((father) => {
     const branchArr = cloneData.filter((child) => {
-      //返回每一项的子级数组
+      // 返回每一项的子级数组
       return father[id] === child[parentId]
     })
     branchArr.length > 0 ? (father.children = branchArr) : ''
-    //返回第一层
+    // 返回第一层
     return father[parentId] === rootId
   })
   return treeData !== '' ? treeData : data

+ 2 - 2
src/views/Profile/components/ProfileUser.vue

@@ -34,13 +34,13 @@
       </li>
       <li class="list-group-item">
         <Icon icon="ep:calendar" class="mr-5px" />{{ t('profile.user.createTime') }}
-        <div class="pull-right">{{ dayjs(userInfo?.createTime).format('YYYY-MM-DD') }}</div>
+        <div class="pull-right">{{ parseTime(userInfo?.createTime) }}</div>
       </li>
     </ul>
   </div>
 </template>
 <script setup lang="ts">
-import dayjs from 'dayjs'
+import { parseTime } from '@/utils/formatTime'
 import UserAvatar from './UserAvatar.vue'
 
 import { getUserProfileApi, ProfileVO } from '@/api/system/user/profile'

+ 2 - 2
src/views/infra/webSocket/index.vue

@@ -44,7 +44,7 @@
           <li v-for="item in getList" class="mt-2" :key="item.time">
             <div class="flex items-center">
               <span class="mr-2 text-primary font-medium">收到消息:</span>
-              <span>{{ dayjs(item.time).format('YYYY-MM-DD HH:mm:ss') }}</span>
+              <span>{{ parseTime(item.time) }}</span>
             </div>
             <div>
               {{ item.res }}
@@ -56,7 +56,7 @@
   </div>
 </template>
 <script setup lang="ts">
-import dayjs from 'dayjs'
+import { parseTime } from '@/utils/formatTime'
 import { useUserStore } from '@/store/modules/user'
 import { useWebSocket } from '@vueuse/core'
 

+ 2 - 1
tsconfig.json

@@ -32,6 +32,7 @@
       "vite-plugin-svg-icons/client",
       "@form-create/element-ui/types"
     ],
+    "outDir": "target", // 请保留这个属性,防止tsconfig.json文件报错
     "typeRoots": ["./node_modules/@types/", "./types"]
   },
   "include": [
@@ -40,5 +41,5 @@
     "src/types/auto-imports.d.ts",
     "src/types/auto-components.d.ts"
   ],
-  "exclude": ["dist", "node_modules"]
+  "exclude": ["dist", "target", "node_modules"]
 }

+ 2 - 1
types/components.d.ts

@@ -1,6 +1,7 @@
 declare module 'vue' {
   export interface GlobalComponents {
-    Icon: typeof import('../components/Icon/src/Icon.vue')['default']
+    Icon: typeof import('@/components/Icon')['Icon']
+    DictTag: typeof import('@/components/DictTag')['DictTag']
   }
 }
 

+ 13 - 13
types/global.d.ts

@@ -1,29 +1,29 @@
 export {}
 declare global {
-  declare interface Fn<T = any> {
+  interface Fn<T = any> {
     (...arg: T[]): T
   }
 
-  declare type Nullable<T> = T | null
+  type Nullable<T> = T | null
 
-  declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>
+  type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>
 
-  declare type Recordable<T = any, K = string> = Record<K extends null | undefined ? string : K, T>
+  type Recordable<T = any, K = string> = Record<K extends null | undefined ? string : K, T>
 
-  declare type ComponentRef<T> = InstanceType<T>
+  type ComponentRef<T> = InstanceType<T>
 
-  declare type LocaleType = 'zh-CN' | 'en'
+  type LocaleType = 'zh-CN' | 'en'
 
-  declare type AxiosHeaders =
+  type AxiosHeaders =
     | 'application/json'
     | 'application/x-www-form-urlencoded'
     | 'multipart/form-data'
 
-  declare type AxiosMethod = 'get' | 'post' | 'delete' | 'put' | 'GET' | 'POST' | 'DELETE' | 'PUT'
+  type AxiosMethod = 'get' | 'post' | 'delete' | 'put' | 'GET' | 'POST' | 'DELETE' | 'PUT'
 
-  declare type AxiosResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
+  type AxiosResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
 
-  declare interface AxiosConfig {
+  interface AxiosConfig {
     params?: any
     data?: any
     url?: string
@@ -32,17 +32,17 @@ declare global {
     responseType?: AxiosResponseType
   }
 
-  declare interface IResponse<T = any> {
+  interface IResponse<T = any> {
     code: string
     data: T extends any ? T : T & any
   }
 
-  declare interface PageParam {
+  interface PageParam {
     pageSize?: number
     pageNo?: number
   }
 
-  declare interface Tree {
+  interface Tree {
     id: number
     name: string
     children?: Tree[] | any[]

+ 2 - 2
types/router.d.ts

@@ -54,7 +54,7 @@ type Component<T = any> =
   | (() => Promise<T>)
 
 declare global {
-  declare interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
+  interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
     name: string
     meta: RouteMeta
     component?: Component | string
@@ -64,7 +64,7 @@ declare global {
     keepAlive?: boolean
   }
 
-  declare interface AppCustomRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
+  interface AppCustomRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
     icon: any
     name: string
     meta: RouteMeta

+ 1 - 1
vite.config.ts

@@ -42,7 +42,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
       // },
     },
     // 项目使用的vite插件。 单独提取到build/vite/plugin中管理
-    plugins: createVitePlugins(env.VITE_APP_TITLE),
+    plugins: createVitePlugins(),
     css: {
       preprocessorOptions: {
         scss: {