ydmyzx 3 months ago
parent
commit
b7a026e27e

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021-present Archer
+
+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.

File diff suppressed because it is too large
+ 0 - 0
src/assets/svgs/login-bg.svg


File diff suppressed because it is too large
+ 0 - 0
src/assets/svgs/login-bg1.svg


+ 148 - 0
src/components/Form/src/helper.ts

@@ -0,0 +1,148 @@
+import type { Slots } from 'vue'
+import { getSlot } from '@/utils/tsxHelper'
+import { PlaceholderModel } from './types'
+import { FormSchema } from '@/types/form'
+import { ColProps } from '@/types/components'
+
+/**
+ *
+ * @param schema 对应组件数据
+ * @returns 返回提示信息对象
+ * @description 用于自动设置placeholder
+ */
+export const setTextPlaceholder = (schema: FormSchema): PlaceholderModel => {
+  const { t } = useI18n()
+  const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword']
+  const selectMap = ['Select', 'SelectV2', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect']
+  if (textMap.includes(schema?.component as string)) {
+    return {
+      placeholder: t('common.inputText') + schema.label
+    }
+  }
+  if (selectMap.includes(schema?.component as string)) {
+    // 一些范围选择器
+    const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange']
+    if (
+      twoTextMap.includes(
+        (schema?.componentProps?.type || schema?.componentProps?.isRange) as string
+      )
+    ) {
+      return {
+        startPlaceholder: t('common.startTimeText'),
+        endPlaceholder: t('common.endTimeText'),
+        rangeSeparator: '-'
+      }
+    } else {
+      return {
+        placeholder: t('common.selectText') + schema.label
+      }
+    }
+  }
+  return {}
+}
+
+/**
+ *
+ * @param col 内置栅格
+ * @returns 返回栅格属性
+ * @description 合并传入进来的栅格属性
+ */
+export const setGridProp = (col: ColProps = {}): ColProps => {
+  const colProps: ColProps = {
+    // 如果有span,代表用户优先级更高,所以不需要默认栅格
+    ...(col.span
+      ? {}
+      : {
+          xs: 24,
+          sm: 12,
+          md: 12,
+          lg: 12,
+          xl: 12
+        }),
+    ...col
+  }
+  return colProps
+}
+
+/**
+ *
+ * @param item 传入的组件属性
+ * @returns 默认添加 clearable 属性
+ */
+export const setComponentProps = (item: FormSchema): Recordable => {
+  const notNeedClearable = ['ColorPicker']
+  const componentProps: Recordable = notNeedClearable.includes(item.component as string)
+    ? { ...item.componentProps }
+    : {
+        clearable: true,
+        ...item.componentProps
+      }
+  // 需要删除额外的属性
+  delete componentProps?.slots
+  return componentProps
+}
+
+/**
+ *
+ * @param slots 插槽
+ * @param slotsProps 插槽属性
+ * @param field 字段名
+ */
+export const setItemComponentSlots = (
+  slots: Slots,
+  slotsProps: Recordable = {},
+  field: string
+): Recordable => {
+  const slotObj: Recordable = {}
+  for (const key in slotsProps) {
+    if (slotsProps[key]) {
+      // 由于组件有可能重复,需要有一个唯一的前缀
+      slotObj[key] = (data: Recordable) => {
+        return getSlot(slots, `${field}-${key}`, data)
+      }
+    }
+  }
+  return slotObj
+}
+
+/**
+ *
+ * @param schema Form表单结构化数组
+ * @param formModel FormModel
+ * @returns FormModel
+ * @description 生成对应的formModel
+ */
+export const initModel = (schema: FormSchema[], formModel: Recordable) => {
+  const model: Recordable = { ...formModel }
+  schema.map((v) => {
+    // 如果是hidden,就删除对应的值
+    if (v.hidden) {
+      delete model[v.field]
+    } else if (v.component && v.component !== 'Divider') {
+      const hasField = Reflect.has(model, v.field)
+      // 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值
+      model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : ''
+    }
+  })
+  return model
+}
+
+/**
+ * @param slots 插槽
+ * @param field 字段名
+ * @returns 返回FormIiem插槽
+ */
+export const setFormItemSlots = (slots: Slots, field: string): Recordable => {
+  const slotObj: Recordable = {}
+  if (slots[`${field}-error`]) {
+    slotObj['error'] = (data: Recordable) => {
+      return getSlot(slots, `${field}-error`, data)
+    }
+  }
+  if (slots[`${field}-label`]) {
+    slotObj['label'] = (data: Recordable) => {
+      return getSlot(slots, `${field}-label`, data)
+    }
+  }
+  return slotObj
+}

+ 8 - 0
src/components/Table/src/helper.ts

@@ -0,0 +1,8 @@
+export const setIndex = (reserveIndex: boolean, index: number, size: number, current: number) => {
+  const newIndex = index + 1
+  if (reserveIndex) {
+    return size * (current - 1) + newIndex
+  } else {
+    return newIndex
+  }
+}

+ 27 - 0
src/directives/permission/hasPermi.ts

@@ -0,0 +1,27 @@
+import type { App } from 'vue'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
+
+const { t } = useI18n() // 国际化
+
+export function hasPermi(app: App<Element>) {
+  app.directive('hasPermi', (el, binding) => {
+    const { wsCache } = useCache()
+    const { value } = binding
+    const all_permission = '*:*:*'
+    const permissions = wsCache.get(CACHE_KEY.USER).permissions
+
+    if (value && value instanceof Array && value.length > 0) {
+      const permissionFlag = value
+
+      const hasPermissions = permissions.some((permission: string) => {
+        return all_permission === permission || permissionFlag.includes(permission)
+      })
+
+      if (!hasPermissions) {
+        el.parentNode && el.parentNode.removeChild(el)
+      }
+    } else {
+      throw new Error(t('permission.hasPermission'))
+    }
+  })
+}

+ 27 - 0
src/directives/permission/hasRole.ts

@@ -0,0 +1,27 @@
+import type { App } from 'vue'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
+
+const { t } = useI18n() // 国际化
+
+export function hasRole(app: App<Element>) {
+  app.directive('hasRole', (el, binding) => {
+    const { wsCache } = useCache()
+    const { value } = binding
+    const super_admin = 'admin'
+    const roles = wsCache.get(CACHE_KEY.USER).roles
+
+    if (value && value instanceof Array && value.length > 0) {
+      const roleFlag = value
+
+      const hasRole = roles.some((role: string) => {
+        return super_admin === role || roleFlag.includes(role)
+      })
+
+      if (!hasRole) {
+        el.parentNode && el.parentNode.removeChild(el)
+      }
+    } else {
+      throw new Error(t('permission.hasRole'))
+    }
+  })
+}

+ 31 - 0
src/layout/components/Breadcrumb/src/helper.ts

@@ -0,0 +1,31 @@
+import { pathResolve } from '@/utils/routerHelper'
+import type { RouteMeta } from 'vue-router'
+
+export const filterBreadcrumb = (
+  routes: AppRouteRecordRaw[],
+  parentPath = ''
+): AppRouteRecordRaw[] => {
+  const res: AppRouteRecordRaw[] = []
+
+  for (const route of routes) {
+    const meta = route?.meta as RouteMeta
+    if (meta.hidden && !meta.canTo) {
+      continue
+    }
+
+    const data: AppRouteRecordRaw =
+      !meta.alwaysShow && route.children?.length === 1
+        ? { ...route.children[0], path: pathResolve(route.path, route.children[0].path) }
+        : { ...route }
+
+    data.path = pathResolve(parentPath, data.path)
+
+    if (data.children) {
+      data.children = filterBreadcrumb(data.children, data.path)
+    }
+    if (data) {
+      res.push(data)
+    }
+  }
+  return res
+}

+ 54 - 0
src/layout/components/Menu/src/helper.ts

@@ -0,0 +1,54 @@
+import type { RouteMeta } from 'vue-router'
+import { findPath } from '@/utils/tree'
+
+type OnlyOneChildType = AppRouteRecordRaw & { noShowingChildren?: boolean }
+
+interface HasOneShowingChild {
+  oneShowingChild?: boolean
+  onlyOneChild?: OnlyOneChildType
+}
+
+export const getAllParentPath = <T = Recordable>(treeData: T[], path: string) => {
+  const menuList = findPath(treeData, (n) => n.path === path) as AppRouteRecordRaw[]
+  return (menuList || []).map((item) => item.path)
+}
+
+export const hasOneShowingChild = (
+  children: AppRouteRecordRaw[] = [],
+  parent: AppRouteRecordRaw
+): HasOneShowingChild => {
+  const onlyOneChild = ref<OnlyOneChildType>()
+
+  const showingChildren = children.filter((v) => {
+    const meta = (v.meta ?? {}) as RouteMeta
+    if (meta.hidden) {
+      return false
+    } else {
+      // Temp set(will be used if only has one showing child)
+      onlyOneChild.value = v
+      return true
+    }
+  })
+
+  // When there is only one child router, the child router is displayed by default
+  if (showingChildren.length === 1) {
+    return {
+      oneShowingChild: true,
+      onlyOneChild: unref(onlyOneChild)
+    }
+  }
+
+  // Show parent if there are no child router to display
+  if (!showingChildren.length) {
+    onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
+    return {
+      oneShowingChild: true,
+      onlyOneChild: unref(onlyOneChild)
+    }
+  }
+
+  return {
+    oneShowingChild: false,
+    onlyOneChild: unref(onlyOneChild)
+  }
+}

+ 51 - 0
src/layout/components/TabMenu/src/helper.ts

@@ -0,0 +1,51 @@
+import { getAllParentPath } from '@/layout/components/Menu/src/helper'
+import type { RouteMeta } from 'vue-router'
+import { isUrl } from '@/utils/is'
+import { cloneDeep } from 'lodash-es'
+
+export type TabMapTypes = {
+  [key: string]: string[]
+}
+
+export const tabPathMap = reactive<TabMapTypes>({})
+
+export const initTabMap = (routes: AppRouteRecordRaw[]) => {
+  for (const v of routes) {
+    const meta = (v.meta ?? {}) as RouteMeta
+    if (!meta?.hidden) {
+      tabPathMap[v.path] = []
+    }
+  }
+}
+
+export const filterMenusPath = (
+  routes: AppRouteRecordRaw[],
+  allRoutes: AppRouteRecordRaw[]
+): AppRouteRecordRaw[] => {
+  const res: AppRouteRecordRaw[] = []
+  for (const v of routes) {
+    let data: Nullable<AppRouteRecordRaw> = null
+    const meta = (v.meta ?? {}) as RouteMeta
+    if (!meta.hidden || meta.canTo) {
+      const allParentPath = getAllParentPath<AppRouteRecordRaw>(allRoutes, v.path)
+
+      const fullPath = isUrl(v.path) ? v.path : allParentPath.join('/')
+
+      data = cloneDeep(v)
+      data.path = fullPath
+      if (v.children && data) {
+        data.children = filterMenusPath(v.children, allRoutes)
+      }
+
+      if (data) {
+        res.push(data)
+      }
+
+      if (allParentPath.length && Reflect.has(tabPathMap, allParentPath[0])) {
+        tabPathMap[allParentPath[0]].push(fullPath)
+      }
+    }
+  }
+
+  return res
+}

+ 21 - 0
src/layout/components/TagsView/src/helper.ts

@@ -0,0 +1,21 @@
+import type { RouteMeta, RouteLocationNormalizedLoaded } from 'vue-router'
+import { pathResolve } from '@/utils/routerHelper'
+
+export const filterAffixTags = (routes: AppRouteRecordRaw[], parentPath = '') => {
+  let tags: RouteLocationNormalizedLoaded[] = []
+  routes.forEach((route) => {
+    const meta = route.meta as RouteMeta
+    const tagPath = pathResolve(parentPath, route.path)
+    if (meta?.affix) {
+      tags.push({ ...route, path: tagPath, fullPath: tagPath } as RouteLocationNormalizedLoaded)
+    }
+    if (route.children) {
+      const tempTags: RouteLocationNormalizedLoaded[] = filterAffixTags(route.children, tagPath)
+      if (tempTags.length >= 1) {
+        tags = [...tags, ...tempTags]
+      }
+    }
+  })
+
+  return tags
+}

BIN
src/views/mall/promotion/kefu/components/asserts/kaixin.png


BIN
src/views/mall/promotion/kefu/components/asserts/keai.png


BIN
src/views/mall/promotion/kefu/components/asserts/keshui.png


BIN
src/views/mall/promotion/kefu/components/asserts/kun.png


BIN
src/views/mall/promotion/kefu/components/asserts/lengku.png


BIN
src/views/mall/promotion/kefu/components/asserts/liuhan.png


BIN
src/views/mall/promotion/kefu/components/asserts/liukoushui.png


BIN
src/views/mall/promotion/kefu/components/asserts/liulei.png


Some files were not shown because too many files changed in this diff