ソースを参照

抽离@fskj-admin/core依赖包

tongshangming 1 年間 前
コミット
9955384a47

+ 1 - 0
package.json

@@ -14,6 +14,7 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.1.0",
+    "@fskj-admin/core": "^0.0.7",
     "@icon-park/vue-next": "^1.4.2",
     "@vueuse/core": "^10.6.1",
     "@wangeditor/editor": "^5.1.23",

+ 14 - 29
plop-template/view.hbs

@@ -1,35 +1,20 @@
-<script setup lang="ts">
-import { use{{pascalCase name}}Service } from '@/domain/{{name}}/service'
-// import { {{pascalCase name}}Entity } from '@/domain/{{name}}/entity'
-import type { BasicForm, ICRUD } from '@/types/form'
-
-const {{camelCase name}}Service = use{{pascalCase name}}Service()
-// const {{camelCase name}}Entity = ref<{{pascalCase name}}Entity>(new {{pascalCase name}}Entity())
-
-const CRUD: ICRUD = {
-  create(data: any) {
-    return {{camelCase name}}Service.create(data)
-  },
-  update(data: any) {
-    return {{camelCase name}}Service.update(data)
-  },
-  getList(data: any) {
-    return {{camelCase name}}Service.getList(data)
-  },
-  delete(data: any) {
-    return {{camelCase name}}Service.del(data)
-  }
-}
-
-const formConfig = reactive<BasicForm>({
-  formItems: []
-})
+<script setup lang='ts'>
+  import { use{{pascalCase name}}Service } from '@/domains/{{name}}/service' // import {
+  {{pascalCase name}}Entity } from '@/domains/{{name}}/entity' import type { BasicForm, ICRUD } from '@/types/form'
+  const
+  {{camelCase name}}Service = use{{pascalCase name}}Service() // const
+  {{camelCase name}}Entity = ref<{{pascalCase name}}Entity>(new
+  {{pascalCase name}}Entity()) const CRUD: ICRUD = { create(data: any) { return
+  {{camelCase name}}Service.create(data) }, update(data: any) { return
+  {{camelCase name}}Service.update(data) }, getList(data: any) { return
+  {{camelCase name}}Service.getList(data) }, delete(data: any) { return
+  {{camelCase name}}Service.del(data) } } const formConfig = reactive<BasicForm>({ formItems: [] })
 </script>
 
 <template>
-  <pro-table :crud="CRUD" :formConfig="formConfig">
-    <vxe-column field="" title=""></vxe-column>
+  <pro-table :crud='CRUD' :formConfig='formConfig'>
+    <vxe-column field='' title=''></vxe-column>
   </pro-table>
 </template>
 
-<style lang="scss" scoped></style>
+<style lang='scss' scoped></style>

+ 21 - 5
pnpm-lock.yaml

@@ -8,6 +8,9 @@ dependencies:
   '@element-plus/icons-vue':
     specifier: ^2.1.0
     version: 2.1.0(vue@3.3.8)
+  '@fskj-admin/core':
+    specifier: ^0.0.7
+    version: 0.0.7
   '@icon-park/vue-next':
     specifier: ^1.4.2
     version: 1.4.2(vue@3.3.8)
@@ -114,7 +117,7 @@ devDependencies:
     version: 5.3.2
   unocss:
     specifier: ^0.57.7
-    version: 0.57.7(postcss@8.4.31)(vite@5.0.2)
+    version: 0.57.7(postcss@8.4.32)(vite@5.0.2)
   unplugin-auto-import:
     specifier: ^0.16.7
     version: 0.16.7(@vueuse/core@10.6.1)
@@ -809,6 +812,10 @@ packages:
     resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
     dev: false
 
+  /@fskj-admin/core@0.0.7:
+    resolution: {integrity: sha512-1TsNxDmvmobCJGGk8KeT72CAbh0a1MVXdGR10JGNAWSYLnpf99ohC1ONTvLzMuy1J6GE3uYzhXq3TzxEDMpJWg==}
+    dev: false
+
   /@humanwhocodes/config-array@0.11.13:
     resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
     engines: {node: '>=10.10.0'}
@@ -1358,7 +1365,7 @@ packages:
       sirv: 2.0.3
     dev: true
 
