Переглянути джерело

1.增加文本组件
2.增加logo置顶
3.增加引导组件
4.修改已编写组件bug

XueNing 5 місяців тому
батько
коміт
a9b5c42f31
41 змінених файлів з 702 додано та 541 видалено
  1. 1 0
      src/components.d.ts
  2. 1 0
      src/components/FsCheckCard/components/CardItem.vue
  3. 24 17
      src/components/FsCheckCard/index.vue
  4. 9 23
      src/components/FsCheckCard/props.ts
  5. 70 23
      src/components/FsImageUpload/index.vue
  6. 25 50
      src/components/FsImageUpload/props.ts
  7. 40 40
      src/components/FsPrinter/index.vue
  8. 13 18
      src/components/FsPrinter/props.ts
  9. 48 48
      src/components/FsSplitPanel/index.vue
  10. 13 18
      src/components/FsSplitPanel/props.ts
  11. 11 8
      src/components/FsTableSelect/index.vue
  12. 1 1
      src/components/FsTableSelect/props.ts
  13. 0 2
      src/components/FsTableSelect/types/index.ts
  14. 52 48
      src/components/FsTour/index.vue
  15. 6 20
      src/components/FsTour/props.ts
  16. 1 1
      src/components/FsTour/util.ts
  17. 1 32
      src/components/core/GlobalAside.vue
  18. 1 0
      src/components/core/GlobalHeader.vue
  19. 54 0
      src/components/core/GlobalLogo.vue
  20. 5 1
      src/components/core/GlobalSetting.vue
  21. 1 0
      src/config/defaultSetting.ts
  22. 10 6
      src/hooks/useExcel.ts
  23. 26 21
      src/layouts/BasicLayout.vue
  24. 79 72
      src/router/asyncRouter.ts
  25. 3 1
      src/stores/theme.ts
  26. 3 0
      src/utils/constants.ts
  27. 3 4
      src/views/extension/checkCard/index.vue
  28. 0 0
      src/views/extension/excel/export.vue
  29. 0 0
      src/views/extension/excel/import.vue
  30. 0 0
      src/views/extension/excel/index.vue
  31. 16 11
      src/views/extension/imageUpload/index.vue
  32. 0 0
      src/views/extension/printer/index.vue
  33. 0 0
      src/views/extension/printer/printContract.vue
  34. 0 0
      src/views/extension/splitePanel/index.vue
  35. 0 0
      src/views/extension/stausText/index.vue
  36. 23 8
      src/views/extension/tableSelect/base.vue
  37. 12 10
      src/views/extension/tableSelect/disabled.vue
  38. 0 0
      src/views/extension/tableSelect/index.vue
  39. 8 9
      src/views/extension/tableSelect/search.vue
  40. 142 0
      src/views/extension/tour/index.vue
  41. 0 49
      src/views/tour/index.vue

+ 1 - 0
src/components.d.ts

