zhanghaijun пре 1 година
родитељ
комит
8e6440422c

BIN
src/assets/img/common/01.webp


BIN
src/assets/img/common/02.webp


BIN
src/assets/img/common/03.webp


BIN
src/assets/img/common/04.webp


+ 3 - 4
src/components/core/layouts/art-header-bar/index.vue

@@ -2,11 +2,11 @@
 <template>
   <div class="layout-top-bar" :class="[tabStyle]">
     <div class="menu">
-      <div class="left" style="display: flex">
+      <div class="left ml-6" style="display: flex">
         <!-- 系统信息  -->
         <div class="top-header" @click="toHome">
           <ArtLogo class="logo" />
-          <p v-if="width >= 1400">{{ AppConfig.systemInfo.name }}</p>
+          <p>{{ AppConfig.systemInfo.name }}</p>
         </div>
       </div>
 
@@ -140,7 +140,7 @@
   import { useI18n } from 'vue-i18n'
   import { useRouter } from 'vue-router'
   import { ElMessageBox } from 'element-plus'
-  import { useFullscreen, useWindowSize } from '@vueuse/core'
+  import { useFullscreen } from '@vueuse/core'
   import { LanguageEnum, MenuTypeEnum } from '@/enums/appEnum'
   import { useSettingStore } from '@/store/modules/setting'
   import { useUserStore } from '@/store/modules/user'
@@ -158,7 +158,6 @@
 
   const router = useRouter()
   const { locale, t } = useI18n()
-  const { width } = useWindowSize()
 
   const settingStore = useSettingStore()
   const userStore = useUserStore()

+ 2 - 2
src/types/component/dict.ts

@@ -1,9 +1,9 @@
-// import { Option } from '@/types'
+import { Option } from '@/types'
 export type ColorType = {
   bgColor?: string
   textColor?: string
 }
