Ver Fonte

feat: 组件测试

zhangjie há 2 semanas atrás
pai
commit
617738b557

+ 10 - 0
auto-imports.d.ts

@@ -0,0 +1,10 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+// biome-ignore lint: disable
+export {}
+declare global {
+
+}

+ 21 - 0
components.d.ts

@@ -9,7 +9,28 @@ export {}
 
 
 declare module '@vue/runtime-core' {
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
   export interface GlobalComponents {
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    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']
+    ElResult: typeof import('element-plus/es')['ElResult']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElUpload: typeof import('element-plus/es')['ElUpload']
+    FileUpload: typeof import('./src/components/file-upload/index.vue')['default']
+    Footer: typeof import('./src/components/footer/index.vue')['default']
+    ImportDialog: typeof import('./src/components/import-dialog/index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     RouterView: typeof import('vue-router')['RouterView']
+    SelectRangeDatetime: typeof import('./src/components/select-range-datetime/index.vue')['default']
+    SelectRangeTime: typeof import('./src/components/select-range-time/index.vue')['default']
+    SelectTask: typeof import('./src/components/select-task/index.vue')['default']
+    SelectTeaching: typeof import('./src/components/select-teaching/index.vue')['default']
+    SvgIcon: typeof import('./src/components/svg-icon/index.vue')['default']
+    UploadButton: typeof import('./src/components/upload-button/index.vue')['default']
   }
   }
 }
 }

+ 1 - 1
config/vite.config.base.ts

@@ -2,7 +2,7 @@ import { resolve } from 'path';
 import { defineConfig } from 'vite';
 import { defineConfig } from 'vite';
 import vue from '@vitejs/plugin-vue';
 import vue from '@vitejs/plugin-vue';
 import svgLoader from 'vite-svg-loader';
 import svgLoader from 'vite-svg-loader';
-import inject from '@rollup/plugin-inject';
+// eslint-disable-next-line import/no-unresolved
 import AutoImport from 'unplugin-auto-import/vite';
 import AutoImport from 'unplugin-auto-import/vite';
 import Components from 'unplugin-vue-components/vite';
 import Components from 'unplugin-vue-components/vite';
 import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
 import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

+ 2 - 5
src/App.vue

@@ -1,10 +1,7 @@
 <template>
 <template>
-  <a-config-provider>
-    <template #empty>
-      <div class="empty-none"> </div>
-    </template>
+  <el-config-provider>
     <router-view />
     <router-view />
-  </a-config-provider>
+  </el-config-provider>
 </template>
 </template>
 
 
 <script lang="ts" setup></script>
 <script lang="ts" setup></script>

+ 0 - 14
src/components/index.ts

@@ -2,29 +2,15 @@ import { App } from 'vue';
 
 
 // selection
 // selection
 import SvgIcon from './svg-icon/index.vue';
 import SvgIcon from './svg-icon/index.vue';
-import SelectTask from './select-task/index.vue';
-import SelectTeaching from './select-teaching/index.vue';
-import SelectAgent from './select-agent/index.vue';
-import SelectRoom from './select-room/index.vue';
 import SelectRangeDatetime from './select-range-datetime/index.vue';
 import SelectRangeDatetime from './select-range-datetime/index.vue';
 import SelectRangeTime from './select-range-time/index.vue';
 import SelectRangeTime from './select-range-time/index.vue';
-import SelectCity from './select-city/index.vue';
-import StatusTag from './status-tag/index.vue';
 import UploadButton from './upload-button/index.vue';
 import UploadButton from './upload-button/index.vue';
-import SelectRole from './select-role/index.vue';
 
 
 export default {
 export default {
   install(Vue: App) {
   install(Vue: App) {
     Vue.component('SvgIcon', SvgIcon);
     Vue.component('SvgIcon', SvgIcon);
-    Vue.component('SelectTask', SelectTask);
-    Vue.component('SelectTeaching', SelectTeaching);
-    Vue.component('SelectAgent', SelectAgent);
-    Vue.component('SelectRoom', SelectRoom);
     Vue.component('SelectRangeDatetime', SelectRangeDatetime);
     Vue.component('SelectRangeDatetime', SelectRangeDatetime);
     Vue.component('SelectRangeTime', SelectRangeTime);
     Vue.component('SelectRangeTime', SelectRangeTime);
-    Vue.component('SelectCity', SelectCity);
-    Vue.component('StatusTag', StatusTag);
     Vue.component('UploadButton', UploadButton);
     Vue.component('UploadButton', UploadButton);
-    Vue.component('SelectRole', SelectRole);
   },
   },
 };
 };