@@ -25,6 +25,7 @@ declare module 'vue' {
     GlobalAside: typeof import('./components/core/GlobalAside.vue')['default']
     GlobalFooter: typeof import('./components/core/GlobalFooter.vue')['default']
     GlobalHeader: typeof import('./components/core/GlobalHeader.vue')['default']
+    GlobalLogo: typeof import('./components/core/GlobalLogo.vue')['default']
     GlobalMenu: typeof import('./components/core/GlobalMenu.vue')['default']
     GlobalNews: typeof import('./components/core/GlobalNews.vue')['default']
     GlobalSetting: typeof import('./components/core/GlobalSetting.vue')['default']

+ 1 - 0
src/components/FsCheckCard/components/CardItem.vue

@@ -26,6 +26,7 @@ defineProps({
 
 <style scoped lang="scss">
 .check-card-item {
+  height: 100%;
   margin-right: 5px;
   margin-bottom: 5px;
   font-size: 13px;

+ 24 - 17
src/components/FsCheckCard/index.vue

@@ -1,10 +1,17 @@
 <script setup lang="ts">
-import { checkCardProps, checkCardEmits } from './props'
+import type { CheckCardProps } from './props'
+import { checkCardEmits } from './props'
 import type { CheckCardItem } from './types'
 import CardItem from './components/CardItem.vue'
 
-const props = defineProps(checkCardProps)
-const emit = defineEmits(checkCardEmits)
+const props = withDefaults(defineProps<CheckCardProps>(), {
+  modelValue: '',
+  multiple: false,
+  disabled: false,
+  bordered: true,
+  arrow: true
+})
+const emits = defineEmits(checkCardEmits)
 
 const onItemClick = (item: CheckCardItem) => {
   // 是否禁用
@@ -15,34 +22,34 @@ const onItemClick = (item: CheckCardItem) => {
   // 是否多选
   if (props.multiple) {
     item.checked = !item.checked
-    const select = props.items?.filter(x => x.checked)
-    updateModelValue(select ? select.map(x => x.value) : null)
+    updateModelValue()
   } else {
     // 单选
     const select = props.items?.find(x => x.checked)
     select && (select.checked = false)
     item.checked = !item.checked
-    updateModelValue((item.checked ? item.value : '') as any)
+    updateModelValue()
   }
 }
 
 /* 修改modelValue */
-const updateModelValue = (modelValue: any) => {
-  emit('update:modelValue', modelValue)
+const updateModelValue = () => {
+  const value =
+    props.items
+      ?.filter(x => x.checked)
+      .map(x => x.value)
+      .join(',') || ''
+  emits('update:modelValue', value)
 }
-// 分割的字符串 1,2,3,45,6
 watch(
   () => props.modelValue,
   () => {
     if (props.modelValue) {
-      if (Array.isArray(props.modelValue)) {
-        props.items?.forEach(item => {
-          item.checked = (props.modelValue as Array<any>).includes(item.value)
-        })
-      } else {
-        const select = props.items?.find(x => x.value === props.modelValue)
-        select && (select.checked = true)
-      }
+      const value = props.modelValue.split(',').filter(x => x)
+      props.items?.forEach(item => {
+        item.checked = value.includes(item.value)
+      })
+      updateModelValue()
     }
   },
   {

+ 9 - 23
src/components/FsCheckCard/props.ts

@@ -2,35 +2,21 @@
 import type { RowProps } from 'element-plus'
 import type { CheckCardItem } from './types'
 
-export const checkCardProps = {
+export interface CheckCardProps {
   // 选中值
-  modelValue: {
-    type: Object, // ToDo
-    default: () => {
-      return null
-    }
-  },
+  modelValue: string
   // 数据
-  items: Object as PropType<CheckCardItem[]>,
+  items?: CheckCardItem[]
   // 是否多选
-  multiple: Boolean,
+  multiple?: boolean
   // 是否禁用
-  disabled: Boolean,
+  disabled?: boolean
   // 是否显示边框
-  bordered: {
-    type: Boolean,
-    default: true
-  },
+  bordered?: boolean
   // 是否需要选中箭头
-  arrow: {
-    type: Boolean,
-    default: true
-  },
-  // 是否使用栅格布局
-  row: {
-    type: [Boolean, Object] as PropType<boolean | Partial<RowProps>>,
-    default: false
-  }
+  arrow?: boolean
+  // 是否使用删格布局
+  row?: boolean | Partial<RowProps>
 }
 
 /* 事件 */

+ 70 - 23
src/components/FsImageUpload/index.vue

@@ -1,18 +1,28 @@
 <script setup lang="ts">
 import { ElMessage } from 'element-plus'
-import { imageUploadProps, imageUploadEmits } from './props'
+import type { ImageUploadProps } from './props'
+import { imageUploadEmits } from './props'
 import type { UploadItem } from './types'
+import { uuid } from '@/utils/utils'
+import axios, { type AxiosProgressEvent } from 'axios'
 
-const emit = defineEmits(imageUploadEmits)
+const emits = defineEmits(imageUploadEmits)
 
-const props = defineProps(imageUploadProps)
+const props = withDefaults(defineProps<ImageUploadProps>(), {
+  fileName: 'file',
+  autoUpload: true,
+  preview: true,
+  limit: 9,
+  fileSize: 5,
+  iconSize: 28
+})
 
 const images = ref<UploadItem[]>([])
 
 // 是否可上传
 const isUpload = computed<boolean>(() => {
   return (
-    !props.readonly &&
+    !props.disabled &&
     !(typeof props.limit === 'number' && props.limit > 0 && images.value != null && images.value.length >= props.limit)
   )
 })
@@ -32,7 +42,6 @@ const previewList = computed(() => {
 
 /* 选择文件 */
 const onUpload = (file: File) => {
-  console.log(file)
   if (!isUpload.value || props.disabled) {
     return false
   }
@@ -46,7 +55,7 @@ const onUpload = (file: File) => {
   }
 
   const item: UploadItem = {
-    key: Date.now(),
+    key: uuid(),
     name: file.name,
     status: void 0,
     progress: 0,
@@ -57,17 +66,41 @@ const onUpload = (file: File) => {
   images.value.push(item)
   // 是否自动上传
   if (props.autoUpload) {
-    uploadItem(item)
+    // 自动上传最后一个文件
+    uploadItem(images.value.at(-1) as UploadItem)
   }
   return false
 }
 
 /* 上传文件 */
 const uploadItem = (item: UploadItem) => {
-  if (typeof props.uploadFunction === 'function') {
-    props.uploadFunction(item)
+  if (!props.action && typeof props.uploadFunction === 'function') {
+    console.log('请传入action路径或者uploadFunction上传方法')
+    return false
+  }
+  // 如果配置了上传路径
+  if (props.action) {
+    const formData = new FormData()
+    formData.append(props.fileName, item.file as File)
+    axios
+      .post(props.action, formData, {
+        onUploadProgress: (e: AxiosProgressEvent) => {
+          if (e.total != null) {
+            item.progress = (e.loaded / e.total) * 100
+          }
+        }
+      })
+      .then(res => {
+        item.status = 'success'
+        item.url = res.data
+      })
+      .catch(() => {
+        item.status = 'danger'
+      })
   } else {
-    console.log('请传入uploadFunction')
+    // 自定义方法上传
+    if (typeof props.uploadFunction != 'function') return false
+    ;(props.uploadFunction as Function)(item)
   }
 }
 
@@ -82,7 +115,7 @@ const submit = () => {
 
 /* 删除图片 */
 const onRemove = (index: number) => {
-  emit('remove', images.value[index])
+  emits('remove', images.value[index])
   images.value.splice(index, 1)
 }
 
@@ -92,22 +125,32 @@ const onRetry = (index: number) => {
 }
 
 /* 修改modelValue */
-const updateModelValue = (items: UploadItem[]) => {
-  // ToDo:这里需要优化,最后格式为'url1,url2,url3'
-  emit('update:modelValue', items)
+const updateModelValue = (items: any) => {
+  emits('update:modelValue', items.map((x: UploadItem) => x.url).join(',') || '')
 }
 
 watch(
   () => props.modelValue,
   () => {
-    // ToDo:这里需要优化,需要自行处理格式
-    images.value = props.modelValue || []
+    if (typeof props.modelValue === 'string' && props.modelValue.startsWith('http')) {
+      // 传进来的数据转换成内部需要数据
+      const formatData = typeof props.modelValue === 'string' ? props.modelValue.split(',').filter(x => x) : []
+      const datas: UploadItem[] = formatData.map((x: string) => {
+        return {
+          key: uuid(),
+          url: x,
+          status: 'success',
+          name: x.split('/').pop()
+        }
+      })
+      images.value = datas
+    }
   },
   { immediate: true }
 )
 
 watch(
-  images.value,
+  images,
   () => {
     updateModelValue(images.value)
   },
@@ -124,9 +167,9 @@ defineExpose({
 
 <template>
   <div class="upload-container">
-    <div class="upload-image" v-for="(item, index) in modelValue" :key="item.key">
+    <div class="upload-image" v-for="(item, index) in images" :key="item.key">
       <el-image :src="item.url" :preview-src-list="previewList" :initial-index="index" fit="cover"></el-image>
-      <div v-if="!readonly && !disabled" class="upload-remove" @click.stop="onRemove(index)">
+      <div v-if="!disabled" class="upload-remove" @click.stop="onRemove(index)">
         <el-icon size="14">
           <Close />
         </el-icon>
@@ -155,7 +198,7 @@ defineExpose({
     <div>
       <el-upload
         action=""
-        :accept="accept"
+        accept="image/*"
         :multiple="multiple"
         :disabled="disabled"
         :show-file-list="false"
@@ -163,8 +206,8 @@ defineExpose({
         :drag="drag"
         v-if="isUpload"
       >
-        <div class="upload-plus" :style="buttonStyle">
-          <el-icon size="30">
+        <div class="upload-plus">
+          <el-icon :size="iconSize">
             <Plus />
           </el-icon>
         </div>
@@ -230,6 +273,10 @@ defineExpose({
         margin-top: 5px;
       }
     }
+    .el-image {
+      width: 100%;
+      height: 100%;
+    }
   }
   .upload-plus {
     width: 100px;
@@ -240,7 +287,7 @@ defineExpose({
     color: var(--el-color-info);
     border: 1px dashed #dcdfe6;
     border-radius: var(--el-border-radius-base);
-    &:hover {
+    &:not(.disabled):hover {
       border-color: var(--el-color-primary);
     }
   }

+ 25 - 50
src/components/FsImageUpload/props.ts

@@ -3,64 +3,39 @@ import type { CSSProperties } from 'vue'
 import type { UploadItem } from './types'
 import type { ProgressProps } from 'element-plus'
 
-// ToDo 默认方式改为withDefaults
-export const imageUploadProps = {
-  // 已上传列表
-  modelValue: {
-    type: Array as PropType<UploadItem[]>,
-    required: true
-  },
+// 传入属性
+export interface ImageUploadProps {
+  // 已上传列表
+  modelValue: string
+  // 上传路径
+  action?: string
+  fileName?: string
   // 是否自动上传
-  autoUpload: {
-    type: Boolean,
-    default: true
-  },
+  autoUpload?: boolean
   // 是否启用拖拽上传
-  drag: Boolean,
-  // 是否只读
-  readonly: Boolean,
+  drag?: boolean
   // 是否禁用
-  disabled: Boolean,
+  disabled?: boolean
   // 是否点击预览
-  preview: {
-    type: Boolean,
-    default: true
-  },
-  // 可上传数量
-  limit: {
-    type: Number,
-    default: 9
-  },
-  // 是否支持多选
-  multiple: {
-    type: Boolean,
-    default: false
-  },
-  // 上传类型 ToDo: 图片类型去掉,内部自行限制是否是图片
-  accept: {
-    type: String,
-    default: 'image/png,image/jpeg'
-  },
+  preview?: boolean
+  // 上传数量
+  limit?: number
+  // 是否可以多选
+  multiple?: boolean
   // 文件大小限制(MB)
-  fileSize: {
-    type: Number,
-    default: 5
-  },
-  // item 样式  Todo: 改为 width height iconStyle
-  itemStyle: Object as PropType<Partial<CSSProperties> | Array<Partial<CSSProperties>>>,
-  // 上传按钮样式
-  buttonStyle: Object as PropType<Partial<CSSProperties> | Array<Partial<CSSProperties>>>,
-  // 上传进度条配置
-  progressProps: Object as PropType<ProgressProps>,
+  fileSize?: number
+  // 宽度
+  width?: number | string
+  // 高度
+  height?: number | string
+  // 图标大小
+  iconSize?: number
+  // 进度条配置
+  progressProps?: ProgressProps
   // 上传方法
-  // ToDo: 结合项目中cofing文件的文件路径实现,增加上传文件key属性
-  uploadFunction: {
-    type: Function as PropType<(file: any) => Promise<any>>
-  }
+  uploadFunction?: (file: any) => Promise<any>
 }
 
-export type ImageUploadProps = ExtractPropTypes<typeof imageUploadProps>
-
 /* 事件 */
 export const imageUploadEmits = {
   // 上传事件

+ 40 - 40
src/components/FsPrinter/index.vue

@@ -1,46 +1,12 @@
-<!-- 打印 -->
-<template>
-  <Teleport :to="container" :disabled="isStatic && !visible">
-    <table :class="['custom-printer', { 'is-open': visible }, { 'is-static': isStatic }]">
-      <thead v-if="$slots.header">
-        <tr>
-          <td>
-            <div class="custom-printer-header" :style="headerStyle">
-              <slot name="header"></slot>
-            </div>
-          </td>
-        </tr>
-      </thead>
-      <tbody>
-        <tr>
-          <td>
-            <div class="custom-printer-body" :style="bodyStyle">
-              <slot></slot>
-            </div>
-          </td>
-        </tr>
-      </tbody>
-      <tfoot v-if="$slots.footer">
-        <tr>
-          <td>
-            <div class="custom-printer-footer" :style="footerStyle">
-              <slot name="footer"></slot>
-            </div>
-          </td>
-        </tr>
-      </tfoot>
-    </table>
-  </Teleport>
-</template>
-
 <script setup lang="ts">
 import { getPrintContainer, doPrint, doPrintOnFrame, mergeOptions, usePrinter } from './util'
 import type { PrintOption } from './types'
-import { printerProps, printerEmits } from './props'
+import { printerEmits } from './props'
+import type { PrinterProps } from './props'
 
-const props = defineProps(printerProps)
+const props = withDefaults(defineProps<PrinterProps>(), {})
 
-const emit = defineEmits(printerEmits)
+const emits = defineEmits(printerEmits)
 
 const { printId } = usePrinter(() => {
   updateModelValue(false)
@@ -84,12 +50,12 @@ const print = (options?: any) => {
 
 /** 打印完成事件 */
 const onDone = () => {
-  emit('done')
+  emits('done')
 }
 
 /** 更新绑定值 */
 const updateModelValue = (value: boolean) => {
-  emit('update:modelValue', value)
+  emits('update:modelValue', value)
 }
 
 watch(
@@ -107,6 +73,40 @@ defineExpose({
 })
 </script>
 
+<template>
+  <Teleport :to="container" :disabled="isStatic && !visible">
+    <table :class="['custom-printer', { 'is-open': visible }, { 'is-static': isStatic }]">
+      <thead v-if="$slots.header">
+        <tr>
+          <td>
+            <div class="custom-printer-header" :style="headerStyle">
+              <slot name="header"></slot>
+            </div>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div class="custom-printer-body" :style="bodyStyle">
+              <slot></slot>
+            </div>
+          </td>
+        </tr>
+      </tbody>
+      <tfoot v-if="$slots.footer">
+        <tr>
+          <td>
+            <div class="custom-printer-footer" :style="footerStyle">
+              <slot name="footer"></slot>
+            </div>
+          </td>
+        </tr>
+      </tfoot>
+    </table>
+  </Teleport>
+</template>
+
 <style lang="scss">
 /* 打印容器 */
 #custom-printer-container {

+ 13 - 18
src/components/FsPrinter/props.ts

@@ -1,37 +1,32 @@
 /* eslint-disable @typescript-eslint/no-unused-vars */
-import type { PropType, ExtractPropTypes, CSSProperties } from 'vue'
+import type { CSSProperties } from 'vue'
 import type { PrintDirection, PrintOrientation, PrintTarget } from './types'
 
-/**
- * 属性
- */
-export const printerProps = {
+export interface PrinterProps {
   // 是否打印
-  modelValue: Boolean,
+  modelValue?: boolean
   // 页眉样式
-  headerStyle: Object as PropType<Partial<CSSProperties> | Array<Partial<CSSProperties>>>,
+  headerStyle?: Partial<CSSProperties> | Array<Partial<CSSProperties>>
   // 内容样式
-  bodyStyle: Object as PropType<Partial<CSSProperties> | Array<Partial<CSSProperties>>>,
+  bodyStyle?: Partial<CSSProperties> | Array<Partial<CSSProperties>>
   // 页脚样式
-  footerStyle: Object as PropType<Partial<CSSProperties> | Array<Partial<CSSProperties>>>,
+  footerStyle?: Partial<CSSProperties> | Array<Partial<CSSProperties>>
   // 标题
-  title: String,
+  title?: string
   // 页间距
-  margin: [String, Number],
+  margin?: string | number
   // 纸张方向
-  direction: String as PropType<PrintDirection | null>,
+  direction?: PrintDirection | null
   // 纸张旋转
-  orientation: String as PropType<PrintOrientation | null>,
+  orientation?: PrintOrientation | null
   // 打印位置
-  target: String as PropType<PrintTarget | null>,
+  target?: PrintTarget | null
   // 是否显示在文档流中
-  static: Boolean,
+  static?: boolean
   // 打印方法参数
-  options: Object
+  options?: Record<string, any>
 }
 
-export type PrinterProps = ExtractPropTypes<typeof printerProps>
-
 /**
  * 事件
  */

+ 48 - 48
src/components/FsSplitPanel/index.vue

@@ -1,52 +1,9 @@
-<!-- 分割面板 -->
-<template>
-  <div
-    ref="rootRef"
-    :class="[
-      'split-panel',
-      { 'is-reverse': reverse },
-      { 'is-vertical': vertical },
-      { 'is-collapse': isCollapse },
-      { 'is-resizing': resizing }
-    ]"
-    :style="{
-      '--split-size': resizedSize ?? size,
-      '--split-space': space
-    }"
-  >
-    <!-- 侧边容器 -->
-    <div ref="wrapRef" class="split-panel-wrap">
-      <div ref="sideRef" class="split-panel-side" :style="customStyle">
-        <slot></slot>
-      </div>
-      <!-- 间距 -->
-      <div class="split-panel-space">
-        <div v-if="resizable" class="split-resize-line" @mousedown="onResize"></div>
-      </div>
-    </div>
-    <!-- 内容 -->
-    <div class="split-panel-body" :style="bodyStyle">
-      <slot name="body" :collapse="isCollapse"></slot>
-    </div>
-    <!-- 折叠按钮 -->
-    <div v-if="allowCollapse" :style="collapseStyle" class="split-collapse-button" @click="toggleCollapse()">
-      <slot name="collapse" :collapse="isCollapse">
-        <ElIcon class="split-collapse-icon">
-          <ArrowUp v-if="vertical" />
-          <ArrowLeft v-else />
-        </ElIcon>
-      </slot>
-    </div>
-    <!-- 小屏幕遮罩层 -->
-    <div class="split-panel-mask" @click="toggleCollapse()"></div>
-  </div>
-</template>
-
 <script setup lang="ts">
-import { splitPanelProps, splitPanelEmits } from './props'
+import type { SplitPanelProps } from './props'
+import { splitPanelEmits } from './props'
 
-const props = defineProps(splitPanelProps)
-const emit = defineEmits(splitPanelEmits)
+const props = withDefaults(defineProps<SplitPanelProps>(), {})
+const emits = defineEmits(splitPanelEmits)
 
 // 根节点
 const rootRef = ref<HTMLElement | null>(null)
@@ -69,7 +26,7 @@ const resizing = ref<boolean>(false)
 /* 切换折叠状态 */
 const toggleCollapse = (collapse?: boolean) => {
   isCollapse.value = typeof collapse === 'boolean' ? collapse : !isCollapse.value
-  emit('update:collapse', isCollapse.value)
+  emits('update:collapse', isCollapse.value)
 }
 
 /* 获取最大拉伸尺寸 */
@@ -137,6 +94,49 @@ watch(
 )
 </script>
 
+<template>
+  <div
+    ref="rootRef"
+    :class="[
+      'split-panel',
+      { 'is-reverse': reverse },
+      { 'is-vertical': vertical },
+      { 'is-collapse': isCollapse },
+      { 'is-resizing': resizing }
+    ]"
+    :style="{
+      '--split-size': resizedSize ?? size,
+      '--split-space': space
+    }"
+  >
+    <!-- 侧边容器 -->
+    <div ref="wrapRef" class="split-panel-wrap">
+      <div ref="sideRef" class="split-panel-side" :style="customStyle">
+        <slot></slot>
+      </div>
+      <!-- 间距 -->
+      <div class="split-panel-space">
+        <div v-if="resizable" class="split-resize-line" @mousedown="onResize"></div>
+      </div>
+    </div>
+    <!-- 内容 -->
+    <div class="split-panel-body" :style="bodyStyle">
+      <slot name="body" :collapse="isCollapse"></slot>
+    </div>
+    <!-- 折叠按钮 -->
+    <div v-if="allowCollapse" :style="collapseStyle" class="split-collapse-button" @click="toggleCollapse()">
+      <slot name="collapse" :collapse="isCollapse">
+        <ElIcon class="split-collapse-icon">
+          <ArrowUp v-if="vertical" />
+          <ArrowLeft v-else />
+        </ElIcon>
+      </slot>
+    </div>
+    <!-- 小屏幕遮罩层 -->
+    <div class="split-panel-mask" @click="toggleCollapse()"></div>
+  </div>
+</template>
+
 <style scoped lang="scss">
 .split-panel {
   display: flex;

+ 13 - 18
src/components/FsSplitPanel/props.ts

@@ -2,38 +2,33 @@
 
 import type { CSSProperties } from 'vue'
 
-/**
- * 属性
- */
-export const splitPanelProps = {
+export interface SplitPanelProps {
   // 默认大小
-  size: String,
+  size?: string
   // 最小尺寸
-  minSize: Number,
+  minSize?: number
   // 最大尺寸
-  maxSize: Number,
+  maxSize?: number
   // 间距
-  space: String,
+  space?: string
   // 自定义样式
-  customStyle: Object as PropType<Partial<CSSProperties> | Array<Partial<CSSProperties>>>,
+  customStyle?: Partial<CSSProperties> | Array<Partial<CSSProperties>>
   // 自定义内容样式
-  bodyStyle: Object as PropType<Partial<CSSProperties> | Array<Partial<CSSProperties>>>,
+  bodyStyle?: Partial<CSSProperties> | Array<Partial<CSSProperties>>
   // 是否可折叠
-  allowCollapse: Boolean,
+  allowCollapse?: boolean
   // 折叠按钮样式
-  collapseStyle: Object,
+  collapseStyle?: Record<string, any>
   // 是否折叠
-  collapse: Boolean,
+  collapse?: boolean
   // 是否垂直方向
-  vertical: Boolean,
+  vertical?: boolean
   // 是否反向布局
-  reverse: Boolean,
+  reverse?: boolean
   // 是否可拉伸宽度
-  resizable: Boolean
+  resizable?: boolean
 }
 
-export type SplitPanelProps = ExtractPropTypes<typeof splitPanelProps>
-
 /**
  * 事件
  */

+ 11 - 8
src/components/FsTableSelect/index.vue

@@ -51,7 +51,7 @@ const loading = ref(false)
 // 表格数据
 const tableData = ref<any[]>([])
 // 页码
-const pageIndex = ref<number>(1)
+const pageNo = ref<number>(1)
 // 总数
 const tableTotal = ref<number>(0)
 
@@ -159,7 +159,7 @@ const initValueChange = (value: any) => {
 
 /* 分页改变事件 */
 const paginationChange = (data: number) => {
-  pageIndex.value = data
+  pageNo.value = data
   request()
 }
 
@@ -184,13 +184,12 @@ const tableDone = () => {
 const request = (where?: any) => {
   if (typeof props.tableConfig?.datasource === 'function') {
     loading.value = true
-    if (where && where.pageIndex) {
-      // ToDo: pageIndex => pageNo
-      pageIndex.value = where.pageIndex
+    if (where && where.pageNo) {
+      pageNo.value = where.pageNo
     }
     props.tableConfig
       .datasource({
-        pageIndex: pageIndex.value,
+        pageNo: pageNo.value,
         pageSize: props.tableConfig.pageSize,
         ...where
       })
@@ -213,7 +212,11 @@ request()
 
 /* 更新选中值 */
 const updateModelValue = (value: any) => {
-  emits('update:modelValue', props.transformData ? props.transformData(value) : transformData(value))
+  if (value) {
+    emits('update:modelValue', props.transformData ? props.transformData(value) : transformData(value))
+  } else {
+    emits('update:modelValue', '')
+  }
 }
 
 /* 更新气泡位置 */
@@ -332,7 +335,7 @@ defineExpose({
         :pager-count="5"
         :page-size="tableConfig && tableConfig.pageSize"
         :total="tableTotal"
-        v-model:current-page="pageIndex"
+        v-model:current-page="pageNo"
         @current-change="paginationChange"
       />
     </div>

+ 1 - 1
src/components/FsTableSelect/props.ts

@@ -18,7 +18,7 @@ export interface TableSelectProps {
   // label 的属性名
   labelKey?: string
   // 回显数据,用于后端分页显示
-  initValue?: string | object
+  initValue?: any
   // 气泡位置
   placement?: PopoverProps['placement']
   // 占位符

+ 0 - 2
src/components/FsTableSelect/types/index.ts

@@ -4,8 +4,6 @@ export interface TableConfig {
   column?: Array<TableColumn>
   // 表格数据
   datasource: (...args: any) => Promise<any>
-  // 总数
-  total?: Number // ToDo: 表格分页时,total是否可以从datasource中获取
   // 每页显示条数
   pageSize?: Number
 }

+ 52 - 48
src/components/FsTour/index.vue

@@ -1,56 +1,17 @@
-<!-- 漫游式引导 -->
-<template>
-  <Teleport to="body">
-    <div :class="['tour', { 'show-mask': showMask }, { 'is-open': visible }]" :style="{ zIndex: zIndex }">
-      <div class="tour-box" :style="boxStyle"></div>
-      <div ref="triggerRef" class="tour-reference" :style="boxStyle"></div>
-      <el-tooltip
-        v-bind="tooltipProps"
-        ref="tooltipRef"
-        :virtualRef="triggerRef"
-        :virtualTriggering="true"
-        :disabled="!visible"
-      >
-        <template #content>
-          <div v-if="steps && step" class="popover-body">
-            <div v-if="step.title" class="tour-title">
-              <slot name="title" :step="step" :current="modelValue">
-                {{ step.title }}
-              </slot>
-            </div>
-            <div class="tour-text">
-              <slot name="text" :step="step" :current="modelValue">
-                {{ step.description }}
-              </slot>
-            </div>
-            <slot name="footer" :step="step" :current="modelValue">
-              <div class="tour-footer">
-                <div class="tour-counter">{{ (modelValue || 0) + 1 }}/{{ steps.length }}</div>
-                <div class="tour-action">
-                  <el-button v-if="!isLast" size="small" @click="onFinish"> 跳过 </el-button>
-                  <el-button v-if="modelValue !== 0" size="small" @click="onPrev"> 上一步 </el-button>
-                  <el-button v-if="!isLast" size="small" type="primary" @click="onNext"> 下一步 </el-button>
-                  <el-button v-if="isLast" size="small" type="primary" @click="onFinish"> 结束 </el-button>
-                </div>
-              </div>
-            </slot>
-          </div>
-        </template>
-      </el-tooltip>
-    </div>
-  </Teleport>
-</template>
-
 <script setup lang="ts">
 import type { Ref, CSSProperties } from 'vue'
 import type { ElTooltipProps, TooltipInstance } from 'element-plus'
 import { getOffset, getPopperProps, scrollIntoView } from './util'
 import type { TourStep } from './types'
-import { tourProps, tourEmits } from './props'
+import { tourEmits } from './props'
+import type { TourProps } from './props'
 
-const props = defineProps(tourProps)
+const props = withDefaults(defineProps<TourProps>(), {
+  mask: true,
+  padding: 6
+})
 
-const emit = defineEmits(tourEmits)
+const emits = defineEmits(tourEmits)
 
 /** 气泡触发组件 */
 const triggerRef = ref<any>(null)
@@ -130,7 +91,7 @@ const close = () => {
 
 /** 更新步骤值 */
 const updateModelValue = (value?: number | null) => {
-  emit('update:modelValue', value)
+  emits('update:modelValue', value)
 }
 
 /** 上一步 */
@@ -174,7 +135,49 @@ watch(
 )
 </script>
 
-<style scoped lang="scss">
+<template>
+  <Teleport to="body">
+    <div :class="['tour', { 'show-mask': showMask }, { 'is-open': visible }]" :style="{ zIndex: zIndex }">
+      <div class="tour-box" :style="boxStyle"></div>
+      <div ref="triggerRef" class="tour-reference" :style="boxStyle"></div>
+      <el-tooltip
+        v-bind="tooltipProps"
+        ref="tooltipRef"
+        :virtualRef="triggerRef"
+        :virtualTriggering="true"
+        :disabled="!visible"
+      >
+        <template #content>
+          <div v-if="steps && step" class="popover-body">
+            <div v-if="step.title" class="tour-title">
+              <slot name="title" :step="step" :current="modelValue">
+                {{ step.title }}
+              </slot>
+            </div>
+            <div class="tour-text">
+              <slot name="text" :step="step" :current="modelValue">
+                {{ step.description }}
+              </slot>
+            </div>
+            <slot name="footer" :step="step" :current="modelValue">
+              <div class="tour-footer">
+                <div class="tour-counter">{{ (modelValue || 0) + 1 }}/{{ steps.length }}</div>
+                <div class="tour-action">
+                  <el-button v-if="!isLast" size="small" @click="onFinish"> 跳过 </el-button>
+                  <el-button v-if="modelValue !== 0" size="small" @click="onPrev"> 上一步 </el-button>
+                  <el-button v-if="!isLast" size="small" type="primary" @click="onNext"> 下一步 </el-button>
+                  <el-button v-if="isLast" size="small" type="primary" @click="onFinish"> 结束 </el-button>
+                </div>
+              </div>
+            </slot>
+          </div>
+        </template>
+      </el-tooltip>
+    </div>
+  </Teleport>
+</template>
+
+<style lang="scss">
 .tour {
   position: fixed;
   top: 0;
@@ -254,6 +257,7 @@ watch(
 
 /* 气泡弹窗效果 */
 .tour-popover.tour-modal {
+  width: 500px;
   margin: 0 auto;
   left: 0 !important;
   top: 50% !important;

+ 6 - 20
src/components/FsTour/props.ts

@@ -1,33 +1,19 @@
 /* eslint-disable @typescript-eslint/no-unused-vars */
 import type { TourStep } from './types'
 
-/**
- * 属性
- */
-export const tourProps = {
+export interface TourProps {
   /** 当前处于第几步 */
-  modelValue: Number,
+  modelValue?: number
   /** 步骤 */
-  steps: {
-    type: Array as PropType<TourStep[]>,
-    required: true
-  },
+  steps?: TourStep[]
   /** 是否开启遮罩层 */
-  mask: {
-    type: Boolean,
-    default: true
-  },
+  mask?: boolean
   /** 高亮区内间距 */
-  padding: {
-    type: Number,
-    default: 6
-  },
+  padding?: number
   /** 层级 */
-  zIndex: Number
+  zIndex?: number
 }
 
-export type TourProps = ExtractPropTypes<typeof tourProps>
-
 /**
  * 事件
  */

+ 1 - 1
src/components/FsTour/util.ts

@@ -32,7 +32,7 @@ export function getPopperProps(visible?: boolean, modal?: boolean, props?: Popov
     trigger: 'click',
     placement: 'top',
     teleported: false,
-    transition: 'tour-fast',
+    transition: 'ele-tour-fast',
     persistent: false,
     effect: 'light',
     ...props,

+ 1 - 32
src/components/core/GlobalAside.vue

@@ -1,42 +1,11 @@
 <script lang="ts" setup>
-import { useMenuStore } from '@/stores/menu'
 import { useThemeStore } from '@/stores/theme'
-import config from '@/config/defaultSetting'
-import router from '@/router'
-
-const handleLogoClick = () => {
-  router.push({ name: config.homeRouteName })
-}
-
-const menuStore = useMenuStore()
-const collapse = computed(() => menuStore.collapse)
 
 const themeStore = useThemeStore()
-const themeStyle = computed(() => themeStore.themeStyle)
-const logoStyle = computed(() => {
-  if (themeStyle.value.name === 'nav-light') {
-    return {
-      color: themeStyle.value.textColor,
-      backgroundColor: themeStyle.value.bgColor
-    }
-  }
-  return {
-    color: '#fff',
-    backgroundColor: themeStyle.value.bgColor
-  }
-})
 </script>
 
 <template>
-  <div
-    class="logo flex items-center justify-center cursor-pointer px-10px"
-    :class="{ collapse: collapse }"
-    :style="logoStyle"
-    @click="handleLogoClick"
-  >
-    <img src="/logo.png" class="h-36px" />
-    <div class="ml-2 line2" :class="{ hidden: collapse }">{{ config.title }}</div>
-  </div>
+  <global-logo v-if="!themeStore.logoInHeader" />
   <global-menu />
 </template>
 

+ 1 - 0
src/components/core/GlobalHeader.vue

@@ -121,6 +121,7 @@ const handleSelect = (menu: any) => {
 
 <template>
   <header class="layout-header" :style="headerStyle">
+    <global-logo v-if="themeStore.logoInHeader" />
     <div class="flex flex-grow items-center justify-between px-4">
       <div class="flex flex-grow">
         <el-button link @click="handleToggle">

+ 54 - 0
src/components/core/GlobalLogo.vue

@@ -0,0 +1,54 @@
+<script setup lang="ts">
+import { useMenuStore } from '@/stores/menu'
+import { useThemeStore } from '@/stores/theme'
+import router from '@/router'
+import config from '@/config/defaultSetting'
+
+const menuStore = useMenuStore()
+
+const collapse = computed(() => menuStore.collapse)
+
+const themeStore = useThemeStore()
+
+const themeStyle = computed(() => themeStore.themeStyle)
+
+const logoStyle = computed(() => {
+  if (themeStore.logoInHeader) {
+    return {
+      color: '#000'
+    }
+  }
+  if (themeStyle.value.name === 'nav-light') {
+    return {
+      color: themeStyle.value.textColor,
+      backgroundColor: themeStyle.value.bgColor
+    }
+  }
+  return {
+    color: '#fff',
+    backgroundColor: themeStyle.value.bgColor
+  }
+})
+
+const handleLogoClick = () => {
+  router.push({ name: config.homeRouteName })
+}
+</script>
+
+<template>
+  <div
+    class="logo flex items-center justify-center cursor-pointer px-10px"
+    :class="{ collapse: !themeStore.logoInHeader && collapse }"
+    :style="logoStyle"
+    @click="handleLogoClick"
+  >
+    <img src="/logo.png" class="h-36px" />
+    <div class="ml-2 line2" :class="{ hidden: collapse }">{{ config.title }}</div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.collapse {
+  width: calc(var(--el-menu-icon-width) + var(--el-menu-base-level-padding) * 2);
+}
+</style>

+ 5 - 1
src/components/core/GlobalSetting.vue

@@ -100,10 +100,14 @@ const themeNav = computed(() => themeStore.themeNav)
       <div>显示标签页</div>
       <el-switch v-model="themeStore.showTabs"></el-switch>
     </div>
-    <div class="flex justify-between items-center">
+    <div class="flex justify-between items-center mb-4">
       <div>标签页持久化</div>
       <el-switch v-model="themeStore.keepAliveTabs"></el-switch>
     </div>
+    <div class="flex justify-between items-center">
+      <div>LOGO置于顶部</div>
+      <el-switch v-model="themeStore.logoInHeader"></el-switch>
+    </div>
   </el-drawer>
 </template>
 

+ 1 - 0
src/config/defaultSetting.ts

@@ -7,6 +7,7 @@ export default {
   uploadApi: '/file/upload', // 文件上传接口
   showTabs: true, // 显示标签页
   keepAliveTabs: true, // 标签页持久化
+  logoInHeader: true, // 头部logo
   oss: true, // 开启oss上传
   ossHost: 'https://fskj-res.oss-cn-zhangjiakou.aliyuncs.com/', // oss域名
   uploadSuccessCb: (res: any) => import.meta.env.VITE_BASE_PATH + res.data, // 文件上传成功回调

+ 10 - 6
src/hooks/useExcel.ts

@@ -80,7 +80,7 @@ export const useExcel = () => {
   }
 
   /**
-   * 导出excel ToDo:增加传入接口地址选项,如果存在接口地址,则直接请求接口
+   * 导出excel
    */
   const exportExcel = (options: IExcel) => {
     return new Promise((resolve, reject) => {
@@ -90,7 +90,6 @@ export const useExcel = () => {
         },
         options
       )
-
       const workbook = new ExcelJS.Workbook()
       const sheet = workbook.addWorksheet('Sheet1')
       if (!(Array.isArray(options.rows) && Array.isArray(options.rows[0]))) {
@@ -164,14 +163,19 @@ export const useExcel = () => {
   }
 
   /**
-   * 下载文件  ToDo:如果传入是http地址,则直接下载
-   * @param data 二进制数据
+   * 下载文件
+   * @param data 二进制数据,如果传入是http地址,则直接下载
    * @param name 文件名
    * @param type 文件类型
    */
   const download = (data: Blob | ArrayBuffer | string, name: string, type?: string) => {
-    const blob = new Blob([data], { type: type || 'application/octet-stream' })
-    const url = window.URL.createObjectURL(blob)
+    let url = ''
+    if (typeof data === 'string') {
+      url = data
+    } else {
+      const blob = new Blob([data], { type: type || 'application/octet-stream' })
+      url = window.URL.createObjectURL(blob)
+    }
     const a = document.createElement('a')
     a.href = url
     a.download = name

+ 26 - 21
src/layouts/BasicLayout.vue

@@ -16,30 +16,35 @@ const themeStore = useThemeStore()
     </router-view>
   </div>
   <el-container class="layout-container" v-else>
-    <el-aside>
-      <global-aside />
-    </el-aside>
+    <el-header v-if="themeStore.logoInHeader">
+      <global-header />
+    </el-header>
     <el-container>
-      <el-header>
-        <global-header />
-      </el-header>
-      <el-main style="padding: 0">
-        <global-tabs v-if="themeStore.showTabs"></global-tabs>
-        <div
-          class="overflow-auto"
-          style="padding: var(--main-padding)"
-          :style="{ height: themeStore.showTabs ? 'calc(100% - 41px)' : '100%' }"
-        >
-          <router-view v-slot="{ Component }">
-            <keep-alive :include="themeStore.keepAliveTabs ? routerStore.keepAliveRouter : []">
-              <component :is="Component" />
-            </keep-alive>
-          </router-view>
-        </div>
-      </el-main>
-      <!-- <el-footer>
+      <el-aside>
+        <global-aside />
+      </el-aside>
+      <el-container>
+        <el-header v-if="!themeStore.logoInHeader">
+          <global-header />
+        </el-header>
+        <el-main style="padding: 0">
+          <global-tabs v-if="themeStore.showTabs"></global-tabs>
+          <div
+            class="overflow-auto"
+            style="padding: var(--main-padding)"
+            :style="{ height: themeStore.showTabs ? 'calc(100% - 41px)' : '100%' }"
+          >
+            <router-view v-slot="{ Component }">
+              <keep-alive :include="themeStore.keepAliveTabs ? routerStore.keepAliveRouter : []">
+                <component :is="Component" />
+              </keep-alive>
+            </router-view>
+          </div>
+        </el-main>
+        <!-- <el-footer>
         <global-footer />
       </el-footer> -->
+      </el-container>
     </el-container>
   </el-container>
 </template>

+ 79 - 72
src/router/asyncRouter.ts

@@ -10,78 +10,6 @@ const asyncRouter: RouteRecordRaw[] = [
       icon: 'House'
     }
   },
-  {
-    path: '/tableSelect',
-    name: 'tableSelect',
-    component: () => import('@/views/tableSelect/index.vue'),
-    meta: {
-      title: '下拉表格',
-      icon: 'MoreFilled'
-    }
-  },
-  {
-    path: '/imageUpload',
-    name: 'imageUpload',
-    component: () => import('@/views/imageUpload/index.vue'),
-    meta: {
-      title: '图片上传',
-      icon: 'MoreFilled'
-    }
-  },
-  {
-    path: '/splitePanel',
-    name: 'splitePanel',
-    component: () => import('@/views/splitePanel/index.vue'),
-    meta: {
-      title: '分割面板',
-      icon: 'MoreFilled'
-    }
-  },
-  {
-    path: '/excel',
-    name: 'excel',
-    component: () => import('@/views/excel/index.vue'),
-    meta: {
-      title: '导入导出Excel',
-      icon: 'MoreFilled'
-    }
-  },
-  {
-    path: '/checkCard',
-    name: 'checkCard',
-    component: () => import('@/views/checkCard/index.vue'),
-    meta: {
-      title: '可选卡片',
-      icon: 'MoreFilled'
-    }
-  },
-  {
-    path: '/printer',
-    name: 'printer',
-    component: () => import('@/views/printer/index.vue'),
-    meta: {
-      title: '打印组件',
-      icon: 'MoreFilled'
-    }
-  },
-  {
-    path: '/tour',
-    name: 'tour',
-    component: () => import('@/views/tour/index.vue'),
-    meta: {
-      title: '引导组件',
-      icon: 'MoreFilled'
-    }
-  },
-  {
-    path: '/stausText',
-    name: 'stausText',
-    component: () => import('@/views/stausText/index.vue'),
-    meta: {
-      title: '文本组件',
-      icon: 'MoreFilled'
-    }
-  },
   // -- APPEND HERE --
   {
     path: 'https://jijian.sxidc.com/',
@@ -349,6 +277,85 @@ const asyncRouter: RouteRecordRaw[] = [
       }
     ]
   },
+  {
+    path: '/extension',
+    name: 'extension',
+    meta: { title: '扩展组件', icon: 'StarFilled' },
+    children: [
+      {
+        path: '/extension/tableSelect',
+        name: 'tableSelect',
+        component: () => import('@/views/extension/tableSelect/index.vue'),
+        meta: {
+          title: '下拉表格',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/imageUpload',
+        name: 'imageUpload',
+        component: () => import('@/views/extension/imageUpload/index.vue'),
+        meta: {
+          title: '图片上传',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/splitePanel',
+        name: 'splitePanel',
+        component: () => import('@/views/extension/splitePanel/index.vue'),
+        meta: {
+          title: '分割面板',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/excel',
+        name: 'excel',
+        component: () => import('@/views/extension/excel/index.vue'),
+        meta: {
+          title: '导入导出Excel',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/checkCard',
+        name: 'checkCard',
+        component: () => import('@/views/extension/checkCard/index.vue'),
+        meta: {
+          title: '可选卡片',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/printer',
+        name: 'printer',
+        component: () => import('@/views/extension/printer/index.vue'),
+        meta: {
+          title: '打印组件',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/tour',
+        name: 'tour',
+        component: () => import('@/views/extension/tour/index.vue'),
+        meta: {
+          title: '引导组件',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/stausText',
+        name: 'stausText',
+        component: () => import('@/views/extension/stausText/index.vue'),
+        meta: {
+          title: '文本组件',
+          icon: 'MoreFilled'
+        }
+      }
+    ]
+  },
   {
     path: '/iframe',
     name: 'iframe',

+ 3 - 1
src/stores/theme.ts

@@ -45,6 +45,7 @@ export const useThemeStore = defineStore('theme', () => {
   // 界面显示
   const showTabs = useStorage('showTabs', config.showTabs)
   const keepAliveTabs = useStorage('keepAliveTabs', config.keepAliveTabs)
+  const logoInHeader = useStorage('logoInHeader', config.logoInHeader)
 
   const initTheme = () => {
     initThemeColor()
@@ -62,6 +63,7 @@ export const useThemeStore = defineStore('theme', () => {
     setThemeNav,
     initTheme,
     showTabs,
-    keepAliveTabs
+    keepAliveTabs,
+    logoInHeader
   }
 })

+ 3 - 0
src/utils/constants.ts

@@ -80,5 +80,8 @@ export const themeNavList = [
   }
 ]
 
+// logo 是否置于顶栏
+export const logoInHeader = true
+
 export const ACCESS_TOKEN = 'Authorization'
 export const TOKEN_PREFIX = 'Bearer '

+ 3 - 4
src/views/checkCard/index.vue → src/views/extension/checkCard/index.vue

@@ -4,9 +4,9 @@ import type { CheckCardItem } from '@/components/FsCheckCard/types'
 
 const select = ref('React')
 
-const select2 = ref([])
+const select2 = ref('')
 
-const select3 = ref(['编码'])
+const select3 = ref('编码')
 
 const select4 = ref('编码')
 
@@ -41,8 +41,7 @@ const items = ref<CheckCardItem[]>([
     label: 'React',
     col: { span: 6 },
     src: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
-    content: 'React 用于构建 Web 和原生交互界面的库',
-    disabled: true
+    content: 'React 用于构建 Web 和原生交互界面的库'
   },
   {
     value: 'Angular',

+ 0 - 0
src/views/excel/export.vue → src/views/extension/excel/export.vue


+ 0 - 0
src/views/excel/import.vue → src/views/extension/excel/import.vue


+ 0 - 0
src/views/excel/index.vue → src/views/extension/excel/index.vue


+ 16 - 11
src/views/imageUpload/index.vue → src/views/extension/imageUpload/index.vue

@@ -1,13 +1,14 @@
 <script setup lang="ts">
 import { ElMessage } from 'element-plus'
-import ImageUpload from '@/components/FsImageUpload/index.vue'
-const images = ref([])
+import FsImageUpload from '@/components/FsImageUpload/index.vue'
+import config from '@/config/defaultSetting'
+const images = ref('')
 // 手动上传数据
-const images2 = ref([])
-const images3 = ref([])
+const images2 = ref('')
+const images3 = ref('')
 const imageUploadRef = ref()
 
-const isReload = ref(false)
+const disabled = ref(false)
 
 const onRemove = (file: any) => {
   console.log(file)
@@ -41,27 +42,31 @@ const submit = () => {
 
 <template>
   <el-card header="基础示例" shadow="never">
-    <ImageUpload
+    <fs-image-upload
       v-model="images"
-      :readonly="isReload"
       :limit="5"
       :uploadFunction="uploadFunction"
+      :disabled="disabled"
+      :action="config.uploadApi"
       drag
       @remove="onRemove"
     />
     <div class="flex items-center mt-2">
-      <div>是否只读:</div>
-      <el-radio-group v-model="isReload">
+      <div>是否禁用:</div>
+      <el-radio-group v-model="disabled">
         <el-radio label="是" :value="true"> </el-radio>
         <el-radio label="否" :value="false"> </el-radio>
       </el-radio-group>
     </div>
+    <div>
+      {{ images }}
+    </div>
   </el-card>
   <el-card header="支持多选" shadow="never" class="mt-3">
-    <ImageUpload v-model="images2" multiple :limit="5" :uploadFunction="uploadFunction" @remove="onRemove" />
+    <fs-image-upload v-model="images2" multiple :limit="5" :uploadFunction="uploadFunction" @remove="onRemove" />
   </el-card>
   <el-card header="基础示例" shadow="never" class="mt-3">
-    <ImageUpload
+    <fs-image-upload
       v-model="images3"
       :auto-upload="false"
       :limit="5"

+ 0 - 0
src/views/printer/index.vue → src/views/extension/printer/index.vue


+ 0 - 0
src/views/printer/printContract.vue → src/views/extension/printer/printContract.vue


+ 0 - 0
src/views/splitePanel/index.vue → src/views/extension/splitePanel/index.vue


+ 0 - 0
src/views/stausText/index.vue → src/views/extension/stausText/index.vue


+ 23 - 8
src/views/tableSelect/base.vue → src/views/extension/tableSelect/base.vue

@@ -6,16 +6,16 @@ const data = ref('')
 
 const datas = ref('')
 
-const request = function ({ pageIndex }: any) {
+const request = function ({ pageNo }: any) {
   return new Promise(resolve => {
     setTimeout(() => {
       const data = {
         list: [
-          { name: '张三' + pageIndex, sex: '男', id: '' + pageIndex + 1 },
-          { name: '李四' + pageIndex, sex: '女', id: '' + pageIndex + 2 },
-          { name: '王五' + pageIndex, sex: '男', id: '' + pageIndex + 3 },
-          { name: '赵六' + pageIndex, sex: '女', id: '' + pageIndex + 4 },
-          { name: '张三' + pageIndex, sex: '男', id: '' + pageIndex + 5 }
+          { name: '张三' + pageNo, sex: '男', id: '' + pageNo + 1 },
+          { name: '李四' + pageNo, sex: '女', id: '' + pageNo + 2 },
+          { name: '王五' + pageNo, sex: '男', id: '' + pageNo + 3 },
+          { name: '赵六' + pageNo, sex: '女', id: '' + pageNo + 4 },
+          { name: '张三' + pageNo, sex: '男', id: '' + pageNo + 5 }
         ],
         total: 10
       }
@@ -46,6 +46,15 @@ const tableConfig: TableConfig = {
   datasource: request,
   pageSize: 5
 }
+
+// 自定义数据格式
+const transformData = (data: any) => {
+  return data.id
+}
+
+const transformData2 = (data: any) => {
+  return data.map((item: any) => item.id)
+}
 </script>
 
 <template>
@@ -53,7 +62,7 @@ const tableConfig: TableConfig = {
     <el-row :gutter="15">
       <el-col :span="6">
         <el-form-item label="单选">
-          <fs-table-select v-model="data" :tableConfig="tableConfig" automaticDropdown>
+          <fs-table-select v-model="data" :tableConfig="tableConfig" :transform-data="transformData">
             <template #sex="{ row }">
               <el-tag size="small">{{ row.sex }}</el-tag>
             </template>
@@ -63,7 +72,13 @@ const tableConfig: TableConfig = {
       </el-col>
       <el-col :span="6">
         <el-form-item label="多选">
-          <fs-table-select v-model="datas" :tableConfig="tableConfig" multiple tagType="success">
+          <fs-table-select
+            v-model="datas"
+            :tableConfig="tableConfig"
+            multiple
+            tagType="success"
+            :transform-data="transformData2"
+          >
             <template #sex="{ row }">
               <el-tag size="small">{{ row.sex }}</el-tag>
             </template>

+ 12 - 10
src/views/tableSelect/disabled.vue → src/views/extension/tableSelect/disabled.vue

@@ -4,20 +4,23 @@ import type { TableConfig } from '@/components/FsTableSelect/types'
 
 const data = ref('')
 
-const datas = ref([])
+const datas = ref('')
 
 const isDisabled = ref(true)
 
-const request = function ({ pageIndex }: any) {
+const request = function ({ pageNo }: any) {
   return new Promise(resolve => {
     setTimeout(() => {
-      const data = [
-        { name: '张三' + pageIndex, sex: '男', id: '' + pageIndex + 1 },
-        { name: '李四' + pageIndex, sex: '女', id: '' + pageIndex + 2 },
-        { name: '王五' + pageIndex, sex: '男', id: '' + pageIndex + 3 },
-        { name: '赵六' + pageIndex, sex: '女', id: '' + pageIndex + 4 },
-        { name: '张三' + pageIndex, sex: '男', id: '' + pageIndex + 5 }
-      ]
+      const data = {
+        list: [
+          { name: '张三' + pageNo, sex: '男', id: '' + pageNo + 1 },
+          { name: '李四' + pageNo, sex: '女', id: '' + pageNo + 2 },
+          { name: '王五' + pageNo, sex: '男', id: '' + pageNo + 3 },
+          { name: '赵六' + pageNo, sex: '女', id: '' + pageNo + 4 },
+          { name: '张三' + pageNo, sex: '男', id: '' + pageNo + 5 }
+        ],
+        total: 10
+      }
       resolve(data)
     }, 1500)
   })
@@ -43,7 +46,6 @@ const tableConfig: TableConfig = {
     }
   ],
   datasource: request,
-  total: 100,
   pageSize: 5
 }
 </script>

+ 0 - 0
src/views/tableSelect/index.vue → src/views/extension/tableSelect/index.vue


+ 8 - 9
src/views/tableSelect/search.vue → src/views/extension/tableSelect/search.vue

@@ -10,17 +10,17 @@ const initValue2 = ref<Array<any>>([])
 
 const current = ref(null)
 
-const request = function ({ pageIndex }: any) {
+const request = function ({ pageNo }: any) {
   return new Promise(resolve => {
     setTimeout(() => {
       const data = {
         total: 20,
         list: [
-          { name: '张三' + pageIndex, sex: '男', id: '' + pageIndex + 1 },
-          { name: '李四' + pageIndex, sex: '女', id: '' + pageIndex + 2 },
-          { name: '王五' + pageIndex, sex: '男', id: '' + pageIndex + 3 },
-          { name: '赵六' + pageIndex, sex: '女', id: '' + pageIndex + 4 },
-          { name: '张三' + pageIndex, sex: '男', id: '' + pageIndex + 5 }
+          { name: '张三' + pageNo, sex: '男', id: '' + pageNo + 1 },
+          { name: '李四' + pageNo, sex: '女', id: '' + pageNo + 2 },
+          { name: '王五' + pageNo, sex: '男', id: '' + pageNo + 3 },
+          { name: '赵六' + pageNo, sex: '女', id: '' + pageNo + 4 },
+          { name: '张三' + pageNo, sex: '男', id: '' + pageNo + 5 }
         ]
       }
       resolve(data)
@@ -48,7 +48,6 @@ const tableConfig: TableConfig = {
     }
   ],
   datasource: request,
-  total: 100,
   pageSize: 5
 }
 
@@ -72,10 +71,10 @@ const form = ref({
 })
 
 const search = () => {
-  tableSelectRef.value.reload({ pageIndex: 1, ...form.value })
+  tableSelectRef.value.reload({ pageNo: 1, ...form.value })
 }
 const search2 = () => {
-  tableSelectRef2.value.reload({ pageIndex: 1, ...form.value })
+  tableSelectRef2.value.reload({ pageNo: 1, ...form.value })
 }
 
 const setInitValue = () => {

+ 142 - 0
src/views/extension/tour/index.vue

@@ -0,0 +1,142 @@
+<script setup lang="ts">
+import FsTour from '@/components/FsTour/index.vue'
+import type { TourStep } from '@/components/FsTour/types'
+import type { ElButton } from 'element-plus'
+const current = ref()
+const uploadRef1 = ref<InstanceType<typeof ElButton>>()
+const saveRef1 = ref<InstanceType<typeof ElButton>>()
+const moreRef1 = ref<InstanceType<typeof ElButton>>()
+
+/** 步骤 */
+const steps = ref<TourStep[]>([
+  {
+    target: () => uploadRef1.value?.$el,
+    title: '修改数据',
+    description: '点击这个按钮在弹出框中选择想要修改的数据即可.'
+  },
+  {
+    target: () => saveRef1.value?.$el,
+    title: '保存数据',
+    description: '数据录入完成后点击这个按钮即可提交数据到后台.'
+  },
+  {
+    target: () => moreRef1.value?.$el,
+    title: '如何进行更多的操作',
+    description: '鼠标移入到此按钮上即可展示出更多的操作功能.'
+  }
+])
+
+const current2 = ref()
+const uploadRef2 = ref<InstanceType<typeof ElButton>>()
+const saveRef2 = ref<InstanceType<typeof ElButton>>()
+const moreRef2 = ref<InstanceType<typeof ElButton>>()
+/** 步骤 */
+const steps2 = ref<TourStep[]>([
+  {
+    target: () => uploadRef2.value?.$el,
+    title: '修改数据',
+    description: '点击这个按钮在弹出框中选择想要修改的数据即可.'
+  },
+  {
+    target: () => saveRef2.value?.$el,
+    title: '保存数据',
+    description: '数据录入完成后点击这个按钮即可提交数据到后台.'
+  },
+  {
+    target: () => moreRef2.value?.$el,
+    title: '如何进行更多的操作',
+    description: '鼠标移入到此按钮上即可展示出更多的操作功能.'
+  }
+])
+
+const current3 = ref()
+const uploadRef3 = ref<InstanceType<typeof ElButton>>()
+const saveRef3 = ref<InstanceType<typeof ElButton>>()
+const moreRef3 = ref<InstanceType<typeof ElButton>>()
+const steps3 = ref<TourStep[]>([
+  {
+    title: '欢迎使用 xxxx管理系统 系统',
+    description: '下面将为您介绍一些常用功能的操作说明, 如果之前已经为您介绍过, 您可以直接点击跳过结束指引.'
+  },
+  {
+    target: () => uploadRef3.value?.$el,
+    title: '如何进行文件上传',
+    description: '点击这个按钮在弹出框中选择想要上传的文件即可.'
+  },
+  {
+    target: () => saveRef3.value?.$el,
+    title: '如何提交数据',
+    description: '数据录入完成后点击这个按钮即可提交数据到后台.',
+    mask: false
+  },
+  {
+    target: () => moreRef3.value?.$el,
+    title: '如何进行更多的操作',
+    description: '鼠标移入到此按钮上即可展示出更多的操作功能.'
+  }
+])
+
+const onStart1 = () => {
+  current.value = 0
+}
+
+const onStart2 = () => {
+  current2.value = 0
+}
+
+const onStart3 = () => {
+  current3.value = 0
+}
+</script>
+
+<template>
+  <el-card header="基础用法" shadow="never">
+    <div>
+      <el-button type="primary" @click="onStart1">开始引导</el-button>
+    </div>
+    <div style="margin-top: 20px">
+      <el-button ref="uploadRef1">修改</el-button>
+      <el-button ref="saveRef1" type="primary">保存</el-button>
+      <el-button ref="moreRef1">更多</el-button>
+    </div>
+    <fs-tour v-model="current" :steps="steps" />
+  </el-card>
+
+  <el-card header="不带遮罩层" shadow="never" class="mt-3">
+    <div>
+      <el-button type="primary" @click="onStart2">开始引导</el-button>
+    </div>
+    <div style="margin-top: 20px">
+      <el-button ref="uploadRef2">修改</el-button>
+      <el-button ref="saveRef2" type="primary">保存</el-button>
+      <el-button ref="moreRef2">更多</el-button>
+    </div>
+    <fs-tour v-model="current2" :steps="steps2" :mask="false" />
+  </el-card>
+
+  <el-card header="混合弹窗等多种形式" shadow="never" class="mt-3">
+    <div>
+      <el-button type="primary" @click="onStart3">开始引导</el-button>
+    </div>
+    <div style="margin-top: 20px">
+      <el-button ref="uploadRef3">Upload</el-button>
+      <el-button ref="saveRef3" type="primary">Save</el-button>
+      <el-button ref="moreRef3">More</el-button>
+    </div>
+    <fs-tour v-model="current3" :steps="steps3">
+      <template #text="{ step, current }">
+        <template v-if="current === 0">
+          <div style="margin-bottom: 10px">
+            <img
+              src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*P0S-QIRUbsUAAAAAAAAAAABkARQnAQ"
+              style="height: 184px; width: 100%; object-fit: cover"
+            />
+          </div>
+          <div>{{ step.description }}</div>
+        </template>
+      </template>
+    </fs-tour>
+  </el-card>
+</template>
+
+<style scoped lang="scss"></style>

+ 0 - 49
src/views/tour/index.vue

@@ -1,49 +0,0 @@
-<script setup lang="ts">
-import FsTour from '@/components/FsTour/index.vue'
-import type { TourStep } from '@/components/FsTour/types'
-import type { ElButton } from 'element-plus'
-const current = ref()
-const uploadRef1 = ref<InstanceType<typeof ElButton>>()
-const saveRef1 = ref<InstanceType<typeof ElButton>>()
-const moreRef1 = ref<InstanceType<typeof ElButton>>()
-
-/** 步骤 */
-const steps = ref<TourStep[]>([
-  {
-    target: () => uploadRef1.value?.$el,
-    title: '修改数据',
-    description: '点击这个按钮在弹出框中选择想要修改的数据即可.'
-  },
-  {
-    target: () => saveRef1.value?.$el,
-    title: '保存数据',
-    description: '数据录入完成后点击这个按钮即可提交数据到后台.'
-  },
-  {
-    target: () => moreRef1.value?.$el,
-    title: '如何进行更多的操作',
-    description: '鼠标移入到此按钮上即可展示出更多的操作功能.'
-  }
-])
-
-/** 开始引导 */
-const onStart1 = () => {
-  current.value = 0
-}
-</script>
-
-<template>
-  <el-card header="基础用法" shadow="never">
-    <div>
-      <el-button type="primary" @click="onStart1">开始引导</el-button>
-    </div>
-    <div style="margin-top: 20px">
-      <el-button ref="uploadRef1">Upload</el-button>
-      <el-button ref="saveRef1" type="primary">Save</el-button>
-      <el-button ref="moreRef1">More</el-button>
-    </div>
-    <fs-tour v-model="current" :steps="steps" />
-  </el-card>
-</template>
-
-<style scoped lang="scss"></style>