-export type NewOption = ColorType
+export type NewOption = Option & ColorType
 // 定义字典项的接口
 export interface DictItem {
   name: string

+ 21 - 16
src/views/dashboard/console/index.vue

@@ -2,13 +2,13 @@
   <div class="flex min-h-[400px] items-center justify-center p-4">
     <div class="box mb-20 flex h-[400px] w-full max-w-[1100px] flex-col rounded-2xl md:flex-row">
       <!-- 左侧大卡片 -->
-      <div class="relative h-[250px] w-full overflow-hidden rounded-2xl bg-cover bg-center shadow-md md:h-full md:w-[350px] md:rounded-r-none" :style="{ backgroundImage: `url(${Image01})` }">
+      <div class="card h-[250px] w-full bg-cover bg-center shadow-md md:h-full md:w-[350px] md:rounded-r-none" :style="{ backgroundImage: `url(${Image01})` }">
         <div class="absolute inset-0"></div>
         <div class="relative h-full p-8">
-          <div class="mt-6 text-2xl font-bold">教员培训</div>
-          <div class="text-md font-medium text-[#7B7A7C]">Subway Teacher Center</div>
+          <div class="mt-6 text-2xl font-bold">教员系统</div>
+          <div class="text-md font-medium text-[#7B7A7C]">Instructor System</div>
           <el-button class="absolute bottom-8 left-8" type="default" size="large" @click="() => router.push('/trainer/course/list')">
-            <span>开始学习课程</span>
+            <span>教员培训</span>
             <Icon :icon="'&#xe792;'" class="ml-2" />
           </el-button>
         </div>
@@ -17,13 +17,13 @@
       <!-- 右侧卡片区域 -->
       <div class="mt-4 flex w-full flex-col gap-4 md:mt-0 md:ml-4 md:w-[723px]">
         <!-- 上部分卡片 -->
-        <div class="relative h-[180px] w-full overflow-hidden rounded-2xl shadow-md md:h-[188px]" :style="{ backgroundImage: `url(${Image02})` }">
+        <div class="card h-[180px] w-full shadow-md md:h-[188px]" :style="{ backgroundImage: `url(${Image02})` }">
           <div class="absolute inset-0"></div>
           <div class="relative h-full p-8">
-            <div class="text-2xl font-bold">课程编辑</div>
-            <div class="text-md font-medium text-[#7B7A7C]">Subway Teacher Center</div>
+            <div class="text-2xl font-bold">场景编辑系统</div>
+            <div class="text-md font-medium text-[#7B7A7C]">Scene Editing System</div>
             <el-button class="absolute bottom-6 left-8" type="default" size="large" @click="() => router.push('/course/list')">
-              <span>开始学习课程</span>
+              <span>课程编辑</span>
               <Icon :icon="'&#xe792;'" class="ml-2" />
             </el-button>
           </div>
@@ -31,24 +31,24 @@
 
         <!-- 下部分两个小卡片 -->
         <div class="grid h-[180px] w-full grid-cols-1 gap-4 md:h-[188px] md:grid-cols-2">
-          <div class="relative overflow-hidden rounded-2xl shadow-md" :style="{ backgroundImage: `url(${Image03})` }">
+          <div class="card shadow-md" :style="{ backgroundImage: `url(${Image03})` }">
             <div class="absolute inset-0"></div>
             <div class="relative h-full p-8">
-              <div class="text-2xl font-bold">故障管理</div>
-              <div class="text-md font-medium text-[#7B7A7C]">Subway Teacher Center</div>
+              <div class="text-2xl font-bold">故障模拟系统</div>
+              <div class="text-md font-medium text-[#7B7A7C]">Fault Management System</div>
               <el-button class="absolute bottom-6 left-8" type="default" size="large" @click="() => router.push('/fault/list')">
-                <span>开始学习课程</span>
+                <span>故障处理</span>
                 <Icon :icon="'&#xe792;'" class="ml-2" />
               </el-button>
             </div>
           </div>
-          <div class="relative overflow-hidden rounded-2xl shadow-md" :style="{ backgroundImage: `url(${Image04})` }">
+          <div class="card relative overflow-hidden rounded-2xl shadow-md" :style="{ backgroundImage: `url(${Image04})` }">
             <div class="absolute inset-0"></div>
             <div class="relative h-full p-8">
-              <div class="text-2xl font-bold">评价中心</div>
-              <div class="text-md font-medium text-[#7B7A7C]">Subway Teacher Center</div>
+              <div class="text-2xl font-bold">评价系统</div>
+              <div class="text-md font-medium text-[#7B7A7C]">Evaluation System</div>
               <el-button class="absolute bottom-6 left-8" type="default" size="large" @click="() => router.push('/evaluation/list')">
-                <span>开始学习课程</span>
+                <span>去评价</span>
                 <Icon :icon="'&#xe792;'" class="ml-2" />
               </el-button>
             </div>
@@ -73,6 +73,11 @@
 <style lang="scss" scoped>
   .box {
     animation: fadeInUp 0.6s ease-out;
+    .card {
+      position: relative;
+      overflow: hidden;
+      border-radius: 10px;
+    }
   }
   @keyframes fadeInUp {
     from {

+ 118 - 0
src/views/train/course/course-add.vue

@@ -0,0 +1,118 @@
+<template>
+  <div>
+    <BasicForm style="margin-top: 12px" @change="handleChange" />
+  </div>
+</template>
+<script setup lang="ts">
+  import { FormItemSchema, useForm } from '@/components/form'
+  const options = ref([
+    { label: '选项一', value: '1' },
+    { label: '选项二', value: '2' },
+    { label: '选项三', value: '3' }
+  ])
+  const formState = ref({})
+  const handleChange = (val: any) => {
+    formState.value = val
+    detailData.value = val
+  }
+  const schema: FormItemSchema[] = [
+    {
+      prop: 'divider-input',
+      label: '',
+      labelWidth: '0px',
+      component: 'Divider',
+      colSpan: 24,
+      componentProps: { title: '课程基本信息' }
+    },
+    {
+      prop: 'courseName',
+      label: '课程名称',
+      component: 'Input',
+      colSpan: 8
+    },
+    {
+      prop: 'courseCode',
+      label: '课程编码',
+      component: 'Input',
+      colSpan: 8
+    },
+    {
+      prop: 'InputNumber',
+      label: '课程时长',
+      component: 'InputNumber',
+      colSpan: 8,
+      componentProps: { style: { width: '100%' } }
+    },
+    {
+      prop: 'courseDesc',
+      label: '课程描述',
+      component: 'Input',
+      colSpan: 24
+    },
+    {
+      prop: 'divider-select',
+      label: '',
+      labelWidth: '0px',
+      component: 'Divider',
+      colSpan: 24,
+      componentProps: {
+        title: '下拉、单选框、多选框'
+      }
+    },
+    {
+      prop: 'Select',
+      label: '下拉选择(单选)',
+      component: 'Select',
+      colSpan: 6,
+      defaultValue: '1',
+      componentProps: { options: options.value, clearable: true }
+    },
+    {
+      prop: 'Select-multiple',
+      label: '下拉选择(多选)',
+      component: 'Select',
+      colSpan: 6,
+      defaultValue: ['1', '2'],
+      componentProps: { options: options.value, clearable: true, multiple: true }
+    },
+    {
+      prop: 'RadioGroup',
+      label: '单选框',
+      component: 'RadioGroup',
+      colSpan: 6,
+      defaultValue: '1',
+      componentProps: { options: options.value }
+    },
+    {
+      prop: 'CheckboxGroup',
+      label: '多选框',
+      component: 'CheckboxGroup',
+      colSpan: 6,
+      defaultValue: ['1'],
+      componentProps: { options: options.value }
+    },
+    {
+      prop: 'divider-dict',
+      label: '',
+      labelWidth: '0px',
+      component: 'Divider',
+      colSpan: 24,
+      componentProps: { title: '字典组件' }
+    },
+    {
+      prop: 'ApiDict-sex-Select',
+      label: '字典组件',
+      helpMessage: 'renderType="Select"',
+      component: 'ApiDict',
+      colSpan: 12,
+      componentProps: { code: 'gender' }
+    }
+  ]
+  const [BasicForm] = useForm({
+    labelPosition: 'right',
+    labelWidth: '150px',
+    schema: schema
+  })
+  const detailData = ref({})
+</script>
+<style lang="scss" scoped></style>

+ 278 - 0
src/views/train/course/course-add2.vue

@@ -0,0 +1,278 @@
+<!-- 表单示例 -->
+<template>
+  <div class="form-example">
+    <h2 class="title">表单组件示例</h2>
+
+    <ElCard class="art-custom-card" shadow="never">
+      <ArtForm
+        ref="formRef"
+        v-model="formData"
+        :items="formItems"
+        :rules="formRules"
+        :defaultExpanded="true"
+        :labelWidth="labelWidth"
+        :labelPosition="labelPosition"
+        :span="span"
+        :gutter="gutter"
+        @reset="handleReset"
+        @submit="handleSubmit"
+      >
+        <template #slots>
+          <ElInput v-model="formData.slots" placeholder="我是插槽渲染出来的组件" />
+        </template>
+      </ArtForm>
+    </ElCard>
+
+    <div class="code">
+      <pre><code>{{ formData }}</code></pre>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { SearchFormItem } from '@/components/core/forms/art-search-bar/index.vue'
+  import { ElMessage, ElUpload, ElButton } from 'element-plus'
+  import type { UploadFile, UploadFiles, UploadUserFile } from 'element-plus'
+
+  interface Emits {
+    (e: 'update:modelValue', value: Record<string, any>): void
+    (e: 'search', params: Record<string, any>): void
+    (e: 'reset'): void
+  }
+  const emit = defineEmits<Emits>()
+
+  // 表单数据双向绑定
+  const formRef = ref()
+
+  // 表单数据
+  const formData = ref({
+    name: undefined,
+    phone: undefined,
+    level: undefined,
+    address: undefined,
+    slots: undefined,
+    date: undefined,
+    daterange: undefined,
+    cascader: undefined,
+    checkboxgroup: undefined,
+    userGender: undefined,
+    iconSelector: undefined,
+    status: undefined,
+    systemName: undefined,
+    fileUpload: [] as UploadUserFile[],
+    imageUpload: [] as UploadUserFile[],
+    multipleFiles: [] as UploadUserFile[],
+    richTextContent: ''
+  })
+
+  // 表单校验规则
+  const formRules = {
+    name: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+    phone: [
+      { required: true, message: '请输入手机号', trigger: 'blur' },
+      { min: 11, max: 11, message: '请输入11位手机号', trigger: 'blur' },
+      { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+    ]
+  }
+
+  const labelWidth = ref(100)
+  const labelPosition = ref<'right' | 'left' | 'top'>('right')
+  const span = ref(6)
+  const gutter = ref(12)
+
+  // 动态 options
+  const levelOptions = ref<{ label: string; value: string; disabled?: boolean }[]>([])
+
+  // 共享的选项数据
+  const LEVEL_OPTIONS = [
+    { label: '普通用户', value: 'normal' },
+    { label: 'VIP用户', value: 'vip' },
+    { label: '高级VIP', value: 'svip' },
+    { label: '企业用户', value: 'enterprise', disabled: true }
+  ]
+
+  const GENDER_OPTIONS = [
+    { label: '男', value: '1' },
+    { label: '女', value: '2' }
+  ]
+
+  const DATE_SHORTCUTS = [
+    { text: '今日', value: new Date() },
+    { text: '昨日', value: () => new Date(Date.now() - 86400000) },
+    { text: '一周前', value: () => new Date(Date.now() - 604800000) }
+  ]
+
+  // 创建表单项的工厂函数
+  const createFormItem = (config: any) => config
+
+  // 基础表单项配置
+  const baseFormItems = {
+    username: createFormItem({
+      label: '用户名',
+      key: 'name',
+      type: 'input',
+      placeholder: '请输入用户名',
+      clearable: true
+    }),
+    phone: createFormItem({
+      label: '手机号',
+      key: 'phone',
+      type: 'input',
+      props: { placeholder: '请输入手机号', maxlength: '11' }
+    }),
+    level: createFormItem({
+      label: '用户等级',
+      key: 'level',
+      type: 'select',
+      props: { placeholder: '请选择等级', options: LEVEL_OPTIONS }
+    }),
+    address: createFormItem({
+      label: '地址',
+      key: 'address',
+      type: 'input',
+      placeholder: '请输入地址'
+    }),
+    date: createFormItem({
+      label: '日期',
+      key: 'date',
+      type: 'datetime',
+      props: {
+        style: { width: '100%' },
+        placeholder: '请选择日期',
+        type: 'date',
+        valueFormat: 'YYYY-MM-DD',
+        shortcuts: DATE_SHORTCUTS
+      }
+    }),
+    gender: createFormItem({
+      label: '性别',
+      key: 'userGender',
+      type: 'radiogroup',
+      props: {
+        options: GENDER_OPTIONS
+      }
+    })
+  }
+
+  const userItem = ref<SearchFormItem>({
+    label: '用户名',
+    key: 'name',
+    type: 'input',
+    props: {
+      placeholder: '请输入用户名',
+      clearable: true
+    }
+  })
+
+  // 控制用户名字段是否显示
+  const showUserName = ref(true)
+  // 表单配置
+  const formItems = computed(() => [
+    ...(showUserName.value ? [userItem.value] : []),
+    {
+      ...baseFormItems.phone
+    },
+    {
+      ...baseFormItems.level,
+      props: { placeholder: '请选择等级', options: levelOptions.value }
+    },
+    baseFormItems.address,
+    baseFormItems.date,
+
+    {
+      ...baseFormItems.gender
+    },
+
+    {
+      label: '是否启用',
+      key: 'isEnabled',
+      type: 'switch',
+      props: {
+        placeholder: '请选择是否启用'
+      }
+    },
+    {
+      label: '年龄',
+      key: 'age',
+      type: 'number',
+      slots: {
+        suffix: () => h('span', { style: 'color: #909399; font-size: 12px' }, '岁')
+      }
+    },
+    // 文件上传示例 - 使用 h 函数渲染
+    {
+      label: '文件上传',
+      key: 'multipleFiles',
+      span: 12,
+      type: () =>
+        h(
+          ElUpload,
+          {
+            multiple: true,
+            limit: 5,
+            action: '#',
+            autoUpload: false,
+            showFileList: true,
+            accept: '.pdf,.mp4,.txt',
+            beforeUpload: (file: File) => {
+              console.log('准备上传文件:', file.name)
+              return true
+            },
+            onChange: (file: UploadFile, fileList: UploadFiles) => {
+              console.log('多文件变化:', file, fileList)
+              formData.value.multipleFiles = fileList as UploadUserFile[]
+            },
+            onRemove: (file: UploadFile, fileList: UploadFiles) => {
+              console.log('删除文件:', file, fileList)
+              formData.value.multipleFiles = fileList as UploadUserFile[]
+            },
+            onExceed: (files: File[], fileList: UploadUserFile[]) => {
+              ElMessage.warning(`最多只能上传 5 个文件,当前选择了 ${files.length + fileList.length} 个文件`)
+            }
+          },
+          { default: () => [h(ElButton, { type: 'primary' }, () => '点击上传')] }
+        )
+    }
+  ])
+
+  // 表单处理函数
+  const handleReset = () => {
+    console.log('重置表单')
+    emit('reset')
+  }
+
+  const handleSubmit = async () => {
+    await formRef.value.validate()
+    emit('search', formData.value)
+    console.log('表单数据', formData.value)
+  }
+</script>
+
+<style scoped lang="scss">
+  .form-example {
+    padding-bottom: 20px;
+
+    .title {
+      margin-bottom: 5px;
+      font-size: 18px;
+      font-weight: 500;
+
+      &.m-15 {
+        margin-top: 15px;
+      }
+    }
+
+    .code {
+      padding: 15px;
+      margin-top: 15px;
+      font-size: 14px;
+      background-color: var(--art-main-bg-color);
+      border: 1px solid var(--art-border-color);
+      border-radius: var(--el-border-radius-base);
+    }
+
+    .button-group {
+      margin-top: 15px;
+    }
+  }
+</style>

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

@@ -1,8 +1,78 @@
+<!-- 表格搜索栏示例 -->
 <template>
-  <div>
-    <h2>课程列表</h2>
-    <div>正在开发中,敬请期待</div>
+  <div class="search-bar">
+    <h2 class="title">课程列表</h2>
+    <ArtSearchBar ref="searchBarBasicRef" v-model="formDataBasic" :items="baseFormItems" @search="search"> </ArtSearchBar>
   </div>
 </template>
-<script lang="ts" setup></script>
-<style lang="scss" scoped></style>
+
+<script setup lang="ts">
+  // 表单数据双向绑定
+  const searchBarBasicRef = ref()
+  // 基础示例表单数据
+  const formDataBasic = ref({
+    name: undefined,
+    phone: undefined,
+    level: undefined,
+    address: undefined,
+    date: undefined,
+    daterange: undefined,
+    status: undefined
+  })
+  // 基础表单项配置
+  const baseFormItems = [
+    { label: '课程名称', key: 'name', type: 'input', placeholder: '请输入课程名称', clearable: true },
+    { label: '手机号', key: 'phone', type: 'input', props: { placeholder: '请输入手机号', maxlength: '11' } },
+    {
+      label: '用户等级',
+      key: 'level',
+      type: 'select',
+      props: {
+        placeholder: '请选择等级',
+        options: [
+          { label: '普通用户', value: 'normal' },
+          { label: 'VIP用户', value: 'vip' },
+          { label: '高级VIP', value: 'svip' },
+          { label: '企业用户', value: 'enterprise', disabled: 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) }
+        ]
+      }
+    }
+  ]
+
+  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>