zhanghaijun месяцев назад: 11
Родитель
Сommit
aca052edd1

+ 3 - 3
src/api/course.api.ts

@@ -23,14 +23,14 @@ export interface Course {
 
 export class CourseServer {
   public static list = (params: Record<string, unknown>) => request.post<Course[]>('/course/list', params)
-  public static detail = (courseId: string) => request.post<Course>('/course/detail/get', { courseId })
+  public static detail = (courseId: string) => request.post<Course>('/course/detail/get', { courseId: Number(courseId) })
   public static add = (data: Record<string, unknown>) => request.post('/course/add', data)
   public static update = (data: Record<string, unknown>) => request.post('/course/update', data)
   public static delete = (courseId: string) => request.post('/course/delete', { courseId })
   public static deleteBatch = (courseIds: string[]) => request.post('/course/delete/batch', { courseIds })
   // 课程复制
-  public static copy = (courseId: string) => request.post('/course/copy', { courseId })
-  public static copyBatch = (courseIds: string[]) => request.post('/course/copy/batch', { courseIds })
+  public static copy = (courseId: string) => request.post('/course/copy', { courseId: Number(courseId) })
+  public static copyBatch = (courseIds: string[]) => request.post('/course/copy/batch', { courseIds: courseIds.map((id) => Number(id)) })
   // 课程导入
   public static import = (file: Blob | File) => request.upload('/course/import', { file })
   // 课程导出

+ 1 - 1
src/components/business/EasyProcess/common/process.data.ts

@@ -19,7 +19,7 @@ export const nodeConfigData: Record<EasyProcess.NodeType, EasyProcess.NodeMeta>
   },
   router: {
     title: '条件分支',
-    canAdd: true,
+    canAdd: false,
     hasDrawer: false,
     icon: { name: '&#xe804;' },
     defaultNode: () => ({

+ 54 - 5
src/components/business/EasyProcess/node/fault_drawer.vue

@@ -65,8 +65,15 @@
       {
         prop: 'param2Type',
         label: '参数2类型',
-        component: 'ApiDict',
-        componentProps: { code: 'gender' },
+        component: 'Select',
+        componentProps: {
+          placeholder: '请选择类型',
+          options: [
+            { label: '文本', value: 1 },
+            { label: '数字', value: 2 },
+            { label: '日期', value: 3 }
+          ]
+        },
         colSpan: 10,
         ifShow: () => templateSelected.value?.param2Type != -1
       },
@@ -79,7 +86,21 @@
         colSpan: 14,
         ifShow: () => templateSelected.value?.param2Type != -1
       },
-      { prop: 'param3Type', label: '参数3类型', component: 'Select', colSpan: 10, ifShow: () => templateSelected.value?.param3Type != -1 },
+      {
+        prop: 'param3Type',
+        label: '参数3类型',
+        component: 'Select',
+        colSpan: 10,
+        componentProps: {
+          placeholder: '请选择类型',
+          options: [
+            { label: '文本', value: 1 },
+            { label: '数字', value: 2 },
+            { label: '日期', value: 3 }
+          ]
+        },
+        ifShow: () => templateSelected.value?.param3Type != -1
+      },
       {
         prop: 'param3Desc',
         label: '',
@@ -89,7 +110,21 @@
         colSpan: 14,
         ifShow: () => templateSelected.value?.param3Type != -1
       },
-      { prop: 'param4Type', label: '参数4类型', component: 'Select', colSpan: 10, ifShow: () => templateSelected.value?.param4Type != -1 },
+      {
+        prop: 'param4Type',
+        label: '参数4类型',
+        component: 'Select',
+        colSpan: 10,
+        componentProps: {
+          placeholder: '请选择类型',
+          options: [
+            { label: '文本', value: 1 },
+            { label: '数字', value: 2 },
+            { label: '日期', value: 3 }
+          ]
+        },
+        ifShow: () => templateSelected.value?.param4Type != -1
+      },
       {
         prop: 'param4Desc',
         label: '',
@@ -99,7 +134,21 @@
         colSpan: 14,
         ifShow: () => templateSelected.value?.param4Type != -1
       },
-      { prop: 'param5Type', label: '参数5类型', component: 'Select', colSpan: 10, ifShow: () => templateSelected.value?.param5Type != -1 },
+      {
+        prop: 'param5Type',
+        label: '参数5类型',
+        component: 'Select',
+        colSpan: 10,
+        componentProps: {
+          placeholder: '请选择类型',
+          options: [
+            { label: '文本', value: 1 },
+            { label: '数字', value: 2 },
+            { label: '日期', value: 3 }
+          ]
+        },
+        ifShow: () => templateSelected.value?.param5Type != -1
+      },
       {
         prop: 'param5Desc',
         label: '',

+ 6 - 1
src/components/form/detail-form.vue

@@ -24,7 +24,9 @@
               <component style="padding-right: 8px" v-if="hasComponent(item.component)" :is="getComponent(item.component)" :value="get(formModel, item.prop)" v-bind="getComponentProps(item)">
               </component>
               <template v-else>
-                {{ get(formModel, item.prop) }}
+                <div class="detail-item">
+                  {{ get(formModel, item.prop) }}
+                </div>
               </template>
             </template>
           </ElFormItem>
@@ -141,6 +143,9 @@
   })
 </script>
 <style lang="scss" scoped>
+  .detail-item {
+    @include ellipsis();
+  }
   .detail-form {
     // .form-with-top-spacing {
     //   margin-top: 0px;

+ 1 - 3
src/composables/useAuth.ts

@@ -23,9 +23,7 @@ export const useAuth = () => {
   const frontendAuthList = info.value?.buttons ?? []
 
   // 后端路由 meta 配置的权限列表(例如:[{ authMark: 'add' }])
-  const backendAuthList: AuthItem[] = Array.isArray(route.meta.authList)
-    ? (route.meta.authList as AuthItem[])
-    : []
+  const backendAuthList: AuthItem[] = Array.isArray(route.meta.authList) ? (route.meta.authList as AuthItem[]) : []
 
   /**
    * 检查是否拥有某权限标识(前后端模式通用)

+ 2 - 6
src/composables/useFastEnter.ts

@@ -15,18 +15,14 @@ export function useFastEnter() {
   const enabledApplications = computed<FastEnterApplication[]>(() => {
     if (!fastEnterConfig.value?.applications) return []
 
-    return fastEnterConfig.value.applications
-      .filter((app) => app.enabled !== false)
-      .sort((a, b) => (a.order || 0) - (b.order || 0))
+    return fastEnterConfig.value.applications.filter((app) => app.enabled !== false).sort((a, b) => (a.order || 0) - (b.order || 0))
   })
 
   // 获取启用的快速链接(按排序权重排序)
   const enabledQuickLinks = computed<FastEnterQuickLink[]>(() => {
     if (!fastEnterConfig.value?.quickLinks) return []
 
-    return fastEnterConfig.value.quickLinks
-      .filter((link) => link.enabled !== false)
-      .sort((a, b) => (a.order || 0) - (b.order || 0))
+    return fastEnterConfig.value.quickLinks.filter((link) => link.enabled !== false).sort((a, b) => (a.order || 0) - (b.order || 0))
   })
 
   // 获取最小显示宽度

+ 3 - 8
src/composables/useHeaderBar.ts

@@ -20,8 +20,7 @@ export function useHeaderBar() {
   const headerBarConfigRef = computed<HeaderBarFeatureConfig>(() => headerBarConfig)
 
   // 从store中获取相关状态
-  const { showMenuButton, showFastEnter, showRefreshButton, showCrumbs, showLanguage } =
-    storeToRefs(settingStore)
+  const { showMenuButton, showFastEnter, showRefreshButton, showCrumbs, showLanguage } = storeToRefs(settingStore)
 
   /**
    * 检查特定功能是否启用
@@ -125,9 +124,7 @@ export function useHeaderBar() {
    * @returns 启用的功能名称数组
    */
   const getEnabledFeatures = (): (keyof HeaderBarFeatureConfig)[] => {
-    return Object.keys(headerBarConfigRef.value).filter(
-      (key) => headerBarConfigRef.value[key as keyof HeaderBarFeatureConfig]?.enabled
-    ) as (keyof HeaderBarFeatureConfig)[]
+    return Object.keys(headerBarConfigRef.value).filter((key) => headerBarConfigRef.value[key as keyof HeaderBarFeatureConfig]?.enabled) as (keyof HeaderBarFeatureConfig)[]
   }
 
   /**
@@ -135,9 +132,7 @@ export function useHeaderBar() {
    * @returns 禁用的功能名称数组
    */
   const getDisabledFeatures = (): (keyof HeaderBarFeatureConfig)[] => {
-    return Object.keys(headerBarConfigRef.value).filter(
-      (key) => !headerBarConfigRef.value[key as keyof HeaderBarFeatureConfig]?.enabled
-    ) as (keyof HeaderBarFeatureConfig)[]
+    return Object.keys(headerBarConfigRef.value).filter((key) => !headerBarConfigRef.value[key as keyof HeaderBarFeatureConfig]?.enabled) as (keyof HeaderBarFeatureConfig)[]
   }
 
   /**

+ 4 - 16
src/composables/useTableColumns.ts

@@ -14,8 +14,7 @@ const SPECIAL_COLUMNS: Record<string, { prop: string; label: string }> = {
 /**
  * 获取列的唯一标识
  */
-export const getColumnKey = <T>(col: ColumnOption<T>) =>
-  SPECIAL_COLUMNS[col.type as keyof typeof SPECIAL_COLUMNS]?.prop ?? (col.prop as string)
+export const getColumnKey = <T>(col: ColumnOption<T>) => SPECIAL_COLUMNS[col.type as keyof typeof SPECIAL_COLUMNS]?.prop ?? (col.prop as string)
 
 /**
  * 获取列的检查状态
@@ -98,9 +97,7 @@ export function useTableColumns<T = any>(
   watch(
     dynamicColumns,
     (newCols) => {
-      const checkedMap = new Map(
-        columnChecks.value.map((c) => [getColumnKey(c), c.checked ?? true])
-      )
+      const checkedMap = new Map(columnChecks.value.map((c) => [getColumnKey(c), c.checked ?? true]))
       const newChecks = getColumnChecks(newCols).map((c) => ({
         ...c,
         checked: checkedMap.has(getColumnKey(c)) ? checkedMap.get(getColumnKey(c)) : c.checked
@@ -147,10 +144,7 @@ export function useTableColumns<T = any>(
         return cols.filter((c) => !propsToRemove.includes(getColumnKey(c)))
       }),
 
-    updateColumn: (prop: string, updates: Partial<ColumnOption<T>>) =>
-      setDynamicColumns((cols) =>
-        cols.map((c) => (getColumnKey(c) === prop ? { ...c, ...updates } : c))
-      ),
+    updateColumn: (prop: string, updates: Partial<ColumnOption<T>>) => setDynamicColumns((cols) => cols.map((c) => (getColumnKey(c) === prop ? { ...c, ...updates } : c))),
 
     toggleColumn: (prop: string, visible?: boolean) => {
       const i = columnChecks.value.findIndex((c) => getColumnKey(c) === prop)
@@ -177,13 +171,7 @@ export function useTableColumns<T = any>(
 
     reorderColumns: (fromIndex: number, toIndex: number) =>
       setDynamicColumns((cols) => {
-        if (
-          fromIndex < 0 ||
-          fromIndex >= cols.length ||
-          toIndex < 0 ||
-          toIndex >= cols.length ||
-          fromIndex === toIndex
-        ) {
+        if (fromIndex < 0 || fromIndex >= cols.length || toIndex < 0 || toIndex >= cols.length || fromIndex === toIndex) {
           return cols
         }
         const next = [...cols]

+ 106 - 0
src/composables/useTimer.ts

@@ -0,0 +1,106 @@
+import { ref, onUnmounted } from 'vue'
+
+/**
+ * 计时器
+ * @param startTime 开始计时的时间
+ * @description 支持时分秒的计时器功能
+ */
+export const useTimer = (startTime: Date) => {
+  const time = ref<number>(0)
+  let timer: ReturnType<typeof setInterval> | null = null
+  const isRunning = ref<boolean>(false)
+  let startTimestamp: number
+  let pausedTime = 0
+
+  // 清除定时器
+  const clearTimer = () => {
+    if (timer) {
+      clearInterval(timer)
+      timer = null
+    }
+  }
+
+  // 更新当前时间
+  const updateTime = () => {
+    const now = new Date().getTime()
+    time.value = Math.floor((now - startTimestamp) / 1000) + pausedTime
+  }
+
+  const start = () => {
+    if (isRunning.value) return
+
+    clearTimer()
+    startTimestamp = new Date().getTime()
+    isRunning.value = true
+    pausedTime = 0
+
+    updateTime()
+    timer = setInterval(updateTime, 1000)
+  }
+
+  const pause = () => {
+    if (!isRunning.value) return
+
+    clearTimer()
+    isRunning.value = false
+    // 记录暂停时已经过去的时间
+    const currentTime = time.value
+    pausedTime = currentTime
+  }
+
+  // 重置计时器
+  const reset = () => {
+    clearTimer()
+    isRunning.value = false
+    time.value = 0
+    pausedTime = 0
+    startTimestamp = new Date().getTime()
+  }
+
+  // 恢复暂停状态
+  const resume = () => {
+    if (isRunning.value) return
+
+    clearTimer()
+    startTimestamp = new Date().getTime() - pausedTime * 1000
+    isRunning.value = true
+
+    updateTime()
+    timer = setInterval(updateTime, 1000)
+  }
+
+  // 初始化时间
+  const init = () => {
+    const now = new Date().getTime()
+    const start = startTime.getTime()
+    time.value = Math.max(0, Math.floor((now - start) / 1000))
+  }
+
+  // 组件卸载时清除定时器
+  onUnmounted(() => {
+    clearTimer()
+  })
+
+  // 初始化
+  init()
+
+  return {
+    time,
+    isRunning,
+    hours: computed<string>(() =>
+      Math.floor(time.value / 3600)
+        .toString()
+        .padStart(2, '0')
+    ),
+    minutes: computed(() =>
+      Math.floor((time.value % 3600) / 60)
+        .toString()
+        .padStart(2, '0')
+    ),
+    seconds: computed(() => (time.value % 60).toString().padStart(2, '0')),
+    start,
+    pause,
+    reset,
+    resume
+  }
+}

+ 2 - 2
src/router/routes/modules/train.ts

@@ -12,13 +12,13 @@ export const routes: AppRouteRecord[] = [
       {
         path: '/course/list',
         name: 'trainer:course-list',
-        component: '/train/trainer/course-list',
+        component: '/train/course/trainer_list',
         meta: { title: '课程列表', icon: 'ant-design:unordered-list-outlined' }
       },
       {
         path: '/course/run/:id',
         name: 'trainer:course-run',
-        component: '/train/trainer/course-run',
+        component: '/train/course/trainer_run',
         meta: { title: '课程运行', icon: 'ant-design:play-circle-outlined' }
       }
     ]

+ 0 - 1
src/views/train/course/course-detail.vue

@@ -1,7 +1,6 @@
 <template>
   <Dialog>
     <DetailForm :model="course">
-      <!-- <slot v-if="$slots[item.prop]" :name="item.prop" :form-model="formModel" :model="formModel" :field="item" /> -->
       <template #guideFilePath="{ model }">
         <div v-if="model.guideFilePath.endsWith('.mp4')" class="border-art-border-dashed-color w-[80%] border border-solid">
           <ArtVideoPlayer playerId="my-video-1" :videoUrl="model.guideFilePath" :posterUrl="posterUrl" :autoplay="false" :volume="1" :playbackRates="[0.5, 1, 1.5, 2]" />

+ 2 - 12
src/views/train/course/course-import.vue

@@ -2,22 +2,12 @@
   <Dialog>
     <div class="m-4">
       <!-- 文件上传区域 -->
-      <el-upload
-        ref="uploadRef"
-        class="avatar-uploader"
-        action=""
-        :auto-upload="false"
-        :show-file-list="false"
-        :on-change="handleChange"
-        :on-exceed="handleExceed"
-        :limit="1"
-        accept=".xlsx,.xls,.csv,.json,.xml"
-      >
+      <el-upload ref="uploadRef" class="avatar-uploader" action="" :auto-upload="false" :show-file-list="false" :on-change="handleChange" :on-exceed="handleExceed" :limit="1" accept=".json">
         <el-button size="small" type="primary" :disabled="!!file">
           {{ file ? '已选择文件' : '点击上传' }}
         </el-button>
         <template #tip>
-          <div class="el-upload__tip mt-1 text-gray-500"> 支持文件格式:Excel、CSV、JSON、XML,大小不超过 5MB </div>
+          <div class="el-upload__tip mt-1 text-gray-500"> 支持文件格式:JSON,大小不超过 5MB </div>
         </template>
       </el-upload>
 

+ 1 - 5
src/views/train/course/detail/course.schem.ts

@@ -9,11 +9,7 @@ const options = ref([
 export const rules: FormRules = {
   courseName: [{ required: true, message: '请输入课程名称', trigger: ['blur'] }],
   courseDesc: [{ required: true, message: '请输入课程描述', trigger: ['blur'] }],
-  totalDuration: [
-    { required: true, message: '请输入课程时长', trigger: ['blur'] },
-    { type: 'number', message: '请输入数字', trigger: ['blur'] },
-    { min: 1, message: '请输入大于等于1的数字', trigger: ['blur'] }
-  ]
+  totalDuration: [{ required: true, message: '请输入课程时长', trigger: ['blur'] }]
 }
 export const schema: FormItemSchema[] = [
   // 课程基本信息

+ 12 - 11
src/views/train/trainer/course-list/course-list.vue

@@ -14,7 +14,7 @@
             </div>
           </template>
           <template #default>
-            <div class="top">
+            <div class="top" @click="courseDetailShow(item)">
               <ElImage class="cover" :src="getCover(item)" lazy fit="cover">
                 <template #error>
                   <div class="image-slot">
@@ -39,6 +39,7 @@
   <div style="margin-top: 16vh" v-if="data.length === 0 && !props.loading">
     <ElEmpty :description="`未找到课程数据`" />
   </div>
+  <courseDetail ref="courseDetailRef" />
 </template>
 <script lang="ts" setup>
   import { Picture as IconPicture, DataLine } from '@element-plus/icons-vue'
@@ -48,6 +49,7 @@
   import cover3 from '@imgs/cover/img3.webp'
   import { type Course } from '@/api/course.api'
   import { router } from '@/router'
+  import courseDetail from '../../course/course-detail.vue'
 
   const props = withDefaults(defineProps<{ data: Course[]; loading: boolean }>(), {
     data: () => [],
@@ -55,21 +57,20 @@
   })
 
   const getCover = (item: Course) => {
-    switch (item.typeID) {
-      case 1:
-        return cover1
-      case 2:
-        return cover2
-      case 3:
-        return cover3
-      default:
-        return cover1
-    }
+    if (item.typeID == '1') return cover1
+    if (item.typeID == '2') return cover2
+    if (item.typeID == '3') return cover3
+    return cover2
   }
 
   const startCourse = (item: Course) => {
     router.push({ path: '/trainer/course/run/' + item.courseID })
   }
+
+  const courseDetailRef = ref()
+  const courseDetailShow = (item: Course) => {
+    courseDetailRef.value.show(item)
+  }
 </script>
 <style lang="scss" scoped>
   .list {

+ 1 - 1
src/views/train/trainer/course-list/index.vue

@@ -8,7 +8,7 @@
 </template>
 
 <script setup lang="ts">
-  import courseSearch from './course-search.vue'
+  import courseSearch from '../course-search.vue'
   import courseList from './course-list.vue'
   import { Course, CourseServer } from '@/api/course.api'
 

+ 42 - 0
src/views/train/course/trainer_run/course-detail.vue

@@ -0,0 +1,42 @@
+<template>
+  <DetailForm :model="courseItem"> </DetailForm>
+</template>
+<script setup lang="tsx">
+  import { Course } from '@/api/course.api'
+  import { useDetailForm } from '@/components/form'
+
+  const props = defineProps<{ course?: Course }>()
+  const courseItem = ref<Course>()
+
+  watch(
+    () => props.course,
+    (newVal) => {
+      if (newVal) courseItem.value = newVal
+    }
+  )
+
+  const [DetailForm] = useDetailForm({
+    labelWidth: '80px',
+    schema: [
+      { prop: 'courseID', label: '课程ID', component: 'Input', colSpan: 8 },
+      { prop: 'courseGUID', label: '课程GUID', component: 'Input', colSpan: 8 },
+      { prop: 'courseName', label: '课程名称', component: 'Input', colSpan: 8 },
+      { prop: 'courseDesc', label: '课程描述', component: 'Input', colSpan: 8 },
+      { prop: 'totalDuration', label: '课程总时长', component: 'Input', colSpan: 8, componentProps: {} },
+      { prop: 'typeID', label: '分类ID', component: 'Input', colSpan: 8 },
+      { prop: 'externalTypeID', label: '外部分类ID', component: 'Input', colSpan: 8 },
+      { prop: 'lineNo', label: '线路号', component: 'Input', colSpan: 8 },
+      { prop: 'runningMap', label: '运行图', component: 'Input', colSpan: 8 }
+      // { prop: 'trainPlan', label: '培训计划', component: 'Input', colSpan: 8 },
+      // { prop: 'creator', label: '创建人', component: 'Input', colSpan: 8 },
+      // { prop: 'createTime', label: '创建时间', component: 'Input', colSpan: 8 }
+    ]
+  })
+
+  onMounted(() => {
+    if (props.course) {
+      courseItem.value = props.course
+    }
+  })
+</script>
+<style lang="scss" scoped></style>

+ 27 - 10
src/views/train/trainer/course-run/index.vue

@@ -3,7 +3,7 @@
     <div class="text-lg font-bold">课程运行</div>
     <div class="course flex flex-row">
       <div class="course-cover">
-        <ElImage class="cover" :src="cover1" lazy fit="cover">
+        <ElImage class="cover" :src="getCover()" lazy fit="cover">
           <template #error>
             <div class="image-slot">
               <ElIcon><icon-picture /></ElIcon>
@@ -12,17 +12,19 @@
         </ElImage>
       </div>
       <div class="course-info flex-1">
-        <div class="course-name">课程名称</div>
-        <div class="course-desc">课程描述</div>
+        <course-detail :course="course"></course-detail>
       </div>
-      <div class="timer"> 00:00:00 </div>
+      <div class="timer w-[320px] text-center"> {{ hours }}:{{ minutes }}:{{ seconds }} </div>
     </div>
     <div class="flex h-full flex-1 flex-row gap-4">
-      <div class="flex-1 border"> 运行流程图 </div>
+      <div class="flex-1 border">
+        <course-flow></course-flow>
+      </div>
       <div class="flex flex-col gap-4">
         <div class="h-[180px] w-[320px] border p-4">
-          <el-button type="primary">开始课程</el-button>
-          <el-button type="primary">结束课程</el-button>
+          <el-button v-if="!isRunning" type="primary" @click="start">开始课程</el-button>
+          <el-button v-else type="primary" @click="pause">暂停课程</el-button>
+          <el-button type="primary" @click="reset">结束课程</el-button>
         </div>
         <div class="flex-1 border p-4">
           <div class="mb-4 text-lg font-medium">节点信息</div>
@@ -33,15 +35,30 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import cover1 from '@imgs/cover/img5.webp'
+  import cover1 from '@imgs/cover/img1.webp'
+  import cover2 from '@imgs/cover/img2.webp'
+  import cover3 from '@imgs/cover/img3.webp'
   import { Picture as IconPicture } from '@element-plus/icons-vue'
   import { Course, CourseServer } from '@/api/course.api'
+  import courseDetail from './course-detail.vue'
+  import courseFlow from '../detail/course-flow.vue'
+  import { useTimer } from '@/composables/useTimer'
+
+  // 计时器
+  const { hours, minutes, seconds, isRunning, start, pause, reset } = useTimer(new Date())
 
   // 路由参数
   const route = useRoute()
-  const courseId = computed(() => Number(route.params.id))
+  const courseId = computed(() => String(route.params.id))
   const course = ref<Course>()
 
+  const getCover = () => {
+    if (course.value?.typeID == '1') return cover1
+    if (course.value?.typeID == '2') return cover2
+    if (course.value?.typeID == '3') return cover3
+    return cover2
+  }
+
   onMounted(async () => {
     const { data } = await CourseServer.detail(courseId.value)
     course.value = data
@@ -53,7 +70,7 @@
   }
   .course {
     border: 1px solid var(--art-gray-200);
-    height: 105px;
+    height: 110px;
   }
   .course-cover {
     padding: 10px 20px;

+ 0 - 102
src/views/train/trainer/course-list/course-search.vue

@@ -1,102 +0,0 @@
-<!-- 表格搜索栏示例 -->
-<template>
-  <ArtSearchBar ref="searchBarBasicRef" v-model="formDataBasic" :items="baseFormItems" @search="search"> </ArtSearchBar>
-</template>
-
-<script setup lang="ts">
-  // 表单数据双向绑定
-  const searchBarBasicRef = ref()
-  // 基础示例表单数据
-  const formDataBasic = ref({
-    courseID: undefined,
-    courseName: undefined,
-    courseType: undefined,
-    createUser: undefined,
-    trainPlan: undefined,
-    level: undefined,
-    date: undefined
-  })
-  // 基础表单项配置
-  const baseFormItems = [
-    { label: '课程编码', key: 'courseID', type: 'input', placeholder: '请输入课程编码', clearable: true },
-    { label: '课程名称', key: 'courseName', type: 'input', placeholder: '请输入课程名称', clearable: true },
-    {
-      label: '课程类型',
-      key: 'courseType',
-      type: 'select',
-      props: {
-        placeholder: '请选择课程类型',
-        options: [
-          { label: '基础操作', value: '1' },
-          { label: '故障处理', value: '2' },
-          { label: '非正常处理', value: '3' }
-        ]
-      }
-    },
-    {
-      label: '培训对象',
-      key: 'trainObject',
-      type: 'select',
-      props: {
-        placeholder: '请选择培训对象',
-        options: [
-          { label: '行车调度员', value: '1' },
-          { label: '电力调度员', value: '2' },
-          { label: '供电检修员', value: '3' }
-        ]
-      }
-    },
-    {
-      label: '培训路线',
-      key: 'trainRoute',
-      type: 'select',
-      props: {
-        placeholder: '请选择培训路线',
-        options: [
-          { label: '成渝高铁线', value: '1' },
-          { label: '成西高铁线', value: '2' },
-          { label: '青藏铁路', value: '3' }
-        ]
-      }
-    },
-    { label: '培训计划', key: 'trainPlan', type: 'input', placeholder: '请输入培训计划', clearable: true },
-    {
-      label: '培训日期',
-      key: 'date',
-      type: 'datetime',
-      props: {
-        style: { width: '100%' },
-        placeholder: '请选择培训日期',
-        type: 'date',
-        valueFormat: 'YYYY-MM-DD',
-        shortcuts: [
-          { text: '今日', value: new Date() },
-          { text: '昨日', value: () => new Date(Date.now() - 86400000) },
-          { text: '一周前', value: () => new Date(Date.now() - 604800000) }
-        ]
-      }
-    },
-    { label: '创建人', key: 'createUser', type: 'input', placeholder: '请输入创建人', clearable: true }
-  ]
-
-  const search = async () => {
-    await searchBarBasicRef.value.validate()
-    console.log(`表单数据`, formDataBasic.value)
-  }
-</script>
-
-<style scoped lang="scss">
-  .search-bar {
-    padding-bottom: 20px;
-
-    .title {
-      margin-bottom: 5px;
-      font-size: 18px;
-      font-weight: 500;
-
-      &.m-15 {
-        margin-top: 15px;
-      }
-    }
-  }
-</style>