瀏覽代碼

feat: 预览页完善

lanjianrong 4 年之前
父節點
當前提交
0ba6e7f4ae

+ 2 - 2
.eslintrc.js

@@ -65,9 +65,9 @@ module.exports = {
     'vue/max-attributes-per-line': 'off',
     'vue/multiline-html-element-content-newline': 'off',
     'vue/singleline-html-element-content-newline': 'off',
-    'vue/attribute-hyphenation': 'off',
-    // 'vue/html-self-closing': 'off',
+    'vue/attribute-hyphenation': 'error',
     'vue/require-default-prop': 'off',
+    'vue/component-name-in-template-casing': ['error', 'PascalCase'],
     'vue/html-self-closing': [
       'error',
       {

+ 11 - 1
src/router/routes/index.ts

@@ -36,5 +36,15 @@ export const LoginRoute: AppRouteRecordRaw = {
   }
 }
 
+export const PreviewRoute: AppRouteRecordRaw = {
+  path: '/preview',
+  name: 'Preview',
+  component: () => import('/@/views/sys/preview/index.vue'),
+  meta: {
+    title: '预览页',
+    ignoreAuth: true
+  }
+}
+
 // Basic routing without permission
-export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE]
+export const basicRoutes = [LoginRoute, PreviewRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE]

+ 1 - 11
src/router/routes/mainOut.ts

@@ -7,16 +7,6 @@ import type { AppRouteModule } from '/@/router/types'
 
 // test
 // http:ip:port/main-out
-export const mainOutRoutes: AppRouteModule[] = [
-  {
-    path: '/preview',
-    name: 'Preview',
-    component: () => import('/@/views/sys/preview/index.vue'),
-    meta: {
-      title: '预览页',
-      ignoreAuth: true
-    }
-  }
-]
+export const mainOutRoutes: AppRouteModule[] = []
 
 export const mainOutRouteNames = mainOutRoutes.map(item => item.name)

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

@@ -9,7 +9,8 @@ const catalog: AppRouteModule = {
   component: LAYOUT,
   redirect: '/design/main',
   meta: {
-    title: '数量表管理'
+    title: '设计表管理',
+    icon: 'mdi:file-table-box-multiple'
   },
   children: [
     {

+ 11 - 7
src/store/modules/user.ts

@@ -1,8 +1,4 @@
-import type {
-  LoginParams,
-  GetUserInfoByUserIdModel,
-  GetUserInfoByUserIdParams
-} from '/@/api/sys/model/userModel'
+import type { LoginParams, GetUserInfoByUserIdModel } from '/@/api/sys/model/userModel'
 import type { UserInfo } from '/@/store/types'
 
 import store from '/@/store/index'
@@ -21,7 +17,8 @@ import { loginApi, getUserInfoById } from '/@/api/sys/user'
 
 import { useI18n } from '/@/hooks/web/useI18n'
 import { ErrorMessageMode } from '/@/utils/http/axios/types'
-import { getAuthCache, setAuthCache } from '/@/utils/auth/index'
+import { getAuthCache, removeAuthCache, setAuthCache } from '/@/utils/auth/index'
+import { mainOutRoutes } from '/@/router/routes/mainOut'
 
 const NAME = 'app-user'
 hotModuleUnregisterModule(NAME)
@@ -119,6 +116,10 @@ class User extends VuexModule {
   @Action
   async logout(goLogin = false) {
     goLogin && router.push(PageEnum.BASE_LOGIN)
+    if (!goLogin && !router.hasRoute('Preview')) {
+      router.addRoute('', mainOutRoutes[0])
+      router.push(PageEnum.BASE_HOME)
+    }
   }
 
   /**
@@ -133,7 +134,10 @@ class User extends VuexModule {
       title: t('sys.app.logoutTip'),
       content: t('sys.app.logoutMessage'),
       onOk: async () => {
-        await this.logout(true)
+        removeAuthCache(ROLES_KEY)
+        removeAuthCache(TOKEN_KEY)
+        removeAuthCache(USER_INFO_KEY)
+        await this.logout(false)
       }
     })
   }

+ 5 - 0
src/utils/auth/index.ts

@@ -19,3 +19,8 @@ export function setAuthCache(key: BasicKeys, value) {
   const fn = isLocal ? Persistent.setLocal : Persistent.setSession
   return fn(key, value)
 }
+
+export function removeAuthCache(key: BasicKeys) {
+  const fn = isLocal ? Persistent.removeLocal : Persistent.removeSession
+  return fn(key)
+}

+ 19 - 11
src/views/design-directive/catalog/index.vue

@@ -1,7 +1,7 @@
 <template>
   <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">
+      <div class="w-1/4 border-gray-400 border h-full">
         <header class="p-2 flex justify-between items-center h-12">
           <div class="w-1/2">
             <BasicUpload
@@ -10,8 +10,8 @@
               :max-number="1"
               @change="handleChange"
               :api="uploadApi"
-              :showPreviewNumber="false"
-              :emptyHidePreview="true"
+              :show-preview-number="false"
+              :empty-hide-preview="true"
               :accept="['xlsx', 'xls', 'pdf']"
             />
           </div>
@@ -29,16 +29,16 @@
             </span>
           </div>
         </header>
-        <section>
-          <BasicTree
+        <div class="tree">
+          <Tree
             v-if="treeData.length"
             :tree-data="treeData"
             :replace-fields="replaceFields"
             :show-line="true"
-            :defaultExpandAll="true"
+            :default-expand-all="true"
             @select="onSelect"
           />
-        </section>
+        </div>
       </div>
       <div class="w-3/4 ml-4 flex flex-col">
         <header class="h-1/12 mt-2 flex justify-between items-center">
@@ -77,19 +77,18 @@
   import { useDesign } from '/@/hooks/web/useDesign'
   import { BasicTree } from '/@/components/Tree/index'
   import { Icon } from '/@/components/Icon'
-  import { message } from 'ant-design-vue'
+  import { message, Tree } 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: { BasicTree, Icon, BasicUpload, PreviewPdf },
+    components: { BasicTree, Icon, BasicUpload, Tree },
     setup() {
       const treeData = ref<TreeResultModel[]>([])
       const row = ref<ATreeRow>({
@@ -100,6 +99,7 @@
         serial: 0,
         attribution: '',
         code: '',
+        code2: '',
         contractId: '',
         createTime: '',
         showUpload: false
@@ -138,12 +138,13 @@
             serial,
             attribution,
             code,
+            code2,
             contractId,
             children,
             createTime
           } = selectedNodes[0]?.props
           row.value = {
-            showUpload: children && children.length ? false : true,
+            showUpload: depth > 2 ? (children && children.length ? false : true) : false,
             id,
             parentId,
             name,
@@ -151,6 +152,7 @@
             serial,
             attribution,
             code,
+            code2,
             contractId,
             createTime
           }
@@ -216,5 +218,11 @@
     width: calc(100% - 2.5rem);
     height: calc(100% - 2.5rem);
     overflow: hidden;
+    .tree {
+      height: calc(100% - 48px);
+      max-height: 809px;
+      overflow-x: scroll;
+      overflow-y: scroll;
+    }
   }
 </style>

+ 50 - 21
src/views/design-directive/management/index.vue

@@ -16,20 +16,22 @@
         :data-source="treeData"
         :columns="columns"
         size="small"
+        :bordered="true"
         row-key="id"
         :pagination="false"
-        :defaultExpandAllRows="true"
+        :default-expand-all-rows="true"
         :custom-row="customRow"
         :row-class-name="rowClassName"
+        :scroll="{ y: scrollY }"
       >
         <template #name="{ text, record }">
           <div class="editable-cell">
             <div v-if="editableData[record.id]" class="editable-cell-input-wrapper">
-              <a-input
+              <AInput
                 v-model:value="editableData[record.id].name"
                 @pressEnter="onSave(record.id)"
               />
-              <check-outlined class="editable-cell-icon-check" @click="onSave(record.id)" />
+              <CheckOutlined class="editable-cell-icon-check" @click="onSave(record.id)" />
 
               <CloseOutlined
                 class="editable-cell-icon-check"
@@ -39,14 +41,14 @@
             </div>
             <div v-else class="editable-cell-text-wrapper">
               {{ text || ' ' }}
-              <edit-outlined class="editable-cell-icon" @click="onEdit(record.id, record)" />
+              <EditOutlined 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)">
+          <APopconfirm v-if="treeData.length" title="确认删除?" @confirm="onDelete(record.id)">
             <a>删除</a>
-          </a-popconfirm>
+          </APopconfirm>
         </template>
       </ATable>
     </section>
@@ -59,34 +61,45 @@
       :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" autocomplete="off" class="w-6" />
-          </a-form-item>
-          <a-form-item
+        <AForm :model="formState" :rules="rules" layout="vertical" ref="formRef">
+          <AFormItem name="name" required label="名称">
+            <AInput v-model:value="formState.name" autocomplete="off" class="w-6" />
+          </AFormItem>
+          <AFormItem
             name="id"
             required
             :label="treeSelectLabel"
             :hidden="type === 'edit' ? true : false"
           >
-            <a-tree-select
+            <ATreeSelect
               v-model:value="formState.id"
-              :treeData="treeSelectData"
+              :tree-data="treeSelectData"
               :replace-fields="replaceFields"
               :tree-default-expand-all="true"
               :dropdown-style="{ height: '20rem' }"
             />
-          </a-form-item>
-          <a-form-item name="code2" label="图标编码">
-            <a-input v-model:value="formState.code2" autocomplete="off" />
-          </a-form-item>
-        </a-form>
+          </AFormItem>
+          <AFormItem name="code2" label="图标编码">
+            <AInput v-model:value="formState.code2" autocomplete="off" />
+          </AFormItem>
+        </AForm>
       </div>
     </AModal>
   </div>
 </template>
 <script lang="ts">
-  import { computed, defineComponent, reactive, ref, toRaw, toRefs, unref, UnwrapRef } from 'vue'
+  import {
+    computed,
+    defineComponent,
+    onMounted,
+    onUnmounted,
+    reactive,
+    ref,
+    toRaw,
+    toRefs,
+    unref,
+    UnwrapRef
+  } from 'vue'
   import { useDesign } from '/@/hooks/web/useDesign'
   import { Table, Modal, Form, Input, TreeSelect, Popconfirm, message } from 'ant-design-vue'
   import { CheckOutlined, EditOutlined, CloseOutlined } from '@ant-design/icons-vue'
@@ -100,6 +113,7 @@
   import { TreeResultModel } from '/@/api/model/tree'
   import { TreeRow } from '/#/tree'
   import { useMessage } from '/@/hooks/web/useMessage'
+  import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'
 
   interface FormState {
     id: string
@@ -129,7 +143,16 @@
       const { prefixCls } = useDesign('management')
       const treeData = ref<TreeResultModel[]>([])
       const editableData: UnwrapRef<Record<string, TreeRow>> = reactive({})
-
+      const scrollY = ref<number | null>(null)
+      function autoTableScroll() {
+        const bodyFontSize =
+          parseInt(window.getComputedStyle(document.body).fontSize.replace('px', '')) * 1.25 * 2
+        scrollY.value =
+          document.body.clientHeight - 48 - 29 - 48 - 39 - bodyFontSize - bodyFontSize * 0.75 * 2
+      }
+      useWindowSizeFn(autoTableScroll, 150, { immediate: true })
+      // onMounted(() => start())
+      // onUnmounted(() => stop())
       const row = ref<TreeRow>({
         name: '',
         code: '',
@@ -160,6 +183,8 @@
             const unRow = unref(row)
             if (type === ModalType.ADD) {
               formState.id = unRow.depth === 3 ? unRow.parentId : unRow.id
+              formState.name = ''
+              formState.code2 = ''
             } else {
               formState.id = unRow.id
               formState.name = unRow.name
@@ -228,17 +253,20 @@
           title: '名称',
           dataIndex: 'name',
           key: 'name',
+          width: '60%',
           slots: { customRender: 'name' }
         },
         {
           title: '图表编码',
           dataIndex: 'code2',
           key: 'code2',
+          width: '30%',
           slots: { customRender: 'code2' }
         },
         {
           title: '操作',
           dataIndex: 'operation',
+          width: '10%',
           slots: { customRender: 'operation' }
         }
       ]
@@ -321,7 +349,8 @@
         handleTreeOpreate,
         treeSelectData,
         treeSelectLabel,
-        onCancel
+        onCancel,
+        scrollY
       }
     }
   })

+ 4 - 14
src/views/sys/login/Login.vue

@@ -2,11 +2,11 @@
   <div :class="prefixCls" class="relative w-full h-full px-4">
     <AppLocalePicker
       class="absolute top-4 right-4 enter-x text-white xl:text-gray-600"
-      :showText="false"
+      :show-text="false"
     />
 
     <span class="-enter-x xl:hidden">
-      <AppLogo :alwaysShowTitle="true" />
+      <AppLogo :always-show-title="true" />
     </span>
 
     <div class="container relative h-full py-2 mx-auto sm:px-10">
@@ -20,10 +20,10 @@
               class="w-1/2 -mt-16 -enter-x"
             />
             <div class="mt-10 font-medium text-white -enter-x">
-              <span class="mt-4 text-3xl inline-block"> {{ t('sys.login.signInTitle') }}</span>
+              <!-- <span class="mt-4 text-3xl inline-block"> {{ t('sys.login.signInTitle') }}</span> -->
             </div>
             <div class="mt-5 text-md text-white font-normal dark:text-gray-500 -enter-x">
-              {{ t('sys.login.signInDesc') }}
+              <!-- {{ t('sys.login.signInDesc') }} -->
             </div>
           </div>
         </div>
@@ -92,7 +92,6 @@
     @media (max-width: @screen-xl) {
       background: linear-gradient(180deg, #1c3faa, #1c3faa);
     }
-
     &::before {
       position: absolute;
       top: 0;
@@ -114,46 +113,38 @@
       position: absolute;
       top: 12px;
       height: 30px;
-
       &__title {
         font-size: 16px;
         color: #fff;
       }
-
       img {
         width: 32px;
       }
     }
-
     .container {
       .@{logo-prefix-cls} {
         display: flex;
         width: 60%;
         height: 80px;
-
         &__title {
           font-size: 24px;
           color: #fff;
         }
-
         img {
           width: 48px;
         }
       }
     }
-
     &-sign-in-way {
       .anticon {
         font-size: 22px;
         color: #888;
         cursor: pointer;
-
         &:hover {
           color: @primary-color;
         }
       }
     }
-
     input:not([type='checkbox']) {
       min-width: 360px;
 
@@ -172,7 +163,6 @@
     .@{countdown-prefix-cls} input {
       min-width: unset;
     }
-
     .ant-divider-inner-text {
       font-size: 12px;
       color: @text-color-secondary;

+ 112 - 10
src/views/sys/preview/index.vue

@@ -5,34 +5,60 @@
       <AButton size="small" @click="$router.push('/login')">登录</AButton>
     </div>
     <div class="flex py-5 container w-full">
-      <div class="w-1/4 border-gray-300 border-1 border">
-        <BasicTree
+      <div class="w-1/4 border-gray-300 border-1 border overflow-y-scroll overflow-x-hidden">
+        <Tree
           v-if="treeData.length"
           :tree-data="treeData"
           :replace-fields="replaceFields"
           :show-line="true"
-          :defaultExpandAll="true"
+          :default-expand-all="true"
+          @select="onSelect"
         />
       </div>
-      <div class="w-3/4 border-gray-300 border-1 border ml-10"></div>
+      <div class="w-3/4 border-gray-300 border-1 border ml-10">
+        <iframe v-if="showPdf" :src="pdfUrl" frameborder="0" width="100%" height="100%"></iframe>
+        <div class="w-full h-full flex items-center justify-center">
+          <AButton v-show="showExcel" type="primary" size="small" class="mr-4" @click="uploadExecl"
+            >下载Excel
+          </AButton>
+        </div>
+      </div>
     </div>
   </div>
 </template>
 
 <script lang="ts">
-  import { ref } from 'vue'
+  import { ref, watch, computed } from 'vue'
   import { TreeResultModel } from '/@/api/model/tree'
-  import { sectionAllApi } from '/@/api/sys/tree'
-  import { BasicTree } from '/@/components/Tree/index'
+  import { sectionAllApi, treeDetailApi } from '/@/api/sys/tree'
   import { useDesign } from '/@/hooks/web/useDesign'
+  import { TreeRow } from '/#/tree'
+  import { Tree } from 'ant-design-vue'
+  import { downloadByUrl } from '/@/utils/file/download'
+  interface ATreeRow extends TreeRow {
+    showUpload: boolean
+  }
   export default {
     name: 'Preview',
     components: {
-      BasicTree
+      Tree
     },
     setup() {
       const treeData = ref<TreeResultModel[]>([])
-
+      const row = ref<ATreeRow>({
+        id: '',
+        parentId: '',
+        name: '',
+        depth: 0,
+        serial: 0,
+        attribution: '',
+        code: '',
+        code2: '',
+        contractId: '',
+        createTime: '',
+        showUpload: false
+      })
+      const detail = ref({ name: '', content: '', filepath: '', filename: '', ext: '', fid: '' })
       async function initData() {
         const result = await sectionAllApi()
         treeData.value = result.children
@@ -40,9 +66,85 @@
 
       initData()
 
+      const onSelect = (selectedKeys: Event, { selectedNodes }) => {
+        if (selectedKeys!.length) {
+          const {
+            id,
+            parentId,
+            name,
+            depth,
+            serial,
+            attribution,
+            code,
+            code2,
+            contractId,
+            children,
+            createTime
+          } = selectedNodes[0]?.props
+          row.value = {
+            showUpload: children && children.length ? false : true,
+            id,
+            parentId,
+            name,
+            depth,
+            serial,
+            attribution,
+            code,
+            code2,
+            contractId,
+            createTime
+          }
+        }
+      }
+
+      async function initSectionDetail(id: string) {
+        const result = await treeDetailApi(id)
+        detail.value = result
+      }
+
+      watch(row, (row, prevRow) => {
+        if (row.id !== prevRow.id) {
+          initSectionDetail(row.id)
+        }
+      })
+
+      const showExcel = computed(() => {
+        if (!detail.value.fid) {
+          return false
+        }
+        return /.(excel)|(xls)|(xlsl)$/.test(detail.value.ext)
+      })
+
+      const showPdf = computed(() => {
+        if (!detail.value.fid) {
+          return false
+        }
+        const isPdf = /.(pdf)|(PDF)$/.test(detail.value.ext)
+        if (!isPdf) {
+          return false
+        }
+        return true
+      })
+
+      const pdfUrl = computed(() => 'http://localhost:7070' + detail.value.filepath)
+
+      const uploadExecl = () => {
+        downloadByUrl({
+          url: 'http://localhost:7070' + detail.value.filepath
+        })
+      }
       const { prefixCls } = useDesign('preview')
       const replaceFields = { children: 'children', title: 'name', key: 'id' }
-      return { treeData, replaceFields, prefixCls }
+      return {
+        treeData,
+        replaceFields,
+        prefixCls,
+        onSelect,
+        showPdf,
+        pdfUrl,
+        uploadExecl,
+        showExcel
+      }
     }
   }
 </script>