+ 8 - 3
src/components/select-range-datetime/index.vue

@@ -8,7 +8,7 @@
     end-placeholder="结束日期时间"
     end-placeholder="结束日期时间"
     value-format="x"
     value-format="x"
     format="YYYY-MM-DD HH:mm"
     format="YYYY-MM-DD HH:mm"
-    :style="{ width: '380px' }" 
+    :style="{ width: '380px' }"
     v-bind="attrs"
     v-bind="attrs"
     @change="onChange"
     @change="onChange"
   />
   />
@@ -19,7 +19,7 @@
 
 
   // import { ElDatePicker } from 'element-plus';
   // import { ElDatePicker } from 'element-plus';
 
 
-defineOptions({
+  defineOptions({
     name: 'SelectRangeDatetime',
     name: 'SelectRangeDatetime',
   });
   });
 
 
@@ -59,7 +59,12 @@ defineOptions({
     () => [props.startTime, props.endTime],
     () => [props.startTime, props.endTime],
     (val) => {
     (val) => {
       // Ensure that null/undefined values are handled correctly for el-date-picker
       // Ensure that null/undefined values are handled correctly for el-date-picker
-      if (val[0] !== undefined && val[1] !== undefined && val[0] !== null && val[1] !== null) {
+      if (
+        val[0] !== undefined &&
+        val[1] !== undefined &&
+        val[0] !== null &&
+        val[1] !== null
+      ) {
         selected.value = [val[0], val[1]] as number[];
         selected.value = [val[0], val[1]] as number[];
       } else {
       } else {
         selected.value = undefined; // Or an empty array [], depending on desired behavior for empty state
         selected.value = undefined; // Or an empty array [], depending on desired behavior for empty state

+ 7 - 2
src/components/select-range-time/index.vue

@@ -6,7 +6,7 @@
     range-separator="至"
     range-separator="至"
     start-placeholder="开始时间"
     start-placeholder="开始时间"
     end-placeholder="结束时间"
     end-placeholder="结束时间"
-    :style="{ width: '220px' }" 
+    :style="{ width: '220px' }"
     v-bind="attrs"
     v-bind="attrs"
     @change="onChange"
     @change="onChange"
   />
   />
@@ -45,7 +45,12 @@
         const hours = date.getHours().toString().padStart(2, '0');
         const hours = date.getHours().toString().padStart(2, '0');
         const minutes = date.getMinutes().toString().padStart(2, '0');
         const minutes = date.getMinutes().toString().padStart(2, '0');
         // Seconds are included if format string contains 'ss'
         // Seconds are included if format string contains 'ss'
-        return props.format.includes('ss') ? `${hours}:${minutes}:${date.getSeconds().toString().padStart(2, '0')}` : `${hours}:${minutes}`;
+        return props.format.includes('ss')
+          ? `${hours}:${minutes}:${date
+              .getSeconds()
+              .toString()
+              .padStart(2, '0')}`
+          : `${hours}:${minutes}`;
       };
       };
       const startTimeStr = formatTime(value[0]);
       const startTimeStr = formatTime(value[0]);
       const endTimeStr = formatTime(value[1]);
       const endTimeStr = formatTime(value[1]);

+ 1 - 0
src/main.ts

@@ -4,6 +4,7 @@ import router from './router';
 import store from './store';
 import store from './store';
 // import './mock';
 // import './mock';
 import App from './App.vue';
 import App from './App.vue';
+import 'element-plus/dist/index.css';
 import '@/assets/style/index.less';
 import '@/assets/style/index.less';
 import '@/api/interceptor';
 import '@/api/interceptor';
 
 

+ 1 - 1
src/router/constants.ts

@@ -4,4 +4,4 @@ export const NOT_FOUND = {
 
 
 export const DEFAULT_ROUTE_NAME = 'Workplace';
 export const DEFAULT_ROUTE_NAME = 'Workplace';
 
 
-export const WHITE_LIST = [NOT_FOUND, { name: 'Login' }];
+export const WHITE_LIST = [NOT_FOUND, { name: 'Login' }, { name: 'CompTest' }];

+ 19 - 8
src/router/routes/externalModules/system.ts

@@ -1,14 +1,25 @@
 import { DEFAULT_ROUTE_NAME } from '../../constants';
 import { DEFAULT_ROUTE_NAME } from '../../constants';
 import { AppRouteRecordRaw } from '../types';
 import { AppRouteRecordRaw } from '../types';
 
 
-const SYSTEM: AppRouteRecordRaw = {
-  path: '/workplace',
-  name: DEFAULT_ROUTE_NAME,
-  component: () => import('@/views/system/workplace/index.vue'),
-  meta: {
-    requiresAuth: true,
-    title: '主系统',
+const SYSTEM: AppRouteRecordRaw[] = [
+  {
+    path: '/workplace',
+    name: DEFAULT_ROUTE_NAME,
+    component: () => import('@/views/system/workplace/index.vue'),
+    meta: {
+      requiresAuth: true,
+      title: '主系统',
+    },
   },
   },
-};
+  {
+    path: '/comp-test',
+    name: 'CompTest',
+    component: () => import('@/views/system/comp-test/index.vue'),
+    meta: {
+      requiresAuth: false,
+      title: '组件测试',
+    },
+  },
+];
 
 
 export default SYSTEM;
 export default SYSTEM;

+ 0 - 42
src/router/routes/modules/base.ts

@@ -1,42 +0,0 @@
-import { DEFAULT_LAYOUT } from '../base';
-import { AppRouteRecordRaw } from '../types';
-
-const routes: AppRouteRecordRaw = {
-  path: '/base',
-  name: 'base',
-  component: DEFAULT_LAYOUT,
-  meta: {
-    requiresAuth: true,
-  },
-  children: [
-    {
-      path: 'teaching-manage',
-      name: 'TeachingManage',
-      component: () => import('@/views/base/teaching-manage/index.vue'),
-      meta: {
-        title: '教学点管理',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'agent-manage',
-      name: 'AgentManage',
-      component: () => import('@/views/base/agent-manage/index.vue'),
-      meta: {
-        title: '考点管理',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'room-manage',
-      name: 'RoomManage',
-      component: () => import('@/views/base/room-manage/index.vue'),
-      meta: {
-        title: '考场管理',
-        requiresAuth: true,
-      },
-    },
-  ],
-};
-
-export default routes;

+ 0 - 87
src/router/routes/modules/order.ts

@@ -1,87 +0,0 @@
-import { DEFAULT_LAYOUT } from '../base';
-import { AppRouteRecordRaw } from '../types';
-
-const routes: AppRouteRecordRaw = {
-  path: '/order',
-  name: 'order',
-  component: DEFAULT_LAYOUT,
-  meta: {
-    requiresAuth: true,
-  },
-  children: [
-    {
-      path: 'task-manage',
-      name: 'TaskManage',
-      component: () => import('@/views/order/task-manage/index.vue'),
-      meta: {
-        title: '预约任务管理',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'reservation-set',
-      name: 'ReservationSet',
-      component: () => import('@/views/order/reservation-set/index.vue'),
-      meta: {
-        title: '考点预约设置',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'room-scheduling-set',
-      name: 'RoomSchedulingSet',
-      component: () => import('@/views/order/room-scheduling-set/index.vue'),
-      meta: {
-        title: '考场排班设置',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'student-import',
-      name: 'StudentImport',
-      component: () => import('@/views/order/student-import/index.vue'),
-      meta: {
-        title: '考生信息导入',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'student-manage',
-      name: 'StudentManage',
-      component: () => import('@/views/order/student-manage/index.vue'),
-      meta: {
-        title: '考生管理',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'order-record-manage',
-      name: 'OrderRecordManage',
-      component: () => import('@/views/order/order-record-manage/index.vue'),
-      meta: {
-        title: '预约名单详情',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'user-manage',
-      name: 'AccountManage',
-      component: () => import('@/views/order/user-manage/index.vue'),
-      meta: {
-        title: '账号管理',
-        requiresAuth: true,
-      },
-    },
-    {
-      path: 'my-task',
-      name: 'MyTask',
-      component: () => import('@/views/order/my-task/index.vue'),
-      meta: {
-        title: '我的任务',
-        requiresAuth: true,
-      },
-    },
-  ],
-};
-
-export default routes;

+ 276 - 0
src/views/system/comp-test/comp-test.vue

@@ -0,0 +1,276 @@
+<template>
+  <div class="comp-test-container">
+    <h1 class="page-title">Component Demo Page</h1>
+
+    <section class="component-section">
+      <h2 class="section-title">FileUpload Component</h2>
+      <FileUpload
+        upload-url="/api/upload"
+        :format="['png', 'jpg', 'pdf']"
+        @upload-success="handleUploadSuccess"
+        @upload-error="handleUploadError"
+        @valid-error="handleValidError"
+      />
+      <p class="section-description"
+        >Upload URL: /api/upload, Formats: png, jpg, pdf, MaxSize: 5MB</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">ImportDialog Component</h2>
+      <el-button type="primary" @click="openImportDialog"
+        >Open Import Dialog</el-button
+      >
+      <ImportDialog
+        ref="importDialogRef"
+        title="Test Import Dialog"
+        upload-url="/api/import"
+        download-url="/api/download-template"
+        download-filename="template.xlsx"
+        @upload-success="handleImportSuccess"
+      />
+      <p class="section-description"
+        >Title: Test Import Dialog, Upload URL: /api/import</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">UploadButton Component</h2>
+      <UploadButton
+        upload-url="/api/upload-single"
+        btn-text="Upload Single File"
+        accept=".txt,.doc"
+        @upload-success="handleUploadSuccess"
+      />
+      <p class="section-description"
+        >Upload URL: /api/upload-single, Btn Text: Upload Single File, MaxSize:
+        2MB, Accept: .txt,.doc</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">SvgIcon Component</h2>
+      <div class="svg-icon-showcase">
+        <SvgIcon name="icon-home" class="demo-svg-icon" style="color: blue" />
+        <SvgIcon name="icon-user" class="demo-svg-icon" style="color: green" />
+        <SvgIcon name="icon-system" class="demo-svg-icon" style="color: red" />
+        <SvgIcon name="icon-add" class="demo-svg-icon" />
+      </div>
+      <p class="section-description"
+        >Displaying 'icon-home', 'icon-user', 'icon-system', 'icon-add'.</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">SelectRangeDatetime Component</h2>
+      <SelectRangeDatetime
+        v-model:start-time="startTime"
+        v-model:end-time="endTime"
+        @change="handleDatetimeChange"
+      />
+      <p class="section-description"
+        >Selected Start: {{ startTime }}, End: {{ endTime }}</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">SelectRangeTime Component</h2>
+      <SelectRangeTime
+        v-model:start-time="startTimeStr"
+        v-model:end-time="endTimeStr"
+        format="HH:mm:ss"
+        @change="handleTimeChange"
+      />
+      <p class="section-description"
+        >Selected Start: {{ startTimeStr }}, End: {{ endTimeStr }} (Format:
+        HH:mm:ss)</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">SelectTask Component</h2>
+      <SelectTask
+        v-model="selectedTask"
+        placeholder="Select a task"
+        clearable
+        @change="handleTaskChange"
+      />
+      <p class="section-description"
+        >Selected Task ID: {{ selectedTask }}. (Note: Options are mocked or need
+        API integration)</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">SelectTeaching Component</h2>
+      <SelectTeaching
+        v-model="selectedTeaching"
+        placeholder="Select a teaching point"
+        clearable
+        prefix
+        @change="handleTeachingChange"
+      />
+      <p class="section-description"
+        >Selected Teaching ID: {{ selectedTeaching }}. (Note: Options are mocked
+        or need API integration)</p
+      >
+    </section>
+
+    <section class="component-section">
+      <h2 class="section-title">Footer Component</h2>
+      <Footer />
+    </section>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { ElMessage, ElButton } from 'element-plus';
+
+  // Assuming components are globally registered or imported locally as needed
+  // For this demo, we'll rely on global registration or you'd add imports here:
+  import FileUpload from '@/components/file-upload/index.vue';
+  import ImportDialog from '@/components/import-dialog/index.vue';
+  // UploadButton is globally registered based on provided context
+  // SvgIcon is globally registered
+  // SelectRangeDatetime is globally registered
+  // SelectRangeTime is globally registered
+  import SelectTask from '@/components/select-task/index.vue';
+  import SelectTeaching from '@/components/select-teaching/index.vue';
+  import Footer from '@/components/footer/index.vue';
+
+  defineOptions({
+    name: 'CompTest',
+  });
+
+  const importDialogRef = ref<InstanceType<typeof ImportDialog> | null>(null);
+
+  const handleUploadSuccess = (data: any) => {
+    ElMessage.success(`FileUpload Success: ${data.filename}`);
+    console.log('Upload success:', data);
+  };
+
+  const handleUploadError = (err: any) => {
+    ElMessage.error('FileUpload Error');
+    console.error('Upload error:', err);
+  };
+
+  const handleValidError = (err: any) => {
+    ElMessage.warning(`FileUpload Validation Error: ${err.message}`);
+    console.warn('Validation error:', err);
+  };
+
+  const openImportDialog = () => {
+    importDialogRef.value?.open();
+  };
+
+  const handleImportSuccess = (data: any) => {
+    ElMessage.success(`ImportDialog Success: ${data.filename}`);
+    console.log('Import success:', data);
+    importDialogRef.value?.close();
+  };
+
+  // For SelectRangeDatetime
+  const startTime = ref<number | undefined>(
+    Date.now() - 7 * 24 * 60 * 60 * 1000
+  ); // Default to 7 days ago
+  const endTime = ref<number | undefined>(Date.now()); // Default to now
+  const handleDatetimeChange = (values: any) => {
+    console.log('Datetime changed:', values);
+    ElMessage.info(
+      `Datetime range selected: ${new Date(
+        values[0]
+      ).toLocaleString()} - ${new Date(values[1]).toLocaleString()}`
+    );
+  };
+
+  // For SelectRangeTime
+  const startTimeStr = ref<string | undefined>('09:00:00');
+  const endTimeStr = ref<string | undefined>('17:00:00');
+  const handleTimeChange = (values: any) => {
+    console.log('Time changed:', values);
+    ElMessage.info(`Time range selected: ${values[0]} - ${values[1]}`);
+  };
+
+  // For SelectTask
+  const selectedTask = ref<number | null>(null);
+  const handleTaskChange = (task: any) => {
+    console.log('Task changed:', task);
+    if (task) {
+      ElMessage.info(`Task selected: ${task.label} (ID: ${task.value})`);
+    } else {
+      ElMessage.info('Task cleared');
+    }
+  };
+
+  // For SelectTeaching
+  const selectedTeaching = ref<number | null>(null);
+  const handleTeachingChange = (teaching: any) => {
+    console.log('Teaching changed:', teaching);
+    if (teaching) {
+      ElMessage.info(
+        `Teaching point selected: ${teaching.label} (ID: ${teaching.value})`
+      );
+    } else {
+      ElMessage.info('Teaching point cleared');
+    }
+  };
+</script>
+
+<style scoped>
+  .comp-test-container {
+    padding: 20px;
+    max-width: 1000px;
+    margin: 0 auto;
+    background-color: #fff; /* White background for the content page */
+    border-radius: 8px; /* Rounded corners for the content container */
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); /* Softer shadow for the container */
+  }
+
+  .page-title {
+    font-size: 1.75rem; /* Larger page title */
+    font-weight: 600;
+    margin-bottom: 24px;
+    color: #343a40;
+    border-bottom: 1px solid #e9ecef; /* Separator line */
+    padding-bottom: 12px;
+  }
+
+  .component-section {
+    margin-bottom: 32px; /* Increased space between sections */
+    padding: 20px;
+    border: 1px solid #e9ecef; /* Lighter border for sections */
+    border-radius: 8px; /* Rounded corners for sections */
+    background-color: #f8f9fa; /* Slightly off-white background for sections */
+  }
+
+  .section-title {
+    font-size: 1.3rem; /* Consistent section title size */
+    font-weight: 500;
+    margin-bottom: 16px;
+    color: #007bff; /* Blue color for section titles */
+  }
+
+  .section-description {
+    margin-top: 12px;
+    font-size: 0.9rem;
+    color: #6c757d; /* Gray color for descriptions */
+    line-height: 1.6;
+  }
+
+  .svg-icon-showcase {
+    display: flex;
+    align-items: center;
+    gap: 20px; /* Space between icons */
+    margin-bottom: 10px;
+  }
+
+  .demo-svg-icon {
+    font-size: 28px; /* Larger demo icons */
+  }
+
+  /* Ensure Element Plus components like el-button integrate well */
+  .component-section .el-button {
+    margin-bottom: 10px; /* Space below buttons if needed */
+  }
+</style>

+ 115 - 0
src/views/system/comp-test/index.vue

@@ -0,0 +1,115 @@
+<template>
+  <div class="comp-test-layout">
+    <div class="sidebar">
+      <h2 class="sidebar-title">Test Pages</h2>
+      <ul class="menu-list">
+        <li>
+          <a
+            href="#"
+            :class="['menu-item', { active: selectedMenu === 'comp-test' }]"
+            @click.prevent="selectedMenu = 'comp-test'"
+          >
+            Component Demos
+          </a>
+        </li>
+        <li>
+          <a
+            href="#"
+            :class="['menu-item', { active: selectedMenu === 'svg-view' }]"
+            @click.prevent="selectedMenu = 'svg-view'"
+          >
+            SVG Icon Viewer
+          </a>
+        </li>
+      </ul>
+    </div>
+    <div class="content-area">
+      <keep-alive>
+        <component :is="currentComponent"></component>
+      </keep-alive>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, computed, defineAsyncComponent } from 'vue';
+
+  defineOptions({
+    name: 'CompTestIndex',
+  });
+
+  const CompTest = defineAsyncComponent(() => import('./comp-test.vue'));
+  const SvgView = defineAsyncComponent(() => import('./svg-view.vue'));
+
+  const selectedMenu = ref('comp-test'); // Default to 'comp-test'
+
+  const currentComponent = computed(() => {
+    switch (selectedMenu.value) {
+      case 'comp-test':
+        return CompTest;
+      case 'svg-view':
+        return SvgView;
+      default:
+        return CompTest; // Fallback to CompTest
+    }
+  });
+</script>
+
+<style scoped>
+  .comp-test-layout {
+    display: flex;
+    height: 100vh;
+    background-color: #f8f9fa; /* Light gray background for the whole layout */
+  }
+
+  .sidebar {
+    width: 250px; /* Fixed width for the sidebar */
+    background-color: #ffffff; /* White background for sidebar */
+    padding: 20px;
+    border-right: 1px solid #dee2e6; /* Light border */
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); /* Subtle shadow */
+  }
+
+  .sidebar-title {
+    font-size: 1.25rem; /* Slightly larger title */
+    font-weight: 600;
+    margin-bottom: 20px;
+    color: #343a40; /* Darker text color */
+    padding-left: 10px;
+  }
+
+  .menu-list {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+  }
+
+  .menu-item {
+    display: block;
+    padding: 12px 15px;
+    text-decoration: none;
+    color: #495057; /* Standard text color for menu items */
+    border-radius: 6px; /* Rounded corners for menu items */
+    margin-bottom: 5px; /* Space between menu items */
+    transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+  }
+
+  .menu-item:hover {
+    background-color: #e9ecef; /* Light hover effect */
+    color: #007bff; /* Blue color on hover */
+  }
+
+  .menu-item.active {
+    background-color: #007bff; /* Blue background for active item */
+    color: #ffffff; /* White text for active item */
+    font-weight: 500;
+    box-shadow: 0 1px 3px rgba(0, 123, 255, 0.3); /* Shadow for active item */
+  }
+
+  .content-area {
+    flex-grow: 1; /* Takes up remaining space */
+    padding: 24px;
+    overflow-y: auto; /* Allows scrolling for content */
+    background-color: #f8f9fa; /* Consistent background */
+  }
+</style>

+ 122 - 0
src/views/system/comp-test/svg-view.vue

@@ -0,0 +1,122 @@
+<template>
+  <div class="svg-view-container">
+    <h1 class="page-title">SVG Icon Viewer</h1>
+    <div class="icon-grid">
+      <div
+        v-for="icon in svgIcons"
+        :key="icon"
+        class="icon-card"
+        @click="copyToClipboard(icon)"
+      >
+        <SvgIcon :name="icon" class="svg-display" />
+        <span class="icon-name">{{ icon }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  // SvgIcon is globally registered
+
+  defineOptions({
+    name: 'SvgView',
+  });
+
+  const svgFileNames = [
+    'icon-add',
+    'icon-apply',
+    'icon-assign',
+    'icon-base',
+    'icon-delete',
+    'icon-error',
+    'icon-file',
+    'icon-home',
+    'icon-import',
+    'icon-logout',
+    'icon-org',
+    'icon-print',
+    'icon-success',
+    'icon-system',
+    'icon-user',
+  ];
+
+  const svgIcons = ref(svgFileNames);
+
+  const copyToClipboard = async (name: string) => {
+    try {
+      await navigator.clipboard.writeText(name);
+      ElMessage.success(`Copied '${name}' to clipboard!`);
+    } catch (err) {
+      ElMessage.error('Failed to copy icon name.');
+      console.error('Failed to copy: ', err);
+    }
+  };
+</script>
+
+<style scoped>
+  .svg-view-container {
+    padding: 20px;
+    background-color: #fff; /* White background for the content page */
+    border-radius: 8px; /* Rounded corners for the content container */
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); /* Softer shadow for the container */
+  }
+
+  .page-title {
+    font-size: 1.75rem;
+    font-weight: 600;
+    margin-bottom: 24px;
+    color: #343a40;
+    border-bottom: 1px solid #e9ecef;
+    padding-bottom: 12px;
+  }
+
+  .icon-grid {
+    display: grid;
+    grid-template-columns: repeat(
+      auto-fill,
+      minmax(120px, 1fr)
+    ); /* Responsive grid */
+    gap: 20px; /* Space between icon cards */
+  }
+
+  .icon-card {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding: 15px;
+    border: 1px solid #e0e0e0; /* Lighter border for cards */
+    border-radius: 8px;
+    background-color: #f8f9fa; /* Light background for cards */
+    cursor: pointer;
+    transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
+    min-height: 100px;
+  }
+
+  .icon-card:hover {
+    transform: translateY(-3px);
+    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
+  }
+
+  .svg-display {
+    font-size: 36px; /* Larger icon display */
+    margin-bottom: 10px;
+  }
+
+  .icon-name {
+    font-size: 0.8rem;
+    color: #495057;
+    text-align: center;
+    word-break: break-all; /* Prevent long names from breaking layout */
+  }
+
+  .copy-feedback {
+    margin-top: 20px;
+    font-size: 0.9rem;
+    color: #28a745; /* Green color for success message */
+    font-weight: 500;
+    text-align: center;
+  }
+</style>

+ 7 - 2
src/views/system/not-found/index.vue

@@ -1,8 +1,13 @@
 <template>
 <template>
   <div class="content">
   <div class="content">
-    <a-result class="result" status="404" :subtitle="'not found'"> </a-result>
+    <el-result
+      icon="primary"
+      title="Primary Tip"
+      sub-title="Please follow the instructions"
+    >
+    </el-result>
     <div class="operation-row">
     <div class="operation-row">
-      <a-button key="back" type="primary" @click="back"> back </a-button>
+      <el-button key="back" type="primary" @click="back"> back </el-button>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>