提交 bf03f0ee authored 作者: 王鹏飞's avatar 王鹏飞

chore: 修改创建课程页面

上级 a3c488f0
......@@ -81,6 +81,8 @@
"refThrottled": true,
"refWithControl": true,
"resolveComponent": true,
"resolveRef": true,
"resolveUnref": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
......@@ -108,6 +110,7 @@
"useAttrs": true,
"useBase64": true,
"useBattery": true,
"useBluetooth": true,
"useBreakpoints": true,
"useBroadcastChannel": true,
"useBrowserLocation": true,
......@@ -146,6 +149,7 @@
"useEyeDropper": true,
"useFavicon": true,
"useFetch": true,
"useFileDialog": true,
"useFileSystemAccess": true,
"useFocus": true,
"useFocusWithin": true,
......@@ -154,6 +158,7 @@
"useGamepad": true,
"useGeolocation": true,
"useIdle": true,
"useImage": true,
"useInfiniteScroll": true,
"useIntersectionObserver": true,
"useInterval": true,
......@@ -175,6 +180,7 @@
"useNavigatorLanguage": true,
"useNetwork": true,
"useNow": true,
"useObjectUrl": true,
"useOffsetPagination": true,
"useOnline": true,
"usePageLeave": true,
......@@ -200,12 +206,14 @@
"useSlots": true,
"useSpeechRecognition": true,
"useSpeechSynthesis": true,
"useStepper": true,
"useStorage": true,
"useStorageAsync": true,
"useStyleTag": true,
"useSwipe": true,
"useTemplateRefsList": true,
"useTextSelection": true,
"useTextareaAutosize": true,
"useThrottle": true,
"useThrottleFn": true,
"useThrottledRefHistory": true,
......@@ -232,6 +240,7 @@
"useWindowScroll": true,
"useWindowSize": true,
"watch": true,
"watchArray": true,
"watchAtMost": true,
"watchDebounced": true,
"watchEffect": true,
......@@ -241,6 +250,7 @@
"watchPostEffect": true,
"watchSyncEffect": true,
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true
}
......
......@@ -82,6 +82,8 @@ declare global {
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
......@@ -109,6 +111,7 @@ declare global {
const useAttrs: typeof import('vue')['useAttrs']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
......@@ -147,6 +150,7 @@ declare global {
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
......@@ -155,6 +159,7 @@ declare global {
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: typeof import('@vueuse/core')['useInterval']
......@@ -176,6 +181,7 @@ declare global {
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: typeof import('@vueuse/core')['useNow']
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
......@@ -201,12 +207,14 @@ declare global {
const useSlots: typeof import('vue')['useSlots']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: typeof import('@vueuse/core')['useStepper']
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
......@@ -233,6 +241,7 @@ declare global {
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch']
const watchArray: typeof import('@vueuse/core')['watchArray']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchEffect: typeof import('vue')['watchEffect']
......@@ -242,6 +251,7 @@ declare global {
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
差异被折叠。
......@@ -7,7 +7,7 @@
"build": "vue-tsc --noEmit && vite build --mode prod && npm run deploy",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:pre": "vue-tsc --noEmit && vite build --mode pre",
"preview": "vite preview --port 5050",
"preview": "vite preview --port 4173",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"deploy": "node ./deploy.js"
......@@ -17,8 +17,10 @@
"@tinymce/tinymce-vue": "^5.0.0",
"axios": "^0.27.2",
"blueimp-md5": "^2.19.0",
"dayjs": "^1.11.3",
"echarts": "^5.3.2",
"element-plus": "^2.2.5",
"element-plus": "^2.2.9",
"lodash-es": "^4.17.21",
"pinia": "^2.0.14",
"qs": "^6.10.5",
"sortablejs": "^1.15.0",
......
......@@ -75,7 +75,7 @@ function genNavClassName(data: IMenuItem) {
.app-header {
position: sticky;
top: 0;
z-index: 1000;
z-index: 2001;
padding: 0 20px;
display: flex;
align-items: center;
......
......@@ -37,8 +37,8 @@ export function updateCourse(data: any) {
}
// 获取封面列表
export function getCoverList() {
return httpRequest.get('/api/resource/v1/util/get-cover-list')
export function getCoverList(params: { type: string }) {
return httpRequest.get('/api/resource/v1/util/get-cover-list', { params })
}
// 获取课程详情
......
......@@ -36,8 +36,8 @@ const swiperChange = (type?: string) => {
type === 'prev' ? swiper.value.prev() : swiper.value.next()
}
// 获取封面
getCoverList().then(res => {
const filtersData = res.data.list.filter((i: any) => i.type === '1')
getCoverList({ type: '2' }).then(res => {
const filtersData = res.data.list
let index = 0
while (index < filtersData.length) {
swiperCovers.push(filtersData.slice(index, (index += 8)))
......
<script setup lang="ts">
import { searchLecturer } from '../../api'
import type { Lecturer } from '../../types'
import { uniqBy } from 'lodash-es'
interface Props {
modelValue: Lecturer[]
}
const emit = defineEmits(['change'])
const props = defineProps({
data: {
type: Array
}
})
const props = withDefaults(defineProps<Props>(), { modelValue: () => [] })
const emit = defineEmits<{
(e: 'update:modelValue', modelValue: Lecturer[]): void
}>()
// table回显的值
// let lecturerList: any = ref([])
const listOptions = computed(() => {
return {
columns: [
{ label: '头像', slots: 'table-avatar', align: 'center' },
{ label: '姓名', prop: 'name', align: 'center' },
// { label: '职位', prop: 'title', align: 'center' },
// { label: '机构', prop: 'office', align: 'center' },
{ label: '简介', slots: 'table-summarize', align: 'center' },
{ label: '操作', slots: 'table-operate', align: 'center' }
],
data: allLecturers.value.filter((item: any) => lecturerValue.value.find((i: any) => i === item.id))
data: props.modelValue
}
})
// 讲师弹窗
// 弹窗
const dialogVisible = ref(false)
// 选中的值
const selectList = ref<Lecturer[]>([])
interface ListItem {
name: string
id: string
}
const options = ref<ListItem[]>([])
const loading = ref(false)
// 所有被搜索出来的值
const allLecturers: any = ref([])
// 讲师选中的值
const lecturerValue = ref([])
// 远程搜索
const loading = ref(false)
const options = ref<Lecturer[]>([])
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
searchLecturer({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
options.value.forEach((item: any) => {
const findItem = allLecturers.value.find((cItem: any) => cItem.id === item.id)
if (!findItem) {
allLecturers.value.push(item)
}
})
})
} else {
options.value = []
}
if (!query) return
loading.value = true
searchLecturer({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
})
}
watch(
() => props.data,
value => {
if (value?.length) {
const list: any = value.map((item: any) => {
return item.id
})
lecturerValue.value = list
allLecturers.value = value
options.value = value as []
}
dialogVisible,
() => {
// 清空已选数据
selectList.value = []
},
{ immediate: true }
)
// 删除讲师
const removeLectuter = (id: string) => {
const index = lecturerValue.value.findIndex((ids: string) => ids === id)
lecturerValue.value.splice(index, 1)
changeData()
// 删除
const handelRemove = (id: string) => {
emit(
'update:modelValue',
props.modelValue.filter((item: any) => item.id !== id)
)
}
const changeData = () => {
emit('change', lecturerValue.value)
// 确认
const handlePrimary = () => {
const list = [...selectList.value, ...props.modelValue]
// 去重
emit('update:modelValue', uniqBy(list, 'id'))
dialogVisible.value = false
}
</script>
......@@ -95,32 +77,32 @@ const changeData = () => {
<div v-html="row.summarize"></div>
</template>
<template #table-operate="{ row }">
<el-button plain @click="removeLectuter(row.id)">删除</el-button>
<el-button plain @click="handelRemove(row.id)">删除</el-button>
</template>
</AppList>
<el-dialog v-model="dialogVisible" width="400px" title="添加讲师">
<div style="display: flex; justify-content: center">
讲师姓名:
<el-select
@change="changeData"
v-model="lecturerValue"
v-model="selectList"
multiple
filterable
remote
reserve-keyword
placeholder="请输入讲师姓名"
value-key="id"
:reserve-keyword="false"
:remote-method="remoteMethod"
:loading="loading"
style="flex: 1"
>
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item" />
</el-select>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确认</el-button>
<el-button type="primary" @click="handlePrimary">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<style lang="scss"></style>
<script setup lang="ts">
import { searchLive } from '../../api'
import type { Live } from '../../types'
import { uniqBy } from 'lodash-es'
import dayjs from 'dayjs'
const emit = defineEmits(['change'])
const add0 = (m: number) => {
return m < 10 ? '0' + m : m
}
const format = (n: number) => {
const time = new Date(n)
const y = time.getFullYear()
const m = time.getMonth() + 1
const d = time.getDate()
const h = time.getHours()
const mm = time.getMinutes()
const s = time.getSeconds()
return y + '-' + add0(m) + '-' + add0(d) + ' ' + add0(h) + ':' + add0(mm) + ':' + add0(s)
interface Props {
modelValue: Live[]
}
const props = defineProps({
data: {
type: Array
}
})
const props = withDefaults(defineProps<Props>(), { modelValue: () => [] })
const emit = defineEmits<{
(e: 'update:modelValue', modelValue: Live[]): void
}>()
const listOptions = computed(() => {
return {
......@@ -32,88 +21,64 @@ const listOptions = computed(() => {
label: '开始时间',
prop: 'start_time',
align: 'center',
computed({ row }: any) {
if (row.start_time === undefined) {
return '-'
} else {
return format(parseInt(row.start_time) * 1000)
}
computed({ row }: { row: Live }) {
return row.start_time ? dayjs(row.start_time * 1000).format('YYYY-MM-DD HH:mm:ss') : '-'
}
},
{
label: '结束时间',
prop: 'end_time',
align: 'center',
computed({ row }: any) {
if (row.end_time === undefined) {
return '-'
} else {
return format(parseInt(row.end_time) * 1000)
}
computed({ row }: { row: Live }) {
return row.start_time ? dayjs(row.end_time * 1000).format('YYYY-MM-DD HH:mm:ss') : '-'
}
},
{ label: '操作', slots: 'table-operate', align: 'center' }
],
data: allLecturers.value.filter((item: any) => lecturerValue.value.find((i: any) => i === item.id))
data: props.modelValue
}
})
// 直播弹窗
// 弹窗
const dialogVisible = ref(false)
// 选中的值
const selectList = ref<Live[]>([])
interface ListItem {
subject: string
id: string
}
const options = ref<ListItem[]>([])
const loading = ref(false)
// 所有被搜索出来的值
const allLecturers: any = ref([])
// 直播选中的值
const lecturerValue = ref([])
// 远程搜索
const loading = ref(false)
const options = ref<Live[]>([])
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
searchLive({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
options.value.forEach((item: any) => {
const findItem = allLecturers.value.find((cItem: any) => cItem.id === item.id)
if (!findItem) {
allLecturers.value.push(item)
}
})
})
} else {
options.value = []
}
if (!query) return
loading.value = true
searchLive({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
})
}
watch(
() => props.data,
value => {
if (value?.length) {
const list: any = value.map((item: any) => {
return item.id
})
lecturerValue.value = list
allLecturers.value = value
options.value = value as []
}
dialogVisible,
() => {
// 清空已选数据
selectList.value = []
},
{ immediate: true }
)
// 删除直播
const removeLectuter = (id: string) => {
const index = lecturerValue.value.findIndex((ids: string) => ids === id)
lecturerValue.value.splice(index, 1)
changeData()
// 删除
const handleRemove = (id: string) => {
emit(
'update:modelValue',
props.modelValue.filter((item: any) => item.id !== id)
)
}
const changeData = () => {
emit('change', lecturerValue.value)
// 确认
const handlePrimary = () => {
const list = [...selectList.value, ...props.modelValue]
// 去重
emit('update:modelValue', uniqBy(list, 'id'))
dialogVisible.value = false
}
</script>
......@@ -128,32 +93,32 @@ const changeData = () => {
<div v-html="row.summarize"></div>
</template>
<template #table-operate="{ row }">
<el-button plain @click="removeLectuter(row.id)">删除</el-button>
<el-button plain @click="handleRemove(row.id)">删除</el-button>
</template>
</AppList>
<el-dialog v-model="dialogVisible" width="400px" title="添加直播">
<div style="display: flex; justify-content: center">
直播会议号:
<el-select
@change="changeData"
v-model="lecturerValue"
v-model="selectList"
multiple
filterable
remote
reserve-keyword
placeholder="会议主题或者会议code"
value-key="id"
:reserve-keyword="false"
:remote-method="remoteMethod"
:loading="loading"
style="flex: 1"
>
<el-option v-for="item in options" :key="item.id" :label="item.subject" :value="item.id" />
<el-option v-for="item in options" :key="item.id" :label="item.subject" :value="item" />
</el-select>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确认</el-button>
<el-button type="primary" @click="handlePrimary">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<style lang="scss"></style>
export interface Lecturer {
avatar: string
id: string
name: string
summarize: string
}
export interface Live {
id: string
join_url: string
meeting_code: string
meeting_id: string
meeting_type: number
start_time: number
end_time: number
subject: string
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论