-  /@unocss/postcss@0.57.7(postcss@8.4.31):
+  /@unocss/postcss@0.57.7(postcss@8.4.32):
     resolution: {integrity: sha512-13c9p5ecTvYa6inDky++8dlVuxQ0JuKaKW5A0NW3XuJ3Uz1t8Pguji+NAUddfTYEFF6GHu47L3Aac7vpI8pMcQ==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -1370,7 +1377,7 @@ packages:
       css-tree: 2.3.1
       fast-glob: 3.3.2
       magic-string: 0.30.5
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: true
 
   /@unocss/preset-attributify@0.57.7:
@@ -5370,6 +5377,15 @@ packages:
       picocolors: 1.0.0
       source-map-js: 1.0.2
 
+  /postcss@8.4.32:
+    resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
+    engines: {node: ^10 || ^12 || >=14}
+    dependencies:
+      nanoid: 3.3.7
+      picocolors: 1.0.0
+      source-map-js: 1.0.2
+    dev: true
+
   /posthtml-parser@0.2.1:
     resolution: {integrity: sha512-nPC53YMqJnc/+1x4fRYFfm81KV2V+G9NZY+hTohpYg64Ay7NemWWcV4UWuy/SgMupqQ3kJ88M/iRfZmSnxT+pw==}
     dependencies:
@@ -6371,7 +6387,7 @@ packages:
     engines: {node: '>= 10.0.0'}
     dev: true
 
-  /unocss@0.57.7(postcss@8.4.31)(vite@5.0.2):
+  /unocss@0.57.7(postcss@8.4.32)(vite@5.0.2):
     resolution: {integrity: sha512-Z99ZZPkbkjIUXEM7L+K/7Y5V5yqUS0VigG7ZIFzLf/npieKmXHKlrPyvQWFQaf3OqooMFuKBQivh75TwvSOkcQ==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -6387,7 +6403,7 @@ packages:
       '@unocss/cli': 0.57.7
       '@unocss/core': 0.57.7
       '@unocss/extractor-arbitrary-variants': 0.57.7
-      '@unocss/postcss': 0.57.7(postcss@8.4.31)
+      '@unocss/postcss': 0.57.7(postcss@8.4.32)
       '@unocss/preset-attributify': 0.57.7
       '@unocss/preset-icons': 0.57.7
       '@unocss/preset-mini': 0.57.7

+ 0 - 18
src/components.d.ts

@@ -7,27 +7,12 @@ export {}
 
 declare module 'vue' {
   export interface GlobalComponents {
-    BasicForm: typeof import('./components/core/form/BasicForm.vue')['default']
     Cropper: typeof import('./components/avatar/cropper.vue')['default']
-    DialogForm: typeof import('./components/core/form/DialogForm.vue')['default']
-    DrawerForm: typeof import('./components/core/form/DrawerForm.vue')['default']
     ElArea: typeof import('./components/form/ElArea.vue')['default']
-    ElCustom: typeof import('./components/form/ElCustom.vue')['default']
     ElDict: typeof import('./components/form/ElDict.vue')['default']
     ElEditor: typeof import('./components/form/ElEditor.vue')['default']
-    ElEditTag: typeof import('./components/form/ElEditTag.vue')['default']
     ElEmployees: typeof import('./components/form/ElEmployees.vue')['default']
-    ElFileUpload: typeof import('./components/form/ElFileUpload.vue')['default']
-    ElFormCard: typeof import('./components/form/ElFormCard.vue')['default']
-    ElFormGroup: typeof import('./components/form/ElFormGroup.vue')['default']
-    ElFormLayout: typeof import('./components/form/ElFormLayout.vue')['default']
-    ElFormSteps: typeof import('./components/form/ElFormSteps.vue')['default']
-    ElFormTable: typeof import('./components/form/ElFormTable.vue')['default']
-    ElFormTabs: typeof import('./components/form/ElFormTabs.vue')['default']
-    ElImageUpload: typeof import('./components/form/ElImageUpload.vue')['default']
-    ElSubForm: typeof import('./components/form/ElSubForm.vue')['default']
     Exception: typeof import('./components/Exception.vue')['default']
-    FormComp: typeof import('./components/core/form/FormComp.vue')['default']
     GlobalAside: typeof import('./components/core/GlobalAside.vue')['default']
     GlobalFooter: typeof import('./components/core/GlobalFooter.vue')['default']
     GlobalHeader: typeof import('./components/core/GlobalHeader.vue')['default']
@@ -39,9 +24,6 @@ declare module 'vue' {
     OrgLayout: typeof import('./components/org/OrgLayout.vue')['default']
     OrgList: typeof import('./components/org/OrgList.vue')['default']
     PaneModel: typeof import('./components/splitpanes/PaneModel.vue')['default']
-    ProCardList: typeof import('./components/core/ProCardList.vue')['default']
-    ProForm: typeof import('./components/core/form/ProForm.vue')['default']
-    ProTable: typeof import('./components/core/ProTable.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     SelIcon: typeof import('./components/SelIcon.vue')['default']

+ 0 - 168
src/components/core/ProCardList.vue

@@ -1,168 +0,0 @@
-<script setup lang="ts">
-defineOptions({
-  inheritAttrs: false
-})
-
-import { useForm } from '@/hooks/useForm'
-import { useTableQuery } from '@/hooks/useTableQuery'
-import { useTable } from '@/hooks/useTable'
-import type { DialogProps, DrawerProps } from 'element-plus'
-import type { BasicForm, ICRUD } from '@/types/form'
-
-interface Props {
-  crud: ICRUD
-  pageSize?: number
-  formConfig: BasicForm
-  dialogConfig?: DialogProps
-  drawerConfig?: Partial<DrawerProps>
-  height?: string
-  cardHeight?: string
-  showAdd?: boolean
-  span?: number
-  formMode?: 'dialog' | 'drawer'
-  beforeCreate?: Function
-}
-
-const props = withDefaults(defineProps<Props>(), {
-  pageSize: 12,
-  showAdd: true,
-  cardHeight: '206px',
-  span: 4,
-  formMode: 'dialog'
-})
-
-const emits = defineEmits(['click-create', 'click-edit'])
-
-// ============== 查询部分开始 ===============
-const { query, searchList, handleQuery, handleReset } = useTableQuery(props, emits)
-// ============== 查询部分结束 ===============
-
-// ============== 表格部分开始 ===============
-const {
-  tableData,
-  formData,
-  loading,
-  total,
-  curPage,
-  formVisible,
-  refresh,
-  handleCreate,
-  handleDelete,
-  handleUpdate,
-  handleView,
-  handleFormSuccess
-} = useTable(props, emits)
-
-// ============== 表格部分结束 ===============
-
-// 构造表单插槽
-const { formSlots } = useForm(props)
-
-defineExpose({
-  handleCreate,
-  handleDelete,
-  handleUpdate,
-  handleView,
-  refresh
-})
-</script>
-
-<template>
-  <div class="flex flex-col" :style="{ height: height || 'calc(100vh - 101px - var(--main-padding) * 2)' }">
-    <el-card class="mb-4" shadow="never" v-if="searchList.length">
-      <el-form :inline="true">
-        <el-form-item :label="item.label" v-for="(item, index) in searchList" :key="index">
-          <form-comp :item="item" v-model="query[item.name]"></form-comp>
-        </el-form-item>
-        <slot name="query" :query="query"></slot>
-        <el-form-item>
-          <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
-          <el-button icon="Refresh" @click="handleReset">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </el-card>
-
-    <el-card class="h-full flex-grow-1" :body-style="{ height: '100%' }" shadow="never">
-      <div class="flex flex-col h-full">
-        <slot name="header"></slot>
-
-        <div class="h-full flex-grow" v-loading="loading">
-          <el-row :gutter="10">
-            <el-col :span="span" v-bind="$attrs" v-if="showAdd" class="mb-10">
-              <div
-                class="flex items-center justify-center border border-dashed w-full h-full card-list-item cursor-pointer"
-                :style="{ height: cardHeight }"
-                @click="handleCreate"
-              >
-                <el-icon :size="18" class="mr-2px"><plus></plus></el-icon>新增
-              </div>
-            </el-col>
-            <el-col :span="span" v-bind="$attrs" v-for="(item, index) in tableData" :key="item.id" class="mb-10">
-              <el-card :body-style="{ padding: '0px' }" shadow="hover">
-                <slot :item="item" :index="index"></slot>
-              </el-card>
-            </el-col>
-          </el-row>
-        </div>
-        <div class="flex justify-end shrink-0">
-          <el-pagination
-            background
-            layout="prev, pager, next, jumper, total"
-            v-model:current-page="curPage"
-            :page-size="pageSize"
-            :total="total"
-            class="mt-16px"
-          />
-        </div>
-      </div>
-    </el-card>
-
-    <template v-if="formMode === 'dialog'">
-      <dialog-form
-        v-model="formVisible"
-        v-if="formVisible"
-        :dialogConfig="dialogConfig"
-        :formConfig="formConfig"
-        :formData="formData"
-        :formSlots="formSlots"
-        :create="crud.create"
-        :update="crud.update"
-        @success="handleFormSuccess"
-      >
-        <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
-          <slot :name="slot.alias" v-bind="slotProps"></slot>
-        </template>
-      </dialog-form>
-    </template>
-    <template v-else>
-      <drawer-form
-        v-model="formVisible"
-        v-if="formVisible"
-        :drawerConfig="drawerConfig"
-        :formConfig="formConfig"
-        :formData="formData"
-        :formSlots="formSlots"
-        :create="crud.create"
-        :update="crud.update"
-        @success="handleFormSuccess"
-      >
-        <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
-          <slot :name="slot.alias" v-bind="slotProps"></slot>
-        </template>
-      </drawer-form>
-    </template>
-  </div>
-</template>
-
-<style scoped lang="scss">
-.card-list-item {
-  border-color: var(--el-border-color-light);
-  border-radius: var(--el-card-border-radius);
-  color: var(--el-text-color-regular);
-
-  &:hover {
-    color: var(--el-color-primary);
-    border-color: currentColor;
-  }
-}
-</style>

+ 0 - 262
src/components/core/ProTable.vue

@@ -1,262 +0,0 @@
-<script setup lang="ts">
-defineOptions({
-  inheritAttrs: false
-})
-
-import { useForm } from '@/hooks/useForm'
-import { useTableQuery } from '@/hooks/useTableQuery'
-import { useTable } from '@/hooks/useTable'
-import type { DialogProps, DrawerProps } from 'element-plus'
-import type { BasicForm, ICRUD } from '@/types/form'
-import type { VxeToolbarProps } from 'vxe-table'
-
-interface CustomTable {
-  showOperate?: boolean
-  showEdit?: boolean
-  showView?: boolean
-  showDelete?: boolean
-  operateWidth?: number
-}
-
-interface Props {
-  crud: ICRUD
-  pageSize?: number
-  selection?: boolean
-  formConfig: BasicForm
-  dialogConfig?: Partial<DialogProps>
-  drawerConfig?: Partial<DrawerProps>
-  tableConfig?: CustomTable
-  toolbarConfig?: VxeToolbarProps
-  showToolbar?: boolean
-  height?: string
-  formMode?: 'dialog' | 'drawer'
-  beforeCreate?: Function
-}
-
-const props = withDefaults(defineProps<Props>(), {
-  pageSize: 10,
-  selection: true,
-  showToolbar: true,
-  formMode: 'dialog'
-})
-const emits = defineEmits(['click-create', 'click-edit', 'click-view', 'checkbox-change', 'click-reset'])
-const slots = useSlots()
-
-// ============== 查询部分开始 ===============
-const { query, searchList, handleQuery, handleReset } = useTableQuery(props, emits)
-// ============== 查询部分结束 ===============
-
-// ============== 表格部分开始 ===============
-const {
-  tableData,
-  formData,
-  total,
-  curPage,
-  loading,
-  formVisible,
-  multipleSelection,
-  refresh,
-  handleCreate,
-  handleDelete,
-  handleBatchDelete,
-  handleUpdate,
-  handleView,
-  handleFormSuccess
-} = useTable(props, emits)
-
-const tableConfig = computed<CustomTable>(() => ({
-  showOperate: true,
-  showView: false,
-  showEdit: true,
-  showDelete: true,
-  operateWidth: 140,
-  ...props.tableConfig
-}))
-
-const xTable = ref<any>()
-const xToolbar = ref<any>()
-nextTick(() => {
-  // 将表格和工具栏进行关联
-  const $table = xTable.value
-  const $toolbar = xToolbar.value
-  $toolbar && $table.connect($toolbar)
-})
-
-const handleSelectionChange = () => {
-  multipleSelection.value = xTable.value.getCheckboxRecords()
-  emits('checkbox-change', multipleSelection.value)
-}
-
-// 动态计算table高度
-const toolbarRef = ref<any>(null)
-const paginationRef = ref<any>(null)
-const tableHeight = ref()
-
-const calcHeight = () => {
-  const toolbarHeight = toolbarRef.value ? toolbarRef.value.offsetHeight : 0
-  const paginationHeight = paginationRef.value ? paginationRef.value.offsetHeight : 0
-  tableHeight.value = `calc(100% - ${toolbarHeight + paginationHeight}px)`
-}
-nextTick(() => {
-  calcHeight()
-})
-watch(
-  () => props.showToolbar,
-  () => {
-    nextTick(() => {
-      calcHeight()
-    })
-  }
-)
-// ============== 表格部分结束 ===============
-
-// 构造表单插槽
-const { formSlots } = useForm(props)
-
-defineExpose({
-  handleCreate,
-  handleDelete,
-  handleUpdate,
-  handleView,
-  handleQuery,
-  handleReset,
-  refresh,
-  formData,
-  query,
-  table: xTable
-})
-</script>
-
-<template>
-  <div class="flex flex-col" style="max-height: 100vh" :style="{ height: height || '100%' }">
-    <el-card
-      class="mb-4 shrink-0"
-      shadow="never"
-      :body-style="{ 'padding-bottom': '0' }"
-      v-if="searchList.length || slots.query"
-    >
-      <el-form :inline="true">
-        <el-form-item :label="item.label" v-for="(item, index) in searchList" :key="index">
-          <form-comp :item="item" v-model="query[item.name]">
-            <template #[slot.alias] v-for="slot in item.slots" :key="slot.alias">
-              <slot :name="slot.alias"></slot>
-            </template>
-          </form-comp>
-        </el-form-item>
-        <slot name="query" :query="query"></slot>
-        <el-form-item>
-          <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
-          <el-button icon="Refresh" @click="handleReset">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </el-card>
-
-    <el-card class="h-full flex-grow" :body-style="{ height: '100%' }" shadow="never">
-      <div class="flex flex-col h-full">
-        <div ref="toolbarRef" class="shrink-0">
-          <slot name="header"></slot>
-          <vxe-toolbar ref="xToolbar" v-bind="toolbarConfig" v-if="showToolbar">
-            <template #buttons>
-              <el-button type="primary" icon="Plus" @click="handleCreate">新增</el-button>
-              <el-button
-                type="danger"
-                icon="Delete"
-                @click="handleBatchDelete"
-                :disabled="!multipleSelection.length"
-                v-if="selection"
-              >
-                删除
-              </el-button>
-              <slot name="toolbar" :selection="multipleSelection"></slot>
-            </template>
-          </vxe-toolbar>
-        </div>
-        <div :style="{ height: tableHeight }">
-          <vxe-table
-            ref="xTable"
-            id="xProTable"
-            size="medium"
-            auto-resize
-            height="auto"
-            :data="tableData"
-            :row-config="{ isHover: true }"
-            v-loading="loading"
-            v-bind="$attrs"
-            @checkbox-change="handleSelectionChange"
-            @checkbox-all="handleSelectionChange"
-          >
-            <vxe-column type="checkbox" width="50" v-if="selection"></vxe-column>
-            <slot></slot>
-            <vxe-column fixed="right" title="操作" :width="tableConfig.operateWidth" v-if="tableConfig.showOperate">
-              <template #default="{ row }">
-                <slot name="operateBefore" :row="row"></slot>
-                <el-button size="small" @click="handleView(row)" v-if="tableConfig.showView"> 查看 </el-button>
-                <el-button type="primary" size="small" @click="handleUpdate(row)" v-if="tableConfig.showEdit">
-                  编辑
-                </el-button>
-                <el-button type="danger" size="small" @click="handleDelete(row.id)" v-if="tableConfig.showDelete">
-                  删除
-                </el-button>
-                <slot name="operateAfter" :row="row"></slot>
-              </template>
-            </vxe-column>
-          </vxe-table>
-        </div>
-        <div ref="paginationRef">
-          <el-pagination
-            background
-            layout="->, prev, pager, next, total"
-            v-model:current-page="curPage"
-            :page-size="pageSize"
-            :total="total"
-            class="mt-16px"
-          />
-        </div>
-      </div>
-    </el-card>
-  </div>
-  <template v-if="formMode === 'dialog'">
-    <dialog-form
-      v-model="formVisible"
-      v-if="formVisible"
-      :dialogConfig="dialogConfig"
-      :formConfig="formConfig"
-      :formData="formData"
-      :formSlots="formSlots"
-      :create="crud.create"
-      :update="crud.update"
-      @success="handleFormSuccess"
-    >
-      <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
-        <slot :name="slot.alias" v-bind="slotProps"></slot>
-      </template>
-    </dialog-form>
-  </template>
-  <template v-else>
-    <drawer-form
-      v-model="formVisible"
-      v-if="formVisible"
-      :drawerConfig="drawerConfig"
-      :formConfig="formConfig"
-      :formData="formData"
-      :formSlots="formSlots"
-      :create="crud.create"
-      :update="crud.update"
-      @success="handleFormSuccess"
-    >
-      <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
-        <slot :name="slot.alias" v-bind="slotProps"></slot>
-      </template>
-    </drawer-form>
-  </template>
-</template>
-
-<style scoped>
-.vxe-toolbar {
-  padding-top: 0;
-}
-:deep(.el-card) {
-  --el-card-padding: 16px;
-  border: none !important;
-}
-</style>

+ 0 - 76
src/components/core/form/BasicForm.vue

@@ -1,76 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem } from '@/types/form'
-import { containerTypes, notFormItem } from '@/utils/constants'
-import { buildContainerSlots } from '@/utils/utils'
-
-interface Props {
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-
-const formData = computed(() => {
-  if (props.formData.id) {
-    return props.formData
-  } else {
-    // 构造表单值
-    const res = props.formData
-    const buildFormData = (formItems: Array<BasicFormItem>, res: any) => {
-      formItems.forEach(item => {
-        if (!item.notFormItem || !notFormItem.includes(item.type)) {
-          // 避免修改当前表单项value重置其他表单项的value
-          res[item.name] = res[item.name] !== undefined && item.value !== undefined ? res[item.name] : item.value
-        }
-        if (item.children) {
-          buildFormData(item.children, res)
-        }
-      })
-    }
-    buildFormData(props.formConfig.formItems, res)
-    return res
-  }
-})
-
-const formItems = computed(() => {
-  return props.formConfig.formItems.filter(item => !item.hidden)
-})
-
-// 构造表单容器插槽
-buildContainerSlots(props.formConfig.formItems)
-</script>
-
-<template>
-  <el-row :gutter="20">
-    <el-col :span="item.span || formConfig.span || 12" v-for="(item, index) in formItems" :key="index">
-      <component
-        :is="'el-' + item.type"
-        v-if="containerTypes.includes(item.type)"
-        v-model="item.value"
-        v-bind="item.props"
-        :formItem="item"
-        :formData="formData"
-        :formConfig="formConfig"
-        v-on="item.events || {}"
-      >
-        <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-          <slot :name="slot.alias" v-bind="slotProps"></slot>
-        </template>
-      </component>
-      <template v-else>
-        <el-form-item :label="item.label" :rules="item.rules" :prop="item.name">
-          <form-comp :item="item" v-model="formData[item.name]">
-            <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-              <slot :name="slot.alias" v-bind="slotProps"></slot>
-            </template>
-          </form-comp>
-        </el-form-item>
-      </template>
-    </el-col>
-  </el-row>
-</template>
-
-<style lang="scss" scoped>
-:deep(.el-select) {
-  width: 100%;
-}
-</style>

+ 0 - 68
src/components/core/form/DialogForm.vue

@@ -1,68 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, FormSlot } from '@/types/form'
-import type { DialogProps } from 'element-plus'
-import { useForm } from '@/hooks/useForm'
-
-interface Props {
-  modelValue: boolean
-  dialogConfig?: Partial<DialogProps>
-  formSlots?: Array<FormSlot>
-  formConfig: BasicForm
-  formData: any
-  create: Function
-  update: Function
-}
-
-const props = defineProps<Props>()
-const emits = defineEmits(['update:modelValue', 'success'])
-
-const { formInitData, formRef, title, visible, formSlots, close, submit } = useForm(props, emits)
-</script>
-
-<template>
-  <div>
-    <el-dialog
-      draggable
-      :title="title"
-      align-center
-      v-bind="dialogConfig"
-      v-model="visible"
-      @close="close"
-      :close-on-click-modal="false"
-    >
-      <pro-form
-        class="pro-form"
-        ref="formRef"
-        :formConfig="formConfig"
-        :formData="formInitData"
-        :formSlots="formSlots"
-        :create="create"
-        :update="update"
-      >
-        <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
-          <slot :name="slot.alias" v-bind="slotProps"></slot>
-        </template>
-      </pro-form>
-
-      <template #footer>
-        <el-button icon="Close" @click="close">取消</el-button>
-        <el-button icon="Check" type="primary" @click="submit" v-if="!formConfig.disabled">保存</el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<style lang="scss" scoped>
-:deep(.el-dialog__header) {
-  border-bottom: 1px solid #e3edfe;
-  margin-right: 0;
-}
-:deep(.el-dialog__body) {
-  max-height: calc(100vh - 56px - 62px);
-  overflow: auto;
-}
-:deep(.el-dialog__footer) {
-  border-top: 1px solid #e3edfe;
-  padding-bottom: 10px;
-}
-</style>

+ 0 - 47
src/components/core/form/DrawerForm.vue

@@ -1,47 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, FormSlot } from '@/types/form'
-import type { DrawerProps } from 'element-plus'
-import { useForm } from '@/hooks/useForm'
-
-interface Props {
-  modelValue: boolean
-  drawerConfig?: Partial<DrawerProps>
-  formSlots?: Array<FormSlot>
-  formConfig: BasicForm
-  formData: any
-  create: Function
-  update: Function
-}
-
-const props = defineProps<Props>()
-const emits = defineEmits(['update:modelValue', 'success'])
-
-const { formInitData, formRef, title, visible, formSlots, close, submit } = useForm(props, emits)
-</script>
-
-<template>
-  <div>
-    <el-drawer :title="title" v-bind="drawerConfig" v-model="visible" @close="close" :close-on-click-modal="false">
-      <pro-form
-        class="pro-form"
-        ref="formRef"
-        :formConfig="formConfig"
-        :formData="formInitData"
-        :formSlots="formSlots"
-        :create="create"
-        :update="update"
-      >
-        <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
-          <slot :name="slot.alias" v-bind="slotProps"></slot>
-        </template>
-      </pro-form>
-
-      <template #footer v-if="!formConfig.disabled">
-        <el-button icon="Close" @click="close">取消</el-button>
-        <el-button icon="Check" type="primary" @click="submit">保存</el-button>
-      </template>
-    </el-drawer>
-  </div>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 154
src/components/core/form/FormComp.vue

@@ -1,154 +0,0 @@
-<!-- eslint-disable vue/no-mutating-props -->
-<script setup lang="ts">
-// @ts-nocheck
-import type { BasicFormItem } from '@/types/form'
-
-interface Props {
-  modelValue: any
-  item: BasicFormItem
-}
-
-const props = defineProps<Props>()
-const emits = defineEmits(['update:modelValue'])
-
-const modelValue = computed({
-  get: () => props.modelValue,
-  set: value => emits('update:modelValue', value)
-})
-
-const placeholder = (item: BasicFormItem) => {
-  if (['select', 'cascader', 'date-picker', 'time-picker', 'time-select', 'dict', 'area'].includes(item.type)) {
-    return '请选择' + item.label
-  } else {
-    return '请输入' + item.label
-  }
-}
-
-if (props.item.request) {
-  props.item.request(props.item).then((res: any) => {
-    if (props.item.optionsType == '1') {
-      props.item.trendsOptions = res
-    } else {
-      props.item.options = res
-    }
-  })
-}
-</script>
-
-<template>
-  <el-cascader
-    v-if="item.type === 'cascader'"
-    style="width: 100%"
-    v-model="modelValue"
-    clearable
-    v-bind="item.props"
-    v-on="item.events || {}"
-    :placeholder="item.placeholder || placeholder(item)"
-  >
-    <template #[slot.name]="{ node, data }" v-for="slot in item.slots" :key="slot.alias">
-      <slot :name="slot.alias" :node="node" :data="data"></slot>
-    </template>
-  </el-cascader>
-  <el-date-picker
-    v-else-if="item.type === 'date-picker'"
-    style="width: 100%"
-    v-model="modelValue"
-    clearable
-    v-bind="item.props"
-    v-on="item.events || {}"
-    :placeholder="item.placeholder || placeholder(item)"
-  >
-    <template #[slot.name]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-      <slot :name="slot.alias" v-bind="slotProps"></slot>
-    </template>
-  </el-date-picker>
-  <el-upload
-    v-else-if="item.type === 'upload'"
-    v-model:file-list="modelValue"
-    v-bind="item.props"
-    v-on="item.events || {}"
-  >
-  </el-upload>
-
-  <div v-else-if="item.type === 'form-html'" v-html="item.props?.html"></div>
-
-  <component
-    v-else
-    :is="'el-' + item.type"
-    :item="item"
-    v-model="modelValue"
-    clearable
-    v-bind="item.props"
-    :placeholder="item.placeholder || placeholder(item)"
-    v-on="item.events || {}"
-  >
-    <template v-if="item.type === 'button'">{{ item.props.buttonName }}</template>
-    <template v-if="item.type === 'tag'">{{ item.props.tagName }}</template>
-    <template v-if="item.type === 'divider'">{{ item.props.dividerName }}</template>
-    <template v-if="item.type === 'text'">{{ item.props.textName }}</template>
-    <template v-if="item.type === 'radio-group'">
-      <template v-if="item.props?.button">
-        <el-radio-button
-          v-for="(option, index) in item.optionsType == '1' ? item.trendsOptions : item.options"
-          :label="option.label"
-          :key="index"
-          v-bind="option.props"
-        >
-          {{ option.value }}
-        </el-radio-button>
-      </template>
-      <template v-else>
-        <el-radio
-          v-for="(option, index) in item.optionsType == '1' ? item.trendsOptions : item.options"
-          :label="option.label"
-          :key="index"
-          :border="item.props?.border"
-          v-bind="option.props"
-        >
-          {{ option.value }}
-        </el-radio>
-      </template>
-    </template>
-    <template v-if="item.type === 'checkbox-group'">
-      <template v-if="item.props?.button">
-        <el-checkbox-button
-          v-for="(option, index) in item.optionsType == '1' ? item.trendsOptions : item.options"
-          :label="option.label"
-          :value="option.value"
-          :key="index"
-          v-bind="option.props"
-        >
-          {{ option.value }}
-        </el-checkbox-button>
-      </template>
-      <template v-else>
-        <el-checkbox
-          v-for="(option, index) in item.optionsType == '1' ? item.trendsOptions : item.options"
-          :label="option.label"
-          :value="option.value"
-          :key="index"
-          :border="item.props?.border"
-          v-bind="option.props"
-        >
-          {{ option.value }}
-        </el-checkbox>
-      </template>
-    </template>
-    <template v-if="item.type === 'select'">
-      <el-option
-        v-for="(option, index) in item.optionsType == '1' ? item.trendsOptions : item.options"
-        :label="option.label"
-        :value="option.value"
-        :key="index"
-        clearable
-        v-bind="option.props"
-      >
-      </el-option>
-    </template>
-    <template #[slot.name]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-      <slot :name="slot.alias" v-bind="slotProps"></slot>
-    </template>
-  </component>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 104
src/components/core/form/ProForm.vue

@@ -1,104 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem, FormSlot } from '@/types/form'
-import { ElMessage, ElLoading } from 'element-plus'
-import { useForm } from '@/hooks/useForm'
-
-interface Props {
-  formConfig: BasicForm
-  formData: any
-  formSlots?: Array<FormSlot>
-  create: Function
-  update: Function
-}
-
-const props = defineProps<Props>()
-
-const formProps: any = ref({
-  labelWidth: '100px',
-  labelPosition: 'top',
-  size: 'large',
-  ...props.formConfig?.props
-})
-
-const formInitData = computed(() => {
-  return props.formData
-})
-const formRef = ref()
-
-const { formSlots } = useForm(props)
-
-// 递归将上传的值改成逗号隔开的字符串
-const uploadValueChange = (formItems: Array<BasicFormItem>, data: any) => {
-  formItems.forEach(item => {
-    if (item.type === 'upload' || item.type === 'image-upload') {
-      if (item.extra) {
-        data[item.name] = item.extra.join(',')
-      } else {
-        data[item.name] = data[item.name].join(',')
-      }
-    } else if (item.type === 'file-upload') {
-      data[item.name] = JSON.stringify(item.extra)
-    }
-    if (item.children) {
-      uploadValueChange(item.children, data)
-    }
-  })
-}
-
-const submit = async () => {
-  let message, result
-  return formRef.value.validate().then(async () => {
-    const loading = ElLoading.service()
-    try {
-      const data = { ...formInitData.value }
-      uploadValueChange(props.formConfig.formItems, data)
-      if (data.id) {
-        result = await props.update(data)
-        message = '编辑成功'
-      } else {
-        result = await props.create(data)
-        message = '新增成功'
-      }
-
-      loading.close()
-
-      if (result === false) {
-        return false
-      } else {
-        ElMessage({
-          type: 'success',
-          message
-        })
-        return true
-      }
-    } catch (error) {
-      loading.close()
-      return false
-    }
-  })
-}
-
-defineExpose({
-  submit
-})
-</script>
-
-<template>
-  <el-form
-    :model="formInitData"
-    :disabled="formConfig.disabled"
-    ref="formRef"
-    status-icon
-    scrollToError
-    v-bind="formProps"
-    class="form"
-  >
-    <basic-form :formConfig="formConfig" :formData="formInitData">
-      <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
-        <slot :name="slot.alias" v-bind="slotProps"></slot>
-      </template>
-    </basic-form>
-  </el-form>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 14
src/components/form/ElCustom.vue

@@ -1,14 +0,0 @@
-<script setup lang="ts">
-defineOptions({
-  inheritAttrs: false
-})
-
-interface Props {}
-defineProps<Props>()
-</script>
-
-<template>
-  <slot></slot>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 85
src/components/form/ElEditTag.vue

@@ -1,85 +0,0 @@
-<script setup lang="ts">
-import type { ElInput } from 'element-plus'
-
-const props = withDefaults(
-  defineProps<{
-    modelValue: string[] | string
-    btnText?: string
-    editable?: boolean
-  }>(),
-  {
-    btnText: '+ 新增',
-    editable: true
-  }
-)
-const emits = defineEmits(['update:modelValue'])
-
-const tagList = ref()
-watch(
-  () => props.modelValue,
-  value => {
-    tagList.value = (typeof value === 'string' ? value.split(',') : value).filter(item => item)
-  },
-  { deep: true, immediate: true }
-)
-watch(
-  tagList,
-  value => {
-    emits('update:modelValue', value.join(','))
-  },
-  { deep: true }
-)
-
-const handleClose = (tag: string) => {
-  tagList.value.splice(tagList.value.indexOf(tag), 1)
-}
-
-// 显示输入框
-const inputVisible = ref(false)
-const inputRef = ref<InstanceType<typeof ElInput>>()
-const showInput = () => {
-  inputVisible.value = true
-  nextTick(() => {
-    inputRef.value!.input!.focus()
-  })
-}
-
-// 输入框的确认操作
-const inputValue = ref('')
-const handleInputConfirm = () => {
-  if (inputValue.value) {
-    tagList.value.push(inputValue.value)
-  }
-  inputVisible.value = false
-  inputValue.value = ''
-}
-</script>
-
-<template>
-  <el-space wrap>
-    <el-tag
-      v-for="tag in tagList"
-      :key="tag"
-      size="small"
-      :closable="editable"
-      :disable-transitions="false"
-      @close="handleClose(tag)"
-    >
-      {{ tag }}
-    </el-tag>
-    <template v-if="editable">
-      <el-input
-        v-if="inputVisible"
-        ref="inputRef"
-        v-model="inputValue"
-        class="ml-1 w-20"
-        size="small"
-        @keyup.enter="handleInputConfirm"
-        @blur="handleInputConfirm"
-      />
-      <el-button v-else class="button-new-tag ml-1" size="small" @click="showInput"> {{ btnText }} </el-button>
-    </template>
-  </el-space>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 40
src/components/form/ElFileUpload.vue

@@ -1,40 +0,0 @@
-<script lang="ts" setup>
-import config from '@/config/defaultSetting'
-import { useUpload } from '@/hooks/useUpload'
-import type { BasicFormItem } from '@/types/form'
-
-interface Props {
-  modelValue: any
-  item?: BasicFormItem
-  fileSize?: number | string
-  action?: string
-  oss?: boolean
-}
-const props = withDefaults(defineProps<Props>(), {
-  action: config.uploadApi,
-  fileSize: 100,
-  oss: config.oss
-})
-const emits = defineEmits(['update:modelValue'])
-
-const { modelValue, headers, action, uploadAttrs, beforeUpload, handleRemove } = useUpload(props, emits, 'file')
-</script>
-
-<template>
-  <el-upload
-    v-model:file-list="modelValue"
-    :action="action"
-    :headers="headers"
-    :before-upload="beforeUpload"
-    :on-remove="handleRemove"
-    v-bind="{ ...uploadAttrs, ...$attrs }"
-  >
-    <template #[slot.name]="slotProps" v-for="slot in item?.slots" :key="slot.alias">
-      <slot :name="slot.name" v-bind="slotProps">
-        <el-button type="primary">上传</el-button>
-      </slot>
-    </template>
-  </el-upload>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 56
src/components/form/ElFormCard.vue

@@ -1,56 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem } from '@/types/form'
-import { containerTypes } from '@/utils/constants'
-
-interface Props {
-  formItem: BasicFormItem
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-
-const cardSlot = props.formItem.slots?.find(slot => slot.name === 'header')
-</script>
-
-<template>
-  <el-card :header="formItem.label">
-    <template #header="slotProps" v-if="cardSlot">
-      <slot :name="cardSlot.alias" v-bind="slotProps"></slot>
-    </template>
-    <el-row :gutter="20">
-      <el-col
-        :span="item.span || formConfig.span || 12"
-        v-for="(item, index) in formItem.children"
-        :key="index"
-        v-show="!item.hidden"
-      >
-        <component
-          :is="'el-' + item.type"
-          v-if="containerTypes.includes(item.type)"
-          v-model="item.value"
-          v-bind="item.props"
-          :children="item.children"
-          :formItem="item"
-          :formData="formData"
-          :formConfig="formConfig"
-          v-on="item.events || {}"
-        >
-          <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-            <slot :name="slot.alias" v-bind="slotProps"></slot>
-          </template>
-        </component>
-        <template v-else>
-          <el-form-item :label="item.label" :rules="item.rules" :prop="item.name">
-            <form-comp :item="item" v-model="formData[item.name]">
-              <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-                <slot :name="slot.alias" v-bind="slotProps"></slot>
-              </template>
-            </form-comp>
-          </el-form-item>
-        </template>
-      </el-col>
-    </el-row>
-  </el-card>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 21
src/components/form/ElFormGroup.vue

@@ -1,21 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem } from '@/types/form'
-
-interface Props {
-  formItem: BasicFormItem
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-
-const config: BasicForm = {
-  ...props.formConfig,
-  formItems: props.formItem.children || []
-}
-</script>
-
-<template>
-  <basic-form :formConfig="config" :formData="formData"></basic-form>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 57
src/components/form/ElFormLayout.vue

@@ -1,57 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem } from '@/types/form'
-import { containerTypes } from '@/utils/constants'
-
-interface Props {
-  formItem: BasicFormItem
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-
-const children = computed(() => {
-  return props.formItem.children?.filter(item => !item.hidden)
-})
-</script>
-
-<template>
-  <el-row :gutter="20">
-    <el-col :span="item.span" v-bind="item.props" v-for="(item, index) in children" :key="index">
-      <component
-        :is="'el-' + item.type"
-        v-if="containerTypes.includes(item.type)"
-        v-model="item.value"
-        v-bind="item.props"
-        :children="item.children"
-        :formItem="item"
-        :formData="formData"
-        :formConfig="formConfig"
-        v-on="item.events || {}"
-      >
-        <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-          <slot :name="slot.alias" v-bind="slotProps"></slot>
-        </template>
-      </component>
-      <template v-else>
-        <el-row :gutter="20">
-          <el-col
-            v-for="(child, index) in item.children"
-            :key="index"
-            :span="child.span || formConfig.span || 12"
-            v-show="!child.hidden"
-          >
-            <el-form-item :label="child.label" :rules="child.rules" :prop="child.name">
-              <form-comp :item="child" v-model="formData[child.name]">
-                <template #[slot.alias]="slotProps" v-for="slot in child.slots" :key="slot.alias">
-                  <slot :name="slot.alias" v-bind="slotProps"></slot>
-                </template>
-              </form-comp>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </template>
-    </el-col>
-  </el-row>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 32
src/components/form/ElFormSteps.vue

@@ -1,32 +0,0 @@
-<script setup lang="ts">
-import type { StepProps } from 'element-plus'
-import type { BasicForm, BasicFormItem } from '@/types/form'
-
-interface Props {
-  formItem: BasicFormItem
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-
-const stepsProps = props.formItem.props as {
-  active: number
-  steps: Array<StepProps>
-}
-</script>
-
-<template>
-  <el-steps class="mb-4" v-bind="stepsProps">
-    <el-step v-for="(item, index) in stepsProps?.steps" :key="index" v-bind="item"></el-step>
-  </el-steps>
-  <template v-for="(item, index) in formItem.children" :key="index">
-    <component
-      :is="'el-' + item.type"
-      :formItem="item"
-      :formData="formData"
-      v-if="stepsProps.active === index"
-    ></component>
-  </template>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 68
src/components/form/ElFormTable.vue

@@ -1,68 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem } from '@/types/form'
-
-interface Props {
-  formItem: BasicFormItem
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-
-const keys: any = {}
-props.formItem.children &&
-  props.formItem.children.forEach((item: BasicFormItem) => {
-    if (item.name) {
-      keys[item.name] = ''
-    }
-  })
-
-if (!Array.isArray(props.formItem.value)) {
-  props.formItem.value = []
-}
-
-const handleAdd = () => {
-  props.formItem.value.push({
-    ...keys
-  })
-}
-const handleDelete = (index: number) => {
-  props.formItem.value.splice(index, 1)
-}
-
-const width = (props.formItem?.props as any)?.width
-</script>
-
-<template>
-  <el-form-item>
-    <div class="w-full">
-      <el-button type="primary" @click="handleAdd" icon="plus" plain size="default" class="mb-2">添加</el-button>
-      <vxe-table class="w-full" :data="formItem.value" border>
-        <vxe-column type="seq" width="50"></vxe-column>
-        <vxe-column
-          v-for="(item, key) in formItem.children"
-          :key="key"
-          :title="item.label"
-          :field="item.name"
-          :width="Array.isArray(width) ? width[key] : width"
-        >
-          <template #default="{ row, _rowIndex }">
-            <el-form-item :prop="formItem.name + '.' + _rowIndex + '.' + item.name" :rules="item.rules">
-              <form-comp :item="item" v-model="row[item.name]">
-                <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-                  <slot :name="slot.alias" v-bind="slotProps"></slot>
-                </template>
-              </form-comp>
-            </el-form-item>
-          </template>
-        </vxe-column>
-        <vxe-column fixed="right" title="操作" width="70">
-          <template #default="{ rowIndex }">
-            <el-button type="danger" size="small" @click="handleDelete(rowIndex)"> 删除 </el-button>
-          </template>
-        </vxe-column>
-      </vxe-table>
-    </div>
-  </el-form-item>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 64
src/components/form/ElFormTabs.vue

@@ -1,64 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem } from '@/types/form'
-import { containerTypes } from '@/utils/constants'
-
-interface Props {
-  modelValue: string
-  formItem: BasicFormItem
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-const emits = defineEmits(['update:modelValue'])
-
-const modelValue = computed({
-  get: () => props.modelValue,
-  set: value => emits('update:modelValue', value)
-})
-</script>
-
-<template>
-  <el-tabs v-model="modelValue" style="width: 100%">
-    <el-tab-pane :label="tab.label" :name="tab.name" v-for="tab in formItem.children" :key="tab.name">
-      <template #[slot.name] v-for="slot in tab.slots" :key="slot.alias">
-        <slot :name="slot.alias"></slot>
-      </template>
-
-      <el-row :gutter="20">
-        <el-col
-          :span="item.span || formConfig.span || 12"
-          v-for="(item, index) in tab.children"
-          :key="index"
-          v-show="!item.hidden"
-        >
-          <component
-            :is="'el-' + item.type"
-            v-if="containerTypes.includes(item.type)"
-            v-model="item.value"
-            v-bind="item.props"
-            :children="item.children"
-            :formItem="item"
-            :formData="formData"
-            :formConfig="formConfig"
-            v-on="item.events || {}"
-          >
-            <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-              <slot :name="slot.alias" v-bind="slotProps"></slot>
-            </template>
-          </component>
-          <template v-else>
-            <el-form-item :label="item.label" :rules="item.rules" :prop="item.name">
-              <form-comp :item="item" v-model="formData[item.name]">
-                <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
-                  <slot :name="slot.alias" v-bind="slotProps"></slot>
-                </template>
-              </form-comp>
-            </el-form-item>
-          </template>
-        </el-col>
-      </el-row>
-    </el-tab-pane>
-  </el-tabs>
-</template>
-
-<style lang="scss" scoped></style>

+ 0 - 118
src/components/form/ElImageUpload.vue

@@ -1,118 +0,0 @@
-<script lang="ts" setup>
-import { isAbsolutePath } from '@/utils/utils'
-import config from '@/config/defaultSetting'
-import { useUpload } from '@/hooks/useUpload'
-import type { UploadFile } from 'element-plus'
-import type { BasicFormItem } from '@/types/form'
-
-interface Props {
-  modelValue: any
-  item?: BasicFormItem
-  size?: number | string
-  iconSize?: number | string
-  fileSize?: number | string
-  action?: string
-  oss?: boolean
-  limit?: number
-}
-const props = withDefaults(defineProps<Props>(), {
-  size: '148px',
-  iconSize: '28px',
-  action: config.uploadApi,
-  fileSize: 10,
-  oss: config.oss,
-  limit: 1
-})
-const emits = defineEmits(['update:modelValue'])
-
-const limit = Number(props.limit) || 1
-const { modelValue, headers, baseApi, uploadList, action, uploadAttrs, beforeUpload } = useUpload(props, emits)
-
-// 生成图片地址
-const prefix = props.oss ? config.ossHost : baseApi
-const genImageUrl = (url: string) => {
-  if (url.startsWith('blob:') || isAbsolutePath(url)) {
-    return url
-  } else {
-    return prefix + url
-  }
-}
-
-const uploadRef = ref()
-// 删除图片
-const handleRemove = (file: UploadFile) => {
-  uploadRef.value.handleRemove(file)
-  uploadList.value = modelValue.value
-}
-// 图片预览
-const showViewer = ref(false)
-const previewIndex = ref(0)
-const previewList = computed(() => modelValue.value.map((item: any) => genImageUrl(item.url)))
-const handlePreview = (file: UploadFile) => {
-  previewIndex.value = modelValue.value.findIndex((item: any) => item.url === file.url)
-  showViewer.value = true
-}
-const closeViewer = () => {
-  showViewer.value = false
-}
-</script>
-
-<template>
-  <el-upload
-    ref="uploadRef"
-    v-model:file-list="modelValue"
-    :class="{ 'is-disabled': modelValue.length >= limit }"
-    :action="action"
-    :headers="headers"
-    :before-upload="beforeUpload"
-    :limit="limit"
-    list-type="picture-card"
-    accept="image/*"
-    v-bind="{ ...uploadAttrs, ...$attrs }"
-  >
-    <el-icon class="avatar-uploader-icon" :style="{ fontSize: iconSize }"><Plus /></el-icon>
-    <template #file="{ file }">
-      <el-image class="el-upload-list__item-thumbnail" :src="genImageUrl(file.url)" fit="cover" />
-      <span class="el-upload-list__item-actions">
-        <span class="el-upload-list__item-preview" @click="handlePreview(file)">
-          <el-icon><zoom-in /></el-icon>
-        </span>
-
-        <span class="el-upload-list__item-delete" @click="handleRemove(file)">
-          <el-icon><Delete /></el-icon>
-        </span>
-      </span>
-    </template>
-    <template #[slot.name]="slotProps" v-for="slot in item?.slots" :key="slot.alias">
-      <slot :name="slot.name" v-bind="slotProps"></slot>
-    </template>
-  </el-upload>
-  <el-image-viewer v-if="showViewer" :url-list="previewList" :initial-index="previewIndex" @close="closeViewer" />
-</template>
-
-<style lang="scss" scoped>
-:deep(.el-upload) {
-  width: v-bind(size);
-  height: v-bind(size);
-  border: 1px dashed var(--el-border-color);
-  border-radius: 6px;
-  cursor: pointer;
-  position: relative;
-  overflow: hidden;
-  transition: var(--el-transition-duration-fast);
-
-  &:hover {
-    border-color: var(--el-color-primary);
-  }
-}
-:deep(.el-upload--picture-card) {
-  background-color: #fff;
-}
-:deep(.el-upload-list--picture-card .el-upload-list__item) {
-  width: v-bind(size);
-  height: v-bind(size);
-}
-.is-disabled :deep(.el-upload--picture-card) {
-  display: none;
-}
-</style>

+ 0 - 82
src/components/form/ElSubForm.vue

@@ -1,82 +0,0 @@
-<script setup lang="ts">
-import type { BasicForm, BasicFormItem } from '@/types/form'
-
-interface Props {
-  formItem: BasicFormItem
-  formConfig: BasicForm
-  formData: any
-}
-const props = defineProps<Props>()
-
-const keys: any = {}
-props.formItem.children &&
-  props.formItem.children.forEach((item: BasicFormItem) => {
-    keys[item.name] = ''
-  })
-
-if (!Array.isArray(props.formItem.value)) {
-  props.formItem.value = []
-}
-
-const handleAdd = () => {
-  props.formItem.value.push({
-    ...keys
-  })
-}
-const handleDelete = (index: number) => {
-  props.formItem.value.splice(index, 1)
-}
-</script>
-
-<template>
-  <div class="w-full">
-    <el-button type="primary" @click="handleAdd" icon="plus" plain class="mb-2" size="default">添加</el-button>
-    <div class="flex item" v-for="(item, index) in formItem.value" :key="index">
-      <div class="w-60px">
-        <el-button class="index" type="primary" size="small" plain round>#{{ index + 1 }}</el-button>
-        <el-button
-          class="del"
-          type="danger"
-          size="small"
-          plain
-          circle
-          icon="delete"
-          @click="handleDelete(index)"
-        ></el-button>
-      </div>
-
-      <div class="flex-grow">
-        <el-form-item
-          v-for="(child, index) in formItem.children"
-          :key="index"
-          :label="child.label"
-          :rules="child.rules"
-          :prop="formItem.name + '.' + index + '.' + child.name"
-          v-show="!child.hidden"
-        >
-          <form-comp :item="child" v-model="item[child.name]">
-            <template #[slot.alias]="slotProps" v-for="slot in child.slots" :key="slot.alias">
-              <slot :name="slot.alias" v-bind="slotProps"></slot>
-            </template>
-          </form-comp>
-        </el-form-item>
-      </div>
-    </div>
-  </div>
-</template>
-
-<style lang="scss" scoped>
-.item {
-  .del {
-    display: none;
-  }
-  &:hover {
-    .index {
-      display: none;
-    }
-    .del {
-      display: block;
-    }
-  }
-}
-</style>

+ 0 - 0
src/domain/role/api.ts → src/domains/role/api.ts


+ 0 - 0
src/domain/role/service.ts → src/domains/role/service.ts


+ 0 - 0
src/domain/user/api.ts → src/domains/user/api.ts


+ 0 - 0
src/domain/user/service.ts → src/domains/user/service.ts


+ 0 - 51
src/hooks/useForm.ts

@@ -1,51 +0,0 @@
-import { buildFormSlots } from '@/utils/utils'
-import type { FormSlot } from '@/types/form'
-
-export const useForm = (props: any, emits?: any) => {
-  const formInitData = ref({})
-  watchEffect(() => {
-    formInitData.value = props.formData
-  })
-
-  const title = computed(() => {
-    if (props.formConfig.disabled) {
-      return '查看'
-    } else {
-      return props.formData.id ? '编辑' : '新增'
-    }
-  })
-  const visible = computed({
-    get: () => props.modelValue,
-    set: value => emits('update:modelValue', value)
-  })
-
-  const close = () => {
-    visible.value = false
-    formInitData.value = {}
-  }
-
-  const formRef = ref()
-  const submit = async () => {
-    const res = await formRef.value.submit()
-    if (res) {
-      close()
-      emits('success')
-    }
-  }
-
-  // 构造表单插槽
-  const formSlots = ref<FormSlot[]>(props.formSlots || [])
-  if (!props.formSlots) {
-    buildFormSlots(props.formConfig.formItems, formSlots.value)
-  }
-
-  return {
-    formInitData,
-    title,
-    visible,
-    close,
-    formRef,
-    submit,
-    formSlots
-  }
-}

+ 0 - 167
src/hooks/useTable.ts

@@ -1,167 +0,0 @@
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-export const useTable = (props: any, emits: any) => {
-  const loading = ref(false)
-  const tableData = ref<any>([])
-  const total = ref(0)
-  const curPage = ref(1)
-  /**
-   * 获取表格数据
-   * @param query - 查询参数
-   */
-  const getTableData = (query?: any) => {
-    loading.value = true
-    props.crud
-      ?.getList({
-        ...query,
-        pageSize: props.pageSize,
-        pageNo: curPage.value
-      })
-      .then((res: any) => {
-        tableData.value = res.infos || res.list || res.rows
-        total.value = res.total || res.totalCount
-      })
-      .finally(() => {
-        loading.value = false
-      })
-  }
-  watch(
-    curPage,
-    () => {
-      getTableData()
-    },
-    {
-      immediate: true
-    }
-  )
-
-  const refresh = () => {
-    curPage.value = 1
-    getTableData()
-  }
-
-  // ============== crud部分开始 ===============
-  const formRoute = ref<any>(props.formConfig.route)
-  const formData = ref<any>({})
-  const formVisible = ref(false)
-
-  const doCreate = () => {
-    emits('click-create')
-    if (formRoute.value) {
-      const router = useRouter()
-      router.push(formRoute.value)
-    } else {
-      formData.value = {}
-      props.formConfig.disabled = false
-      formVisible.value = true
-    }
-  }
-  const handleCreate = async () => {
-    if (!props.beforeCreate) {
-      return doCreate()
-    }
-
-    let result = true
-    try {
-      const beforeUploadPromise = props.beforeCreate(formData.value)
-      result = await beforeUploadPromise
-    } catch {
-      result = false
-    }
-
-    if (result === false) {
-      return
-    }
-    doCreate()
-  }
-
-  // 编辑、查看
-  const useRecordData = (row: any, disabled: boolean) => {
-    // 此函数用于获取记录数据并设置相应的表单配置
-    if (formRoute.value) {
-      const router = useRouter()
-      router.push(formRoute.value)
-    } else {
-      if (props.crud?.getRecord) {
-        props.crud.getRecord({ id: row.id }).then((res: any) => {
-          formData.value = res.data
-        })
-      } else {
-        formData.value = { ...row }
-      }
-      props.formConfig.disabled = disabled
-      formVisible.value = true
-    }
-  }
-  const handleUpdate = (row: any) => {
-    emits('click-edit', row)
-    useRecordData(row, false)
-  }
-  const handleView = (row: any) => {
-    emits('click-view', row)
-    useRecordData(row, true)
-  }
-
-  // 删除成功
-  const deleteSuccess = (data: any) => {
-    if (data.success || data.code === 200) {
-      getTableData()
-      ElMessage({
-        type: 'success',
-        message: '删除成功'
-      })
-    } else {
-      ElMessage.error(data.msg)
-    }
-  }
-  const handleDelete = (id: string | number) => {
-    ElMessageBox.confirm('您确定要删除该项吗', '提示', {
-      type: 'warning'
-    }).then(async () => {
-      const data = await props.crud?.delete({ id })
-      deleteSuccess(data)
-    })
-  }
-
-  const multipleSelection = ref<any[]>([])
-  const handleBatchDelete = () => {
-    ElMessageBox.confirm('您确定要删除吗', '提示', {
-      type: 'warning'
-    }).then(async () => {
-      if (props.crud.deleteBatch) {
-        const data = await props.crud?.deleteBatch({
-          ids: multipleSelection.value.map(item => item.id).join(',')
-        })
-        deleteSuccess(data)
-      } else {
-        ElMessage({
-          type: 'error',
-          message: '未提供deleteBatch方法'
-        })
-      }
-    })
-  }
-  // ============== crud部分结束 ===============
-
-  const handleFormSuccess = () => {
-    getTableData()
-  }
-
-  return {
-    tableData,
-    total,
-    curPage,
-    loading,
-    formData,
-    formVisible,
-    multipleSelection,
-    getTableData,
-    refresh,
-    handleCreate,
-    handleDelete,
-    handleBatchDelete,
-    handleUpdate,
-    handleView,
-    handleFormSuccess
-  }
-}

+ 0 - 49
src/hooks/useTableQuery.ts

@@ -1,49 +0,0 @@
-import { useTable } from './useTable'
-import type { BasicFormItem } from '@/types/form'
-
-export const useTableQuery = (props: any, emits: any) => {
-  const query = ref<any>({})
-  const defaultQuery = ref<any>({})
-  const searchList = ref<any>([])
-  // 构造搜索列表
-  const buildSearchList = (item: BasicFormItem) => {
-    if (item.search) {
-      searchList.value.push(Object.assign({}, item, { props: { ...item.props, disabled: false } }))
-      if (item.value) {
-        query.value[item.name] = item.value
-        defaultQuery.value[item.name] = item.value
-      }
-    }
-    if (item.children) {
-      item.children.forEach(buildSearchList)
-    }
-  }
-  watch(
-    () => props.formConfig.formItems,
-    val => {
-      searchList.value = []
-      val.forEach((item: BasicFormItem) => {
-        buildSearchList(item)
-      })
-    },
-    { immediate: true }
-  )
-
-  const { curPage, getTableData } = useTable(props, emits)
-  const handleQuery = () => {
-    curPage.value = 1
-    getTableData(query.value)
-  }
-  const handleReset = () => {
-    query.value = { ...defaultQuery.value }
-    emits('click-reset', query.value)
-    handleQuery()
-  }
-
-  return {
-    query,
-    searchList,
-    handleQuery,
-    handleReset
-  }
-}

+ 0 - 102
src/hooks/useUpload.ts

@@ -1,102 +0,0 @@
-import { getToken } from '@/utils/micro'
-import { ACCESS_TOKEN } from '@/utils/constants'
-import { ElMessage } from 'element-plus'
-import { isAbsolutePath, ossUpload } from '@/utils/utils'
-import config from '@/config/defaultSetting'
-import type { UploadProps, UploadFile } from 'element-plus'
-
-export const useUpload = (props: any, emits: any, type?: string) => {
-  // 将上传的值改成数组
-  const modelValue = computed({
-    get: () => {
-      if (!Array.isArray(props.modelValue)) {
-        if (type === 'file') {
-          try {
-            return JSON.parse(props.modelValue || '[]').map((item: any) => ({
-              url: item.url,
-              name: item.name
-            }))
-          } catch (error) {
-            return []
-          }
-        } else {
-          return props.modelValue
-            .split(',')
-            .filter((item: string) => item)
-            .map((item: string) => ({ url: item }))
-        }
-      } else {
-        return props.modelValue
-      }
-    },
-    set: value => emits('update:modelValue', value)
-  })
-
-  const headers = reactive({
-    [ACCESS_TOKEN]: 'Bearer ' + getToken()
-  })
-
-  const baseApi = import.meta.env.VITE_BASE_API
-  const action = isAbsolutePath(props.action) ? props.action : baseApi + props.action
-
-  const fileSize = Number(props.fileSize) || 50
-  const beforeUpload: UploadProps['beforeUpload'] = rawFile => {
-    if (rawFile.size / 1024 / 1024 > fileSize) {
-      ElMessage.error(`文件大小不能超过${fileSize}MB!`)
-      return false
-    }
-    return true
-  }
-
-  const uploadList = ref([...modelValue.value])
-  const handleUploadSuccess: UploadProps['onSuccess'] = (
-    response,
-    uploadFile: UploadFile,
-    uploadFiles: UploadFile[]
-  ) => {
-    if (response.success || response.code === 200) {
-      // 附加oss路径
-      uploadList.value = uploadFiles
-    } else {
-      ElMessage.error(response.msg)
-    }
-  }
-
-  // 删除文件
-  const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
-    uploadList.value = uploadFiles
-  }
-
-  const oss = props.oss ?? config.oss
-  // 动态属性
-  const uploadAttrs: any = oss ? { 'http-request': ossUpload } : {}
-  if (props.item) {
-    if (!props.item.props?.onSuccess) {
-      uploadAttrs['on-success'] = handleUploadSuccess
-    }
-    // 接口需要的属性
-    if (type === 'file') {
-      props.item.extra = computed(() =>
-        uploadList.value.map((item: any) => ({
-          name: item.name,
-          url: item.response?.url || item.url
-        }))
-      )
-    } else {
-      props.item.extra = computed(() => uploadList.value.map((item: any) => item.response?.url || item.url))
-    }
-  }
-
-  return {
-    modelValue,
-    headers,
-    baseApi,
-    action,
-    oss,
-    uploadAttrs,
-    uploadList,
-    beforeUpload,
-    handleUploadSuccess,
-    handleRemove
-  }
-}

+ 11 - 0
src/main.ts

@@ -53,12 +53,17 @@ import {
 import 'vxe-table/styles/cssvar.scss'
 import '@/utils/tableFormat'
 
+import FsAdminCore from '@fskj-admin/core'
+import '@fskj-admin/core/lib/style.css'
+
 import App from './App.vue'
 import router from './router'
 
 import './assets/main.css'
 import 'virtual:svg-icons-register'
 
+import { ossUpload } from '@/utils/utils'
+
 // import { useMainMicro } from '@/hooks/useMainMicro'
 
 function useTable(app: any) {
@@ -109,6 +114,12 @@ app.use(createPinia())
 app.use(router)
 app.use(ElementPlus)
 app.use(useTable)
+app.use(FsAdminCore, {
+  oss: true,
+  ossHost: '',
+  ossUpload,
+  baseApi: import.meta.env.VITE_BASE_API
+})
 registerComponent(app)
 
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {

+ 1 - 1
src/stores/user.ts

@@ -1,5 +1,5 @@
 import router from '@/router'
-import { useUserService } from '@/domain/user/service'
+import { useUserService } from '@/domains/user/service'
 
 const { login } = useUserService()
 export const useUserStore = defineStore({

+ 0 - 40
src/utils/utils.ts

@@ -1,6 +1,4 @@
 import dayjs from 'dayjs'
-import type { BasicFormItem, FormSlot } from '@/types/form'
-import { containerTypes } from './constants'
 import { ossPolicy } from '@/api/oss'
 
 export const formatDate = (date: any, format = 'YYYY-MM-DD HH:mm') => {
@@ -35,44 +33,6 @@ export const uuid = (len = 16) => {
   return uuid.join('')
 }
 
-export const placeholder = (item: BasicFormItem) => {
-  if (['select', 'cascader', 'date-picker', 'time-picker', 'time-select', 'dict'].includes(item.type)) {
-    return '请选择' + item.label
-  } else {
-    return '请输入' + item.label
-  }
-}
-
-// 构造表单插槽
-export const buildFormSlots = (formItems: Array<BasicFormItem>, formSlots: Array<FormSlot>) => {
-  formItems.forEach((formItem: BasicFormItem) => {
-    formItem.slots && Array.prototype.push.apply(formSlots, formItem.slots)
-    if (formItem.children) {
-      buildFormSlots(formItem.children, formSlots)
-    }
-  })
-}
-
-// 构造表单容器插槽
-export const buildContainerSlots = (formItems: Array<BasicFormItem>) => {
-  const recursionSlots = (formItems: Array<BasicFormItem>, formSlots: Array<FormSlot>) => {
-    formItems.forEach((formItem: BasicFormItem) => {
-      formItem.slots && Array.prototype.push.apply(formSlots, formItem.slots)
-      if (formItem.children) {
-        recursionSlots(formItem.children, formSlots)
-      }
-    })
-  }
-  formItems.forEach((formItem: BasicFormItem) => {
-    if (containerTypes.includes(formItem.type)) {
-      formItem.slots = formItem.slots || []
-      if (formItem.children) {
-        recursionSlots(formItem.children, formItem.slots)
-      }
-    }
-  })
-}
-
 // oss上传
 export const ossUpload = (param: any) => {
   const { file } = param

+ 2 - 2
src/views/system/Role.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import type { BasicForm, ICRUD } from '@/types/form'
-import { useRoleService } from '@/domain/role/service'
-import { useUserService } from '@/domain/user/service'
+import { useRoleService } from '@/domains/role/service'
+import { useUserService } from '@/domains/user/service'
 
 const roleService = useRoleService()
 const CRUD: ICRUD = {

+ 2 - 2
src/views/system/User.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import type { BasicForm, ICRUD } from '@/types/form'
-import { useUserService } from '@/domain/user/service'
-import { useRoleService } from '@/domain/role/service'
+import { useUserService } from '@/domains/user/service'
+import { useRoleService } from '@/domains/role/service'
 
 const curRow = ref<any>(null)