Pārlūkot izejas kodu

重构多个组件,优化props定义方式,使用defineProps简化代码;更新ztTableFooter和ztTableSetting组件以支持默认值;删除不再使用的登录演示页面,提升代码整洁性和可读性。

xbx 1 nedēļu atpakaļ
vecāks
revīzija
c9bb6696f6

+ 2 - 2
src/components/Descriptions/src/DescriptionsItemLabel.vue

@@ -1,8 +1,8 @@
 <script setup lang="ts">
 import { Component } from 'vue'
 
-// 修改props,接收Vue组件而非字符串
-const props = defineProps({
+// 使用defineProps而不赋值给变量
+defineProps({
   label: {
     type: String,
     required: true

+ 6 - 6
src/components/Editor/src/Editor.vue

@@ -1,8 +1,7 @@
 <script lang="ts" setup>
-import { PropType } from 'vue'
+import { PropType, computed, ref, shallowRef, unref, watch } from 'vue'
 import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
 import { i18nChangeLanguage, IDomEditor, IEditorConfig } from '@wangeditor/editor'
-import { propTypes } from '@/utils/propTypes'
 import { isNumber } from '@/utils/is'
 import { ElMessage } from 'element-plus'
 import { useLocaleStore } from '@/store/modules/locale'
@@ -19,15 +18,16 @@ const currentLocale = computed(() => localeStore.getCurrentLocale)
 
 i18nChangeLanguage(unref(currentLocale).lang)
 
+// 使用普通的props定义方式,不使用propTypes
 const props = defineProps({
-  editorId: propTypes.string.def('wangeEditor-1'),
-  height: propTypes.oneOfType([Number, String]).def('500px'),
+  editorId: { type: String, default: 'wangeEditor-1' },
+  height: { type: [Number, String], default: '500px' },
   editorConfig: {
     type: Object as PropType<Partial<IEditorConfig>>,
     default: () => undefined
   },
-  readonly: propTypes.bool.def(false),
-  modelValue: propTypes.string.def('')
+  readonly: { type: Boolean, default: false },
+  modelValue: { type: String, default: '' }
 })
 
 const emit = defineEmits(['change', 'update:modelValue'])

+ 6 - 7
src/components/Table/components/ztTableFooter.vue

@@ -7,23 +7,24 @@
 </template>
 <script lang="ts" setup>
 import { Descriptions , useDescription } from '@/components/Descriptions';
-import { propTypes } from '@/utils/propTypes'
-import { DescriptionsSchema, DescriptionsProps, DescInstance } from '@/components/Descriptions/src/types'
+import { PropType, ref, watchEffect } from 'vue';
+import { DescriptionsSchema } from '@/components/Descriptions/src/types'
 defineOptions({ name: 'BasicTableFooter' })
 
 const props = defineProps({
   summaryData: {
-    type: Object
+    type: Object,
+    default: () => ({})
   },
   schema: {
-    type: Array as PropType<DescriptionsSchema[]>
+    type: Array as PropType<DescriptionsSchema[]>,
+    default: () => []
   }
 })
 
 const curData = ref()
 
 watchEffect(() => {
-
   curData.value = props.summaryData
 })
 
@@ -33,9 +34,7 @@ const [register] = useDescription({
   data: curData,
   schema: props.schema
 })
-
 </script>
 
 <style scoped lang="scss">
-
 </style>

+ 1 - 5
src/components/Table/components/ztTableSetting.vue

@@ -6,7 +6,7 @@
 </template>
 <script lang="ts" setup>
 import type { TableSetting } from '@/types/table'
-import { PropType } from 'vue'
+import { PropType, computed } from 'vue'
 import RedoSetting from './redoSetting.vue'
 import ColumnSetting from "./columnSetting.vue"
 
@@ -22,8 +22,6 @@ const props = defineProps({
   }
 })
 
-
-
 const getSetting = computed<TableSetting>(() => {
   return Object.assign(
     {
@@ -44,8 +42,6 @@ const getSetting = computed<TableSetting>(() => {
     margin-right: 12px;
   }
 
- 
-
   .zt-icon{
     cursor: pointer;
   }

+ 71 - 69
src/components/helpMessage/src/index.vue

@@ -1,81 +1,83 @@
-<script lang="tsx">
-import type { CSSProperties } from 'vue'
-import { defineComponent, computed, unref, PropType } from 'vue'
+<template>
+  <ElTooltip
+    popper-class="basic-help__wrap"
+    :placement="placement"
+  >
+    <template #content>
+      <div>
+        <!-- 字符串直接渲染 -->
+        <p v-if="isString(text)">{{ text }}</p>
+        
+        <!-- 数组循环渲染 -->
+        <template v-else-if="isArray(text)">
+          <p v-for="(item, index) in text" :key="item">
+            <span>
+              <template v-if="showIndex">{{ index + 1 }}. </template>
+              {{ item }}
+            </span>
+          </p>
+        </template>
+      </div>
+    </template>
+    
+    <!-- 默认插槽 -->
+    <slot></slot>
+    
+    <!-- 问号图标 -->
+    <div class="help-icon">
+      <el-icon :style="tooltipStyle">
+        <QuestionFilled />
+      </el-icon>
+    </div>
+  </ElTooltip>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
 import { ElTooltip, ElIcon } from 'element-plus'
 import { QuestionFilled } from '@element-plus/icons-vue'
 import { isString, isArray } from '@/utils/is'
+import type { CSSProperties } from 'vue'
 
-const props = {
-  //最大宽度
-  maxWidth: { type: String, default: '600px' },
-  //颜色
-  color: { type: String, default: '#000' },
-  //是否显示多行的序号比如1.xxxx2.xxxx
-  showIndex: { type: Boolean, default: true },
-  //字体
-  fontSize: { type: Number, default: 15 },
-  //弹窗位置
-  placement: { type: String, default: 'right' },
-  //文字提示字符或列表
-  text: { type: [Array, String] as PropType<string[] | string> }
-}
-
-
-export default defineComponent({
-  name: 'BasicHelp',
-  components: { ElTooltip, ElIcon, QuestionFilled },
-  props: props,
-  setup(props, { slots }) {
-   
-    const getTooltipStyle = computed(
-      (): CSSProperties => ({ color: props.color, fontSize: `${props.fontSize}px` })
-    )
-
-    // 数组渲染成多列,文字就一行
-    function renderTitle() {
-      const textList = props.text
-      // 一行就一个p包裹
-      if (isString(textList)) {
-        return <p>{textList}</p>
-      }
+defineOptions({
+  name: 'BasicHelp'
+})
 
-      if (isArray(textList)) {
-        return (textList as string[]).map((text, index) => {
-          return (
-            <p key={text}>
-                <span>
-                  {props.showIndex ? `${index + 1}. ` : ''}
-                {text}
-              </span>
-            </p>
-          )
-        })
-      }
+// 定义 Tooltip 的 placement 类型
+type Placement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end';
 
-      return null
-    }
+interface Props {
+  // 最大宽度
+  maxWidth?: string;
+  // 颜色
+  color?: string;
+  // 是否显示多行的序号比如1.xxxx2.xxxx
+  showIndex?: boolean;
+  // 字体
+  fontSize?: number;
+  // 弹窗位置
+  placement?: Placement;
+  // 文字提示字符或列表
+  text?: string[] | string;
+}
 
-    return () => {
-      return (
-        <ElTooltip
-          popperClass="basic-help__wrap"
-          placement={props.placement as any}
-          v-slots={{
-            content: () => <div>{renderTitle()}</div>
-          }}
-        >
-          {slots.default?.()}
-          <div class="help-icon">
-            <ElIcon style={unref(getTooltipStyle)}>
-              <QuestionFilled />
-            </ElIcon>
-          </div>
-        </ElTooltip>
-      )
-    }
-  }
+const props = withDefaults(defineProps<Props>(), {
+  maxWidth: '600px',
+  color: '#000',
+  showIndex: true,
+  fontSize: 15,
+  placement: 'right'
 })
+
+// 计算tooltip样式
+const tooltipStyle = computed(
+  (): CSSProperties => ({ 
+    color: props.color, 
+    fontSize: `${props.fontSize}px` 
+  })
+)
 </script>
+
 <style lang="scss">
 .basic-help__wrap {
   p {

+ 26 - 0
src/router/routes/modules/library.ts

@@ -0,0 +1,26 @@
+import { AppRouteRecordRaw } from '../types';
+
+const LIBRARY_MANAGEMENT: AppRouteRecordRaw = {
+  path: '/library',
+  name: 'LibraryManagement',
+  component: () => import('@/layout/default-layout.vue'),
+  meta: {
+    title: '书库管理',
+    
+    order: 1,
+    icon: 'BookFilled',
+  },
+  children: [
+    {
+      path: 'stack',
+      name: 'LibraryStack',
+      component: () => import('@/views/library/stack/index.vue'),
+      meta: {
+        title: '书库列表',
+        requiresAuth: true,
+      },
+    }
+  ]
+};
+
+export default LIBRARY_MANAGEMENT; 

+ 24 - 0
src/types/components.d.ts

@@ -8,19 +8,43 @@ export {}
 /* prettier-ignore */
 declare module 'vue' {
   export interface GlobalComponents {
+    DescriptionsSrcDescriptions: typeof import('./../components/Descriptions/src/Descriptions.vue')['default']
+    DescriptionsSrcDescriptionsItemLabel: typeof import('./../components/Descriptions/src/DescriptionsItemLabel.vue')['default']
     Dialog: typeof import('./../components/Dialog/index.vue')['default']
     EditorSrcEditor: typeof import('./../components/Editor/src/Editor.vue')['default']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElCard: typeof import('element-plus/es')['ElCard']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElInput: typeof import('element-plus/es')['ElInput']
+    ElOption: typeof import('element-plus/es')['ElOption']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSpace: typeof import('element-plus/es')['ElSpace']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTag: typeof import('element-plus/es')['ElTag']
     HelpMessageSrc: typeof import('./../components/helpMessage/src/index.vue')['default']
     Icon: typeof import('./../components/Icon/Index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
+    TableComponentsColumnSetting: typeof import('./../components/Table/components/columnSetting.vue')['default']
+    TableComponentsRedoSetting: typeof import('./../components/Table/components/redoSetting.vue')['default']
+    TableComponentsTableAction: typeof import('./../components/Table/components/TableAction.vue')['default']
+    TableComponentsZtTableFooter: typeof import('./../components/Table/components/ztTableFooter.vue')['default']
+    TableComponentsZtTableHeader: typeof import('./../components/Table/components/ztTableHeader.vue')['default']
+    TableComponentsZtTableHeaderHelp: typeof import('./../components/Table/components/ztTableHeaderHelp.vue')['default']
+    TableComponentsZtTableHeaderTitle: typeof import('./../components/Table/components/ztTableHeaderTitle.vue')['default']
+    TableComponentsZtTableImg: typeof import('./../components/Table/components/ztTableImg.vue')['default']
+    TableComponentsZtTableSetting: typeof import('./../components/Table/components/ztTableSetting.vue')['default']
+    TableSrcZtTable: typeof import('./../components/Table/src/ztTable.vue')['default']
+  }
+  export interface GlobalDirectives {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
   }
 }

+ 215 - 0
src/views/library/stack/index.vue

@@ -0,0 +1,215 @@
+<template>
+  <div class="library-stack-container">
+    <el-card class="box-card">
+      <template #header>
+        <div class="card-header">
+          <span>书库列表</span>
+          <el-button type="primary">添加书库</el-button>
+        </div>
+      </template>
+      
+      <!-- 搜索区域 -->
+      <div class="search-container">
+        <el-form :inline="true" :model="searchForm" class="form-inline">
+          <el-form-item label="书库名称">
+            <el-input v-model="searchForm.name" placeholder="请输入书库名称" clearable></el-input>
+          </el-form-item>
+          <el-form-item label="状态">
+            <el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
+              <el-option label="正常" value="normal"></el-option>
+              <el-option label="禁用" value="disabled"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleSearch">搜索</el-button>
+            <el-button @click="resetSearch">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+      
+      <!-- 表格区域 -->
+      <el-table :data="tableData" style="width: 100%" v-loading="loading">
+        <el-table-column prop="id" label="ID" width="80"></el-table-column>
+        <el-table-column prop="name" label="书库名称"></el-table-column>
+        <el-table-column prop="bookCount" label="书籍数量"></el-table-column>
+        <el-table-column prop="createdTime" label="创建时间"></el-table-column>
+        <el-table-column prop="status" label="状态">
+          <template #default="scope">
+            <el-tag :type="scope.row.status === 'normal' ? 'success' : 'danger'">
+              {{ scope.row.status === 'normal' ? '正常' : '禁用' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="180">
+          <template #default="scope">
+            <el-button type="primary" link size="small" @click="handleEdit(scope.row)">编辑</el-button>
+            <el-button type="primary" link size="small" @click="handleView(scope.row)">查看</el-button>
+            <el-button type="danger" link size="small" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      
+      <!-- 分页区域 -->
+      <div class="pagination-container">
+        <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted } from 'vue'
+
+// 定义数据类型
+interface SearchForm {
+  name: string;
+  status: string;
+}
+
+interface TableItem {
+  id: number;
+  name: string;
+  bookCount: number;
+  createdTime: string;
+  status: string;
+}
+
+// 搜索表单
+const searchForm = reactive<SearchForm>({
+  name: '',
+  status: ''
+})
+
+// 表格数据
+const tableData = ref<TableItem[]>([
+  {
+    id: 1,
+    name: '主要书库',
+    bookCount: 1250,
+    createdTime: '2023-08-15',
+    status: 'normal'
+  },
+  {
+    id: 2,
+    name: '历史文献',
+    bookCount: 850,
+    createdTime: '2023-08-16',
+    status: 'normal'
+  },
+  {
+    id: 3,
+    name: '科技资料',
+    bookCount: 620,
+    createdTime: '2023-08-17',
+    status: 'disabled'
+  }
+])
+
+// 加载状态
+const loading = ref(false)
+
+// 分页相关
+const currentPage = ref(1)
+const pageSize = ref(10)
+const total = ref(100)
+
+// 生命周期钩子
+onMounted(() => {
+  // 初始化数据,可以调用API获取数据
+  // fetchData()
+})
+
+// 搜索方法
+const handleSearch = () => {
+  console.log('搜索条件:', searchForm)
+  currentPage.value = 1
+  // fetchData()
+}
+
+// 重置搜索
+const resetSearch = () => {
+  searchForm.name = '';
+  searchForm.status = '';
+  currentPage.value = 1
+  // fetchData()
+}
+
+// 编辑
+const handleEdit = (row: TableItem) => {
+  console.log('编辑', row)
+}
+
+// 查看
+const handleView = (row: TableItem) => {
+  console.log('查看', row)
+}
+
+// 删除
+const handleDelete = (row: TableItem) => {
+  console.log('删除', row)
+  // 可以调用删除API
+}
+
+// 分页大小变化
+const handleSizeChange = (val: number) => {
+  pageSize.value = val
+  // fetchData()
+}
+
+// 页码变化
+const handleCurrentChange = (val: number) => {
+  currentPage.value = val
+  // fetchData()
+}
+
+// 获取数据方法(实际项目中可以调用API)
+/*
+const fetchData = async () => {
+  loading.value = true
+  try {
+    // 调用API获取数据
+    // const res = await api.getLibraryList({
+    //   page: currentPage.value,
+    //   pageSize: pageSize.value,
+    //   ...searchForm
+    // })
+    // tableData.value = res.data.list
+    // total.value = res.data.total
+  } catch (error) {
+    console.error('获取数据失败', error)
+  } finally {
+    loading.value = false
+  }
+}
+*/
+</script>
+
+<style scoped lang="scss">
+.library-stack-container {
+  padding: 20px;
+
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+
+  .search-container {
+    margin-bottom: 20px;
+  }
+
+  .pagination-container {
+    margin-top: 20px;
+    display: flex;
+    justify-content: flex-end;
+  }
+}
+</style>

+ 0 - 68
src/views/login-demo/index.vue

@@ -1,68 +0,0 @@
-<template>
-  <div class="flex items-center justify-center h-screen bg-gray-100">
-    <div class="text-center bg-white p-10 rounded-lg shadow-md">
-      <h1 class="text-2xl font-bold mb-5 text-gray-800">登录弹窗演示</h1>
-      <p class="text-gray-600 mb-8">点击下方按钮显示登录弹窗</p>
-      
-      <el-button type="primary" @click="showLoginDialog">打开登录弹窗</el-button>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { ref } from 'vue';
-import useDialog from '@/components/Dialog/useDialog';
-import { ElMessage } from 'element-plus';
-
-// 动态导入登录页面
-const LoginView = () => import('@/views/login/index.vue');
-
-// 初始化弹窗
-const loginDialog = useDialog({
-  content: LoginView,
-  modalProps: {
-    title: '',
-    width: '520px',
-    showClose: true,
-    closeOnClickModal: false,
-    header: true,
-    footer: false,
-    showFullscreen: true,
-    appendToBody: true,
-    destroyOnClose: true
-  }
-});
-
-// 显示登录弹窗
-const showLoginDialog = () => {
-  loginDialog.openModal();
-};
-</script>
-
-<style lang="scss" scoped>
-.login-demo-page {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 100vh;
-  background-color: #f5f7fa;
-  
-  .container {
-    text-align: center;
-    padding: 40px;
-    border-radius: 8px;
-    background-color: #fff;
-    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
-    
-    h1 {
-      margin-bottom: 20px;
-      color: #333;
-    }
-    
-    p {
-      margin-bottom: 30px;
-      color: #666;
-    }
-  }
-}
-</style>