lanjianrong преди 4 години
родител
ревизия
3a0b49aa02

+ 1 - 1
.env.development

@@ -8,7 +8,7 @@ VITE_PUBLIC_PATH = /
 
 # Cross-domain proxy, you can configure multiple
 # Please note that no line breaks
-VITE_PROXY = [["/api","http://localhost:7070/api"],["/upload","http://localhost:3001/upload"]]
+VITE_PROXY = [["/api","http://localhost:7070/api"], ["/upload", "http://localhost:7070/api/upload"]]
 # VITE_PROXY=[["/api","https://vvbin.cn/test"]]
 
 # Delete console

+ 5 - 13
.vscode/settings.json

@@ -1,5 +1,6 @@
 {
   "typescript.tsdk": "./node_modules/typescript/lib",
+  "typescript.validate.enable": false,
   "volar.tsPlugin": true,
   "volar.tsPluginStatus": false,
   //===========================================
@@ -77,6 +78,7 @@
   },
   "files.exclude": {
     "**/bower_components": true,
+    "**/node_modules/**": true,
     "**/.idea": true,
     "**/tmp": true,
     "**/.git": true,
@@ -136,7 +138,6 @@
       "singleQuote": true
     }
   },
-  "javascript.updateImportsOnFileMove.enabled": "never",
   "liveServer.settings.donotShowInfoMsg": true,
   "terminal.integrated.rendererType": "dom",
   "telemetry.enableCrashReporter": false,
@@ -147,7 +148,6 @@
   },
   "prettier.requireConfig": true,
   "typescript.updateImportsOnFileMove.enabled": "always",
-  "typescript.validate.enable": false,
   "workbench.sideBar.location": "left",
   "[javascriptreact]": {
     "editor.defaultFormatter": "esbenp.prettier-vscode"
@@ -178,18 +178,10 @@
   },
   "[vue]": {
     "editor.codeActionsOnSave": {
-      "source.fixAll.eslint": false
+      "source.fixAll.eslint": true,
+      "source.fixAll.stylelint": true
     },
     "editor.defaultFormatter": "esbenp.prettier-vscode"
   },
-  "editor.formatOnSave": true,
-
-  "i18n-ally.localesPaths": ["src/locales/lang"],
-  "i18n-ally.keystyle": "nested",
-  "i18n-ally.sortKeys": true,
-  "i18n-ally.namespace": true,
-  "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
-  "i18n-ally.enabledParsers": ["ts"],
-  "i18n-ally.sourceLanguage": "en",
-  "i18n-ally.enabledFrameworks": ["vue", "react"]
+  "editor.formatOnSave": true
 }

+ 28 - 0
src/api/model/tree.ts

@@ -0,0 +1,28 @@
+export interface TreeResfulParams {
+  id: string
+  type: 'depth' | 'serial'
+  operation: 'upDepth' | 'downDepth' | 'upSerial' | 'downSerial'
+}
+
+export interface TreeResultModel {
+  id: string
+  parentId: string
+  name: string
+  depth: number
+  serial: number
+  attribution: string
+  code: string
+  contractId: string
+  createTime: string
+  children: TreeResultModel[]
+}
+
+export interface SectionAddParams {
+  id: string
+  name: string
+}
+
+export interface SectionFileParams {
+  id: string
+  contractId: string
+}

+ 2 - 2
src/api/sys/model/uploadModel.ts

@@ -1,5 +1,5 @@
 export interface UploadApiResult {
-  message: string
+  msg: string
   code: number
-  url: string
+  data: string
 }

+ 92 - 2
src/api/sys/tree.ts

@@ -1,16 +1,28 @@
+import {
+  SectionAddParams,
+  SectionFileParams,
+  TreeResfulParams,
+  TreeResultModel
+} from '../model/tree'
 import { defHttp } from '/@/utils/http/axios'
 
 import { ErrorMessageMode } from '/@/utils/http/axios/types'
 
 enum Api {
-  SectionAll = '/contract/section/all'
+  Section = '/contract/section',
+  SectionAll = '/contract/section/all',
+  AddSection = '/contract/section/add', // 新增项目节
+  UpdateSection = '/contract/section/update', // 更新项目节名称
+  UpOrDownSection = '/contract/section/serial', // 上下移
+  UpgradeOrRelegateSection = '/contract/section/depth', // 升降级
+  SaveSectionFile = '/contract/file' // 保存项目节附件
 }
 
 /**
  * @description: 获取设计表树结构
  */
 export function sectionAllApi(mode: ErrorMessageMode = 'message') {
-  return defHttp.get<LoginResultModel>(
+  return defHttp.get<TreeResultModel>(
     {
       url: Api.SectionAll
     },
@@ -19,3 +31,81 @@ export function sectionAllApi(mode: ErrorMessageMode = 'message') {
     }
   )
 }
+
+/**
+ * @description: 合同项目节上下、升降级
+ */
+export function treeResfulApi(
+  { id, type, operation }: TreeResfulParams,
+  mode: ErrorMessageMode = 'message'
+) {
+  return defHttp.post(
+    {
+      url: type === 'depth' ? Api.UpgradeOrRelegateSection : Api.UpOrDownSection,
+      params: { id, operation }
+    },
+    {
+      errorMessageMode: mode
+    }
+  )
+}
+
+/**
+ * @description 获取单个项目节详情
+ */
+export function treeDetailApi(id: string, mode: ErrorMessageMode = 'message') {
+  return defHttp.get(
+    {
+      url: Api.Section,
+      params: { id }
+    },
+    {
+      errorMessageMode: mode
+    }
+  )
+}
+
+/**
+ * @description 添加目录
+ */
+export function sectionAddApi(params: SectionAddParams, mode: ErrorMessageMode = 'message') {
+  return defHttp.post(
+    {
+      url: Api.AddSection,
+      params
+    },
+    {
+      errorMessageMode: mode
+    }
+  )
+}
+
+/**
+ * @description 更新目录
+ */
+export function sectionUpdateApi(params: SectionAddParams, mode: ErrorMessageMode = 'message') {
+  return defHttp.post(
+    {
+      url: Api.UpdateSection,
+      params
+    },
+    {
+      errorMessageMode: mode
+    }
+  )
+}
+
+/**
+ * @description 保存附件id
+ */
+export function saveSectionFileApi(params: SectionFileParams, mode: ErrorMessageMode = 'message') {
+  return defHttp.post(
+    {
+      url: Api.SaveSectionFile,
+      params
+    },
+    {
+      errorMessageMode: mode
+    }
+  )
+}

+ 8 - 13
src/components/Upload/src/BasicUpload.vue

@@ -11,27 +11,22 @@
             {{ fileListRef.length }}
           </template>
         </template>
-        <a-button @click="openPreviewModal">
+        <!-- <a-button @click="openPreviewModal">
           <Icon icon="bi:eye" />
           <template v-if="fileListRef.length && showPreviewNumber">
             {{ fileListRef.length }}
           </template>
-        </a-button>
+        </a-button> -->
       </Tooltip>
     </a-button-group>
 
-    <UploadModal
-      v-bind="bindValue"
-      :previewFileList="fileListRef"
-      @register="registerUploadModal"
-      @change="handleChange"
-    />
+    <UploadModal v-bind="bindValue" @register="registerUploadModal" @change="handleChange" />
 
-    <UploadPreviewModal
+    <!-- <UploadPreviewModal
       :value="fileListRef"
       @register="registerPreviewModal"
       @list-change="handlePreviewChange"
-    />
+    /> -->
   </div>
 </template>
 <script lang="ts">
@@ -60,7 +55,7 @@
       const [registerUploadModal, { openModal: openUploadModal }] = useModal()
 
       //   预览modal
-      const [registerPreviewModal, { openModal: openPreviewModal }] = useModal()
+      // const [registerPreviewModal, { openModal: openPreviewModal }] = useModal()
 
       const fileListRef = ref<string[]>([])
 
@@ -100,8 +95,8 @@
         openUploadModal,
         handleChange,
         handlePreviewChange,
-        registerPreviewModal,
-        openPreviewModal,
+        // registerPreviewModal,
+        // openPreviewModal,
         fileListRef,
         showPreview,
         bindValue,

+ 7 - 4
src/components/Upload/src/UploadModal.vue

@@ -134,6 +134,7 @@
           createMessage.error!(t('component.upload.acceptUpload', [accept.join(',')]))
           return false
         }
+
         const commonItem = {
           uuid: buildUUID(),
           file,
@@ -209,6 +210,9 @@
 
       // 点击开始上传
       async function handleStartUpload() {
+        console.log(fileListRef.value)
+        console.log(props.previewFileList)
+
         const { maxNumber } = props
         if ((fileListRef.value.length + props.previewFileList?.length ?? 0) > maxNumber) {
           return createMessage.warning(t('component.upload.maxNumber', [maxNumber]))
@@ -248,7 +252,7 @@
         for (const item of fileListRef.value) {
           const { status, responseData } = item
           if (status === UploadResultStatus.SUCCESS && responseData) {
-            fileList.push(responseData.url)
+            fileList.push(responseData.data)
           }
         }
         // 存在一个上传成功的即可保存
@@ -264,6 +268,8 @@
       function handleCloseFunc() {
         if (!isUploadingRef.value) {
           fileListRef.value = []
+          console.log(fileListRef.value)
+
           return true
         } else {
           createMessage.warning(t('component.upload.uploadWait'))
@@ -299,16 +305,13 @@
     .ant-upload-list {
       display: none;
     }
-
     .ant-table-wrapper .ant-spin-nested-loading {
       padding: 0;
     }
-
     &-toolbar {
       display: flex;
       align-items: center;
       margin-bottom: 8px;
-
       &__btn {
         margin-left: 8px;
         text-align: right;

+ 9 - 9
src/components/Upload/src/data.tsx

@@ -15,15 +15,15 @@ const { t } = useI18n()
 // 文件上传列表
 export function createTableColumns(): BasicColumn[] {
   return [
-    {
-      dataIndex: 'thumbUrl',
-      title: t('component.upload.legend'),
-      width: 100,
-      customRender: ({ record }) => {
-        const { thumbUrl } = (record as FileItem) || {}
-        return thumbUrl && <ThumbUrl fileUrl={thumbUrl} />
-      }
-    },
+    // {
+    //   dataIndex: 'thumbUrl',
+    //   title: t('component.upload.legend'),
+    //   width: 100,
+    //   customRender: ({ record }) => {
+    //     const { thumbUrl } = (record as FileItem) || {}
+    //     return thumbUrl && <ThumbUrl fileUrl={thumbUrl} />
+    //   }
+    // },
     {
       dataIndex: 'name',
       title: t('component.upload.fileName'),

+ 1 - 1
src/router/routes/modules/design.ts

@@ -9,7 +9,7 @@ const catalog: AppRouteModule = {
   component: LAYOUT,
   redirect: '/design/main',
   meta: {
-    title: '数量表列表'
+    title: '数量表管理'
   },
   children: [
     {

+ 187 - 12
src/views/design-directive/catalog/index.vue

@@ -1,22 +1,197 @@
 <template>
-  <div class="welcome">
-    <!-- <House /> -->
+  <div :class="prefixCls" class="m-5 bg-white">
+    <div class="flex flex-nowrap w-full h-full">
+      <div class="w-1/4 border-gray-400 border">
+        <header class="p-2 flex justify-between items-center h-12">
+          <div class="w-1/2">
+            <BasicUpload
+              v-show="showUploadBtn"
+              :max-size="20"
+              :max-number="1"
+              @change="handleChange"
+              :api="uploadApi"
+              :showPreviewNumber="false"
+              :emptyHidePreview="true"
+              :accept="['xlsx', 'xls', 'pdf']"
+            />
+          </div>
+          <div class="flex flex-row w-1/2 justify-end">
+            <span class="flex align-middle cursor-pointer" @click="handleTreeOpreate('upSerial')">
+              <Icon icon="mdi:arrow-up" :size="18" />
+              上移
+            </span>
+            <span
+              class="flex align-middle ml-1 cursor-pointer"
+              @click="handleTreeOpreate('downSerial')"
+            >
+              <Icon icon="mdi:arrow-down" :size="18" />
+              下移
+            </span>
+          </div>
+        </header>
+        <section>
+          <BasicTree
+            v-if="treeData.length"
+            :tree-data="treeData"
+            :replace-fields="replaceFields"
+            :show-line="true"
+            :defaultExpandAll="true"
+            @select="onSelect"
+          />
+        </section>
+      </div>
+      <div class="w-3/4 ml-4 flex flex-col">
+        <header class="h-1/12 mt-2 flex justify-between items-center">
+          <span class="text-3xl font-bold">{{ detail.name }}</span>
+          <section>
+            <AButton
+              v-show="showExcelUploadBtn"
+              type="primary"
+              size="small"
+              class="mr-4"
+              @click="uploadExecl"
+              >下载Excel</AButton
+            >
+            <!-- <AButton type="primary" size="small" class="mx-1">下载PDF</AButton> -->
+            <!-- <AButton type="primary" size="small" class="mx-1">编辑</AButton> -->
+            <!-- <span class="h-full border-l-1 border-gray-400 mx-1"></span> -->
+            <!-- <AButton type="primary" size="small" class="mx-1 mr-4">下载</AButton> -->
+          </section>
+        </header>
+        <section class="h-11/12 border-gray-400 border"> </section>
+      </div>
+    </div>
   </div>
 </template>
 <script lang="ts">
-  import { defineComponent } from 'vue'
-  // import House from '../house/index.vue'
+  import { computed, defineComponent, ref, watch } from 'vue'
+  import { saveSectionFileApi, sectionAllApi, treeDetailApi, treeResfulApi } from '/@/api/sys/tree'
+  import { useDesign } from '/@/hooks/web/useDesign'
+  import { BasicTree } from '/@/components/Tree/index'
+  import { Icon } from '/@/components/Icon'
+  import { message } from 'ant-design-vue'
+  import { TreeResultModel } from '/@/api/model/tree'
+  import { BasicUpload } from '/@/components/Upload'
+  import { uploadApi } from '/@/api/sys/upload'
+  import { TreeRow } from '/#/tree'
+  import { downloadByUrl } from '/@/utils/file/download'
+  // import PreviewPdf from './pdf.vue'
+  interface ATreeRow extends TreeRow {
+    showUpload: boolean
+  }
   export default defineComponent({
-    name: 'Catalog'
-    // components: { House }
+    name: 'Catalog',
+    components: { BasicTree, Icon, BasicUpload },
+    setup() {
+      const treeData = ref<TreeResultModel[]>([])
+      const row = ref<ATreeRow>({
+        id: '',
+        parentId: '',
+        name: '',
+        depth: 0,
+        serial: 0,
+        attribution: '',
+        code: '',
+        contractId: '',
+        createTime: '',
+        showUpload: false
+      })
+      const detail = ref({ name: '', content: '', filepath: '', filename: '', ext: '', fid: '' })
+      async function initData() {
+        const result = await sectionAllApi()
+        treeData.value = result.children
+      }
+
+      async function initSectionDetail(id: string) {
+        const result = await treeDetailApi(id)
+        detail.value = result
+      }
+      initData()
+
+      const handleTreeOpreate = async (operation: 'upSerial' | 'downSerial') => {
+        const id = row.value.id
+        if (!id) {
+          return message.error('请先选中节点')
+        }
+        await treeResfulApi({ id, type: 'serial', operation })
+        await initData()
+      }
+
+      const { prefixCls } = useDesign('catalog')
+      const replaceFields = { children: 'children', title: 'name', key: 'id' }
+
+      const onSelect = (selectedKeys: Event, { selectedNodes }) => {
+        if (selectedKeys.length) {
+          const {
+            id,
+            parentId,
+            name,
+            depth,
+            serial,
+            attribution,
+            code,
+            contractId,
+            children,
+            createTime
+          } = selectedNodes[0]?.props
+          row.value = {
+            showUpload: children && children.length ? false : true,
+            id,
+            parentId,
+            name,
+            depth,
+            serial,
+            attribution,
+            code,
+            contractId,
+            createTime
+          }
+        }
+      }
+
+      watch(row, (row, prevRow) => {
+        if (row.id !== prevRow.id) {
+          initSectionDetail(row.id)
+        }
+      })
+
+      const handleChange = async (list: string[]) => {
+        await saveSectionFileApi({ id: row.value.id, contractId: list[0] })
+        await initSectionDetail(row.value.id)
+      }
+
+      const showUploadBtn = computed(() => row.value.showUpload)
+
+      const showExcelUploadBtn = computed(() => /.(excel)|(xls)|(xlsl)$/.test(detail.value.ext))
+
+      const uploadExecl = () => {
+        downloadByUrl({
+          url: 'http://localhost:7070' + detail.value.filepath
+        })
+      }
+      return {
+        treeData,
+        replaceFields,
+        prefixCls,
+        onSelect,
+        handleTreeOpreate,
+        detail,
+        uploadApi,
+        handleChange,
+        row,
+        showUploadBtn,
+        showExcelUploadBtn,
+        uploadExecl
+      }
+    }
   })
 </script>
 <style lang="less" scoped>
-  .welcome {
-    display: flex;
-    width: 100%;
-    height: 100%;
-    justify-content: center;
-    align-items: center;
+  @prefix-cls: ~'@{namespace}-catalog';
+
+  .@{prefix-cls} {
+    width: calc(100% - 2.5rem);
+    height: calc(100% - 2.5rem);
+    overflow: hidden;
   }
 </style>

+ 303 - 12
src/views/design-directive/management/index.vue

@@ -1,22 +1,313 @@
 <template>
-  <div class="welcome">
-    <!-- <House /> -->
+  <div :class="prefixCls" class="m-5 bg-white">
+    <header class="p-3">
+      <AButton type="primary" size="small" @click="toggleModal('add')">添加目录</AButton>
+      <AButton type="primary" class="ml-2" size="small" @click="toggleModal('edit')">编辑</AButton>
+      <AButton type="primary" class="ml-2" size="small" @click="handleTreeOpreate('upSerial')"
+        >上移</AButton
+      >
+      <AButton type="primary" class="ml-2" size="small" @click="handleTreeOpreate('downSerial')"
+        >下移</AButton
+      >
+    </header>
+    <section class="p-3">
+      <ATable
+        v-if="treeData.length"
+        :data-source="treeData"
+        :columns="columns"
+        size="small"
+        row-key="id"
+        :pagination="false"
+        :defaultExpandAllRows="true"
+        :custom-row="customRow"
+        :row-class-name="rowClassName"
+      >
+        <template #name="{ text, record }">
+          <div class="editable-cell">
+            <div v-if="editableData[record.id]" class="editable-cell-input-wrapper">
+              <a-input
+                v-model:value="editableData[record.id].name"
+                @pressEnter="onSave(record.id)"
+              />
+              <check-outlined class="editable-cell-icon-check" @click="onSave(record.id)" />
+            </div>
+            <div v-else class="editable-cell-text-wrapper">
+              {{ text || ' ' }}
+              <edit-outlined class="editable-cell-icon" @click="onEdit(record.id, record)" />
+            </div>
+          </div>
+        </template>
+        <template #operation="{ record }">
+          <a-popconfirm v-if="treeData.length" title="确认删除?" @confirm="onDelete(record.id)">
+            <a>删除</a>
+          </a-popconfirm>
+        </template>
+      </ATable>
+    </section>
+    <AModal
+      :title="modalTitle"
+      :visible="visible"
+      @cancel="hideModal"
+      @ok="onConfirm"
+      :ok-button-props="{ size: 'small' }"
+      :cancel-button-props="{ size: 'small' }"
+    >
+      <div class="p-3">
+        <a-form :model="formState" :rules="rules" layout="vertical" ref="formRef">
+          <a-form-item name="name" required label="节点名称">
+            <a-input v-model:value="formState.name" />
+          </a-form-item>
+          <a-form-item name="id" required label="父级">
+            <a-tree-select
+              v-model:value="formState.id"
+              :treeData="treeData"
+              :replace-fields="replaceFields"
+              :tree-default-expand-all="true"
+              :dropdown-style="{ height: '20rem' }"
+            />
+          </a-form-item>
+        </a-form>
+      </div>
+    </AModal>
   </div>
 </template>
 <script lang="ts">
-  import { defineComponent } from 'vue'
-  // import House from '../house/index.vue'
+  import { computed, defineComponent, reactive, ref, toRaw, toRefs, UnwrapRef } from 'vue'
+  import { useDesign } from '/@/hooks/web/useDesign'
+  import { Table, Modal, Form, Input, TreeSelect, Popconfirm, message } from 'ant-design-vue'
+  import { CheckOutlined, EditOutlined } from '@ant-design/icons-vue'
+  import { sectionAddApi, sectionAllApi, sectionUpdateApi, treeResfulApi } from '/@/api/sys/tree'
+  import { TreeResultModel } from '/@/api/model/tree'
+  import { TreeRow } from '/#/tree'
+
+  interface FormState {
+    id: string
+    name: string
+  }
+  enum ModalType {
+    ADD = 'add',
+    EDIT = 'edit'
+  }
   export default defineComponent({
-    name: 'Management'
-    // components: { House }
+    name: 'Management',
+    components: {
+      ATable: Table,
+      AModal: Modal,
+      AForm: Form,
+      AInput: Input,
+      ATreeSelect: TreeSelect,
+      AFormItem: Form.Item,
+      CheckOutlined,
+      EditOutlined,
+      APopconfirm: Popconfirm
+    },
+    setup() {
+      const formRef = ref()
+      const { prefixCls } = useDesign('management')
+      const treeData = ref<TreeResultModel[]>([])
+      const editableData: UnwrapRef<Record<string, TreeRow>> = reactive({})
+
+      const row = ref<TreeRow>({
+        name: '',
+        code: '',
+        id: '',
+        parentId: '',
+        depth: 0,
+        serial: 0,
+        attribution: '',
+        contractId: '',
+        createTime: ''
+      })
+      const modal = reactive({
+        visible: false,
+        type: ''
+      })
+      const formState: UnwrapRef<FormState> = reactive({
+        id: '',
+        name: ''
+      })
+
+      // 目录操作相关
+      const toggleModal = (type?: string) => {
+        // 要打开form弹窗了, 设置当前的表单值
+        if (!modal.visible) {
+        }
+        modal.visible = !modal.visible
+        type && (modal.type = type)
+      }
+      const hideModal = () => (modal.visible = false)
+      const onSubmit = async () => {
+        const values = await formRef.value.validate()
+
+        const { type } = toRaw(modal)
+
+        if (type === ModalType.ADD) {
+          await sectionAddApi(values)
+        } else {
+          await sectionUpdateApi(values)
+        }
+      }
+
+      const onConfirm = () => {
+        try {
+          onSubmit()
+          initData()
+          toggleModal()
+        } catch (error) {}
+      }
+      async function initData() {
+        const result = await sectionAllApi()
+        treeData.value = result.children
+      }
+      initData()
+
+      const handleTreeOpreate = async (operation: 'upSerial' | 'downSerial') => {
+        const id = row.value.id
+        if (!id) {
+          return message.error('请先选中节点')
+        }
+        await treeResfulApi({ id, type: 'serial', operation })
+        await initData()
+      }
+
+      const modalTitle = computed(() => {
+        let title = ''
+        switch (modal.type) {
+          case ModalType.ADD:
+            title = '添加目录'
+            break
+          case ModalType.EDIT:
+            title = '编辑'
+            break
+
+          default:
+            break
+        }
+        return title
+      })
+      const columns = [
+        {
+          title: '名称',
+          dataIndex: 'name',
+          key: 'name',
+          slots: { customRender: 'name' }
+        },
+        {
+          title: '图表编码',
+          dataIndex: 'code',
+          key: 'code',
+          slots: { customRender: 'code' }
+        },
+        {
+          title: '操作',
+          dataIndex: 'operation',
+          slots: { customRender: 'operation' }
+        }
+      ]
+      const customRow = (record: TreeRow) => {
+        return {
+          onClick: () => {
+            row.value = record
+          }
+        }
+      }
+
+      const rowClassName = (record: TreeRow) => {
+        return record.id === row.value.id ? 'row-active' : ''
+      }
+
+      const onEdit = (key: string, record: TreeRow) => {
+        editableData[key] = record
+      }
+
+      const onSave = (key: string) => {
+        delete editableData[key]
+        // 执行保存请求 后初始化数据
+        // initData()
+      }
+
+      const onDelete = (key: string) => {
+        // 执行删除请求 后初始化数据
+        // initData()
+      }
+
+      const rules = {
+        name: [{ required: true, message: '请输入名称' }],
+        id: [{ required: true, message: '请选择父节点' }]
+      }
+
+      const replaceFields = { children: 'children', title: 'name', value: 'id' }
+
+      return {
+        ...toRefs(modal),
+        prefixCls,
+        treeData,
+        columns,
+        toggleModal,
+        formRef,
+        hideModal,
+        onConfirm,
+        modalTitle,
+        customRow,
+        rowClassName,
+        formState,
+        rules,
+        replaceFields,
+        editableData,
+        onEdit,
+        onSave,
+        onDelete,
+        handleTreeOpreate
+      }
+    }
   })
 </script>
 <style lang="less" scoped>
-  .welcome {
-    display: flex;
-    width: 100%;
-    height: 100%;
-    justify-content: center;
-    align-items: center;
+  @prefix-cls: ~'@{namespace}-management';
+
+  .@{prefix-cls} {
+    width: calc(100% - 2.5rem);
+    height: calc(100% - 2.5rem);
+    ::v-deep(.ant-table-row.row-active) {
+      background: #cdefff;
+    }
+    ::v-deep(.ant-table-row > td:first-of-type) {
+      display: flex;
+      align-items: center;
+    }
+    .editable-cell {
+      position: relative;
+      .editable-cell-input-wrapper,
+      .editable-cell-text-wrapper {
+        padding-right: 24px;
+      }
+      .editable-cell-text-wrapper {
+        padding: 5px 24px 5px 5px;
+      }
+      .editable-cell-icon,
+      .editable-cell-icon-check {
+        position: absolute;
+        right: 0;
+        width: 20px;
+        cursor: pointer;
+      }
+      .editable-cell-icon {
+        display: none;
+        margin-top: 4px;
+      }
+      .editable-cell-icon-check {
+        line-height: 28px;
+      }
+      .editable-cell-icon:hover,
+      .editable-cell-icon-check:hover {
+        color: #108ee9;
+      }
+      .editable-add-btn {
+        margin-bottom: 8px;
+      }
+    }
+    .editable-cell:hover .editable-cell-icon {
+      display: inline-block;
+    }
   }
 </style>

+ 7 - 6
src/views/sys/preview/index.vue

@@ -7,7 +7,7 @@
     <div class="flex py-5 container w-full">
       <div class="w-1/4 border-gray-300 border-1 border">
         <BasicTree
-          v-if="treeData && treeData.length"
+          v-if="treeData.length"
           :tree-data="treeData"
           :replace-fields="replaceFields"
           :show-line="true"
@@ -20,7 +20,8 @@
 </template>
 
 <script lang="ts">
-  import { onMounted, ref } from 'vue'
+  import { ref } from 'vue'
+  import { TreeResultModel } from '/@/api/model/tree'
   import { sectionAllApi } from '/@/api/sys/tree'
   import { BasicTree } from '/@/components/Tree/index'
   import { useDesign } from '/@/hooks/web/useDesign'
@@ -30,14 +31,14 @@
       BasicTree
     },
     setup() {
-      const treeData = ref([])
+      const treeData = ref<TreeResultModel[]>([])
+
       async function initData() {
         const result = await sectionAllApi()
         treeData.value = result.children
       }
-      onMounted(() => {
-        initData()
-      })
+
+      initData()
 
       const { prefixCls } = useDesign('preview')
       const replaceFields = { children: 'children', title: 'name', key: 'id' }

+ 11 - 0
types/tree.d.ts

@@ -0,0 +1,11 @@
+import { TreeResultModel } from '/@/api/model/tree'
+
+type TreeRow = Omit<TreeResultModel, 'children'>
+
+type PdfData = {
+  pageNo: number
+  pdfPageNumber: number
+  pdfTotalPages: number
+  renderingPage: boolean
+  scale: number // 缩放值
+}