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

feat: 列表支持卡片和列表模式切换

上级 030b547a
......@@ -128,7 +128,7 @@ defineExpose({ refetch, tableRef })
<slot></slot>
<!-- 主体 -->
<div class="table-list-bd">
<slot name="body" v-bind="{ data: dataList }">
<slot name="body" v-bind="{ data: dataList, loading }">
<!-- 桌面端表格 -->
<el-table v-if="!isMobile || !mobileCards" stripe :header-cell-style="{ background: '#ededed' }"
:data="dataList" v-loading="loading && hasLoading" v-bind="$attrs" ref="tableRef">
......
......@@ -10,7 +10,7 @@ export function getExperimentList(params: { course_id?: string }) {
return httpRequest.get('/api/lab/v1/teacher/book/experiments', { params })
}
// 获取指导书列表
export function getBookList(params?: { name?: string; experiment_id?: string; page?: number; page_size?: number }) {
export function getBookList(params?: { name?: string; experiment_id?: string; status?: string; page?: number; page_size?: number }) {
return httpRequest.get('/api/lab/v1/teacher/book/list', { params })
}
// 获取指导书详情
......
<script setup lang="ts">
import type { BookItem } from '../types'
import { CirclePlus } from '@element-plus/icons-vue'
import { useLocalStorage } from '@vueuse/core'
import AppList from '@/components/base/AppList.vue'
import ListItem from '../components/ListItem.vue'
import { getBookList } from '../api'
import { useGetExperimentList } from '../composables/useGetExperimentList'
import { useMapStore } from '@/stores/map'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const VIEW_MODE_STORAGE_KEY = 'list-view-mode'
type ViewMode = 'card' | 'list'
const status = computed(() => useMapStore().getMapValuesByKey('system_status'))
// 实验列表
const { experiments, updateExperiments } = useGetExperimentList()
updateExperiments()
......@@ -14,12 +19,21 @@ updateExperiments()
const route = useRoute()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
const viewMode = useLocalStorage<ViewMode>(VIEW_MODE_STORAGE_KEY, 'list')
function setViewMode(mode: ViewMode) {
viewMode.value = mode
}
function isActiveStatus(statusValue: string) {
return String(statusValue) === '1'
}
// 列表配置
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getBookList,
params: { name: '', experiment_id: route.query.experiment_id || '' }
params: { name: '', experiment_id: route.query.experiment_id || '', status: '' },
},
filters: [
{ type: 'input', prop: 'name', label: '实验指导书名称', placeholder: '请输入实验指导书名称' },
......@@ -30,10 +44,27 @@ const listOptions = $computed(() => {
options: experiments.value,
labelKey: 'name',
valueKey: 'id',
placeholder: '请选择关联实验'
}
placeholder: '请选择关联实验',
},
{
type: 'select',
prop: 'status',
label: '生效状态',
options: status.value,
placeholder: '请选择生效状态',
},
],
columns: [
{ label: '名称', prop: 'name' },
{ label: '文件大小', prop: 'size_name', width: 110 },
{ label: '观看次数', prop: 'pv', width: 110 },
{ label: '关联实验', prop: 'experiment_id_name' },
{ label: '生效状态', prop: 'status_name', width: 110, slots: 'table-status' },
{ label: '创建人', prop: 'created_operator_name', width: 120 },
{ label: '创建时间', prop: 'created_time', width: 180 },
{ label: '更新时间', prop: 'updated_time', width: 180 },
{ label: '操作', slots: 'table-x', width: 160 },
],
columns: [{ label: '名称', prop: 'name' }]
}
})
......@@ -62,13 +93,46 @@ function onUpdateSuccess() {
<el-button type="primary" :icon="CirclePlus" @click="handleAdd" v-permission="'v1-teacher-book-create'"
>新增实验指导书</el-button
>
<div class="view-switch">
<button
type="button"
class="view-switch__button"
:class="{ 'is-active': viewMode === 'card' }"
@click="setViewMode('card')"
title="卡片展示">
<span class="view-switch__icon view-switch__icon--card" aria-hidden="true">
<span v-for="item in 4" :key="item" class="view-switch__cell"></span>
</span>
</button>
<button
type="button"
class="view-switch__button"
:class="{ 'is-active': viewMode === 'list' }"
@click="setViewMode('list')"
title="列表展示">
<span class="view-switch__icon view-switch__icon--list" aria-hidden="true">
<span v-for="item in 3" :key="item" class="view-switch__line"></span>
</span>
</button>
</div>
</template>
<template #body="{ data }">
<template #body="{ data }" v-if="viewMode === 'card'">
<div class="list-card" v-if="data.length">
<ListItem v-for="item in data" :data="item" :key="item.id" @clickEdit="handleUpdate(item)"></ListItem>
</div>
<el-empty description="暂无数据" v-else />
</template>
<template #table-x="{ row }: { row: BookItem }">
<el-button type="primary" round v-permission="'v1-teacher-book-view'">
<router-link :to="`/admin/lab/book/${row.id}`" target="_blank">查看</router-link>
</el-button>
<el-button type="primary" round @click="handleUpdate(row)" v-permission="'v1-teacher-book-update'"
>编辑</el-button
>
</template>
<template #table-status="{ row }: { row: BookItem }">
<span :class="isActiveStatus(row.status) ? 'status-active' : 'status-inactive'">{{ row.status_name }}</span>
</template>
</AppList>
</AppCard>
<FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog>
......@@ -80,11 +144,94 @@ function onUpdateSuccess() {
background: #fff;
border-radius: 12px;
}
:deep(.table-list-buttons) {
align-items: center;
justify-content: space-between;
}
.view-switch {
display: inline-flex;
margin-left: auto;
border: 1px solid #e6e8eb;
border-radius: 10px;
background: #fff;
overflow: hidden;
}
.view-switch__button {
width: 32px;
height: 32px;
border: 0;
padding: 0;
background: transparent;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
&:hover {
background: #f6f7f9;
}
&.is-active {
background: rgba(186, 20, 62, 0.12);
}
}
.view-switch__icon {
width: 14px;
height: 14px;
color: #7a7f87;
display: inline-flex;
}
.view-switch__button.is-active .view-switch__icon {
color: var(--main-color);
}
.view-switch__icon--card {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2px;
}
.view-switch__cell {
width: 6px;
height: 6px;
border: 1.25px solid currentColor;
border-radius: 2px;
box-sizing: border-box;
}
.view-switch__icon--list {
flex-direction: column;
justify-content: space-between;
}
.view-switch__line {
width: 14px;
height: 2px;
border-radius: 999px;
background: currentColor;
}
.status-active {
color: #67c23a;
}
.status-inactive {
color: var(--main-color);
}
.list-card {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
@media (max-width: 1024px) { .list-card { grid-template-columns: repeat(2, 1fr); gap: 16px; } }
@media (max-width: 767px) { .list-card { grid-template-columns: 1fr; gap: 12px; } }
@media (max-width: 1024px) {
.list-card {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
}
@media (max-width: 767px) {
:deep(.table-list-buttons) {
justify-content: flex-start;
}
.view-switch {
margin-left: 0;
}
.list-card {
grid-template-columns: 1fr;
gap: 12px;
}
}
</style>
......@@ -10,7 +10,7 @@ export function getExperimentList(params: { course_id?: string }) {
return httpRequest.get('/api/lab/v1/teacher/experiment-cases/experiments', { params })
}
// 获取案例原文列表
export function getCaseList(params?: { name?: string; experiment_id?: string; page?: number; page_size?: number }) {
export function getCaseList(params?: { name?: string; experiment_id?: string; status?: string; page?: number; page_size?: number }) {
return httpRequest.get('/api/lab/v1/teacher/experiment-cases/list', { params })
}
// 获取案例原文详情
......
......@@ -27,6 +27,9 @@ const status = useMapStore().getMapValuesByKey('system_status')
const statusName = computed(() => {
return status.find(item => item.value === props.data.status)?.label
})
const statusClass = computed(() => {
return String(props.data.status) === '1' ? 'status-active' : 'status-inactive'
})
const sizeName = computed(() => {
return props.data.size + 'MB'
......@@ -54,7 +57,7 @@ const sizeName = computed(() => {
</div>
<div class="list-item-title">
<h2>{{ data.name }}</h2>
<h6>{{ statusName }}</h6>
<h6 :class="statusClass">{{ statusName }}</h6>
</div>
<div class="list-item-main">
<ol>
......@@ -175,6 +178,11 @@ const sizeName = computed(() => {
font-family: Source Han Sans CN;
font-weight: 500;
line-height: 20px;
}
.status-active {
color: #67c23a;
}
.status-inactive {
color: var(--main-color);
}
}
......
<script setup lang="ts">
import type { CaseItem } from '../types'
import { CirclePlus } from '@element-plus/icons-vue'
import { useLocalStorage } from '@vueuse/core'
import AppList from '@/components/base/AppList.vue'
import ListItem from '../components/ListItem.vue'
import { getCaseList } from '../api'
import { useGetExperimentList } from '../composables/useGetExperimentList'
import { useMapStore } from '@/stores/map'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const VIEW_MODE_STORAGE_KEY = 'list-view-mode'
type ViewMode = 'card' | 'list'
type CaseListRow = CaseItem & {
course_name: string
experiment_name: string
created_operator_name: string
status_name: string
size_name: string
}
const mapStore = useMapStore()
const status = computed(() => mapStore.getMapValuesByKey('system_status'))
// 实验列表
const { experiments, updateExperiments } = useGetExperimentList()
updateExperiments()
......@@ -14,12 +28,44 @@ updateExperiments()
const route = useRoute()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
const viewMode = useLocalStorage<ViewMode>(VIEW_MODE_STORAGE_KEY, 'list')
function setViewMode(mode: ViewMode) {
viewMode.value = mode
}
function getOperatorName(operator: CaseItem['created_operator']) {
return operator?.real_name || operator?.nickname || operator?.username || ''
}
function getStatusName(statusValue: string) {
return status.value.find((item) => item.value === statusValue)?.label || ''
}
function isActiveStatus(statusValue: string) {
return String(statusValue) === '1'
}
// 列表配置
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getCaseList,
params: { name: '', experiment_id: route.query.experiment_id || '' }
params: { name: '', experiment_id: route.query.experiment_id || '', status: '' },
callback(data: { total: number; list: CaseItem[] }) {
const { list = [], total = 0 } = data || {}
const dataList = list.map((item) => {
return {
...item,
course_name: item.course?.name || '',
experiment_name: item.experiment?.name || '',
created_operator_name: getOperatorName(item.created_operator),
status_name: getStatusName(item.status),
size_name: `${item.size}MB`,
}
})
return { list: dataList, total }
},
},
filters: [
{ type: 'input', prop: 'name', label: '案例原文名称', placeholder: '请输入案例原文名称' },
......@@ -30,10 +76,27 @@ const listOptions = $computed(() => {
options: experiments.value,
labelKey: 'name',
valueKey: 'id',
placeholder: '请选择关联实验'
}
placeholder: '请选择关联实验',
},
{
type: 'select',
prop: 'status',
label: '生效状态',
options: status.value,
placeholder: '请选择生效状态',
},
],
columns: [
{ label: '名称', prop: 'name' },
{ label: '文件大小', prop: 'size_name', width: 110 },
{ label: '观看次数', prop: 'pv', width: 110 },
{ label: '关联实验', prop: 'experiment_name' },
{ label: '生效状态', prop: 'status_name', width: 110, slots: 'table-status' },
{ label: '创建人', prop: 'created_operator_name', width: 120 },
{ label: '创建时间', prop: 'created_time', width: 180 },
{ label: '更新时间', prop: 'updated_time', width: 180 },
{ label: '操作', slots: 'table-x', width: 160 },
],
columns: [{ label: '名称', prop: 'name' }]
}
})
......@@ -62,13 +125,46 @@ function onUpdateSuccess() {
<el-button type="primary" :icon="CirclePlus" @click="handleAdd" v-permission="'teacher-experiment-cases-create'"
>新增案例原文</el-button
>
<div class="view-switch">
<button
type="button"
class="view-switch__button"
:class="{ 'is-active': viewMode === 'card' }"
@click="setViewMode('card')"
title="卡片展示">
<span class="view-switch__icon view-switch__icon--card" aria-hidden="true">
<span v-for="item in 4" :key="item" class="view-switch__cell"></span>
</span>
</button>
<button
type="button"
class="view-switch__button"
:class="{ 'is-active': viewMode === 'list' }"
@click="setViewMode('list')"
title="列表展示">
<span class="view-switch__icon view-switch__icon--list" aria-hidden="true">
<span v-for="item in 3" :key="item" class="view-switch__line"></span>
</span>
</button>
</div>
</template>
<template #body="{ data }: { data: CaseItem[] }">
<div class="list-card" v-if="data.length">
<template #body="{ data }: { data: CaseListRow[] }" v-if="viewMode === 'card'">
<div class="list-card" v-if="viewMode === 'card' && data.length">
<ListItem v-for="item in data" :data="item" :key="item.id" @clickEdit="handleUpdate(item)"></ListItem>
</div>
<el-empty description="暂无数据" v-else />
</template>
<template #table-x="{ row }: { row: CaseListRow }">
<el-button type="primary" round v-permission="'teacher-experiment-cases-detail'">
<router-link :to="`/admin/lab/case/${row.id}`" target="_blank">查看</router-link>
</el-button>
<el-button type="primary" round @click="handleUpdate(row)" v-permission="'teacher-experiment-cases-update'"
>编辑</el-button
>
</template>
<template #table-status="{ row }: { row: CaseListRow }">
<span :class="isActiveStatus(row.status) ? 'status-active' : 'status-inactive'">{{ row.status_name }}</span>
</template>
</AppList>
</AppCard>
<FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog>
......@@ -80,11 +176,94 @@ function onUpdateSuccess() {
background: #fff;
border-radius: 12px;
}
:deep(.table-list-buttons) {
align-items: center;
justify-content: space-between;
}
.view-switch {
display: inline-flex;
margin-left: auto;
border: 1px solid #e6e8eb;
border-radius: 10px;
background: #fff;
overflow: hidden;
}
.view-switch__button {
width: 32px;
height: 32px;
border: 0;
padding: 0;
background: transparent;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
&:hover {
background: #f6f7f9;
}
&.is-active {
background: rgba(186, 20, 62, 0.12);
}
}
.view-switch__icon {
width: 14px;
height: 14px;
color: #7a7f87;
display: inline-flex;
}
.view-switch__button.is-active .view-switch__icon {
color: var(--main-color);
}
.view-switch__icon--card {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2px;
}
.view-switch__cell {
width: 6px;
height: 6px;
border: 1.25px solid currentColor;
border-radius: 2px;
box-sizing: border-box;
}
.view-switch__icon--list {
flex-direction: column;
justify-content: space-between;
}
.view-switch__line {
width: 14px;
height: 2px;
border-radius: 999px;
background: currentColor;
}
.status-active {
color: #67c23a;
}
.status-inactive {
color: var(--main-color);
}
.list-card {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
@media (max-width: 1024px) { .list-card { grid-template-columns: repeat(2, 1fr); gap: 16px; } }
@media (max-width: 767px) { .list-card { grid-template-columns: 1fr; gap: 12px; } }
@media (max-width: 1024px) {
.list-card {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
}
@media (max-width: 767px) {
:deep(.table-list-buttons) {
justify-content: flex-start;
}
.view-switch {
margin-left: 0;
}
.list-card {
grid-template-columns: 1fr;
gap: 12px;
}
}
</style>
......@@ -43,7 +43,6 @@ const rules = ref({
end_time: [{ required: true, message: '请选择' }],
questions: [{ required: true, message: '请选择' }],
competition: [{ required: true, message: '请选择' }],
'config.practical_question': [{ required: true, message: '请输入实操试题' }],
})
const options = ref([
......@@ -196,12 +195,6 @@ function disabledMinSubmitDate(time: Date) {
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="大赛模式">
<el-radio-group v-model="form.config.mode">
<el-radio label="test">训练</el-radio>
<el-radio label="formal">正赛</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="开始时间" prop="start_time">
<el-date-picker
v-model="form.start_time"
......@@ -232,6 +225,12 @@ function disabledMinSubmitDate(time: Date) {
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="大赛模式">
<el-radio-group v-model="form.config.mode">
<el-radio label="test">训练</el-radio>
<el-radio label="formal">正赛</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="实操试题" prop="config.practical_question">
<AppEditor v-model="form.config.practical_question" :height="320" placeholder="请输入实操试题内容" />
</el-form-item>
......
......@@ -10,7 +10,7 @@ export function getExperimentList(params: { course_id?: string }) {
return httpRequest.get('/api/lab/v1/teacher/video/experiments', { params })
}
// 获取操作视频列表
export function getVideoList(params?: { name?: string; experiment_id?: string; page?: number; page_size?: number }) {
export function getVideoList(params?: { name?: string; experiment_id?: string; status?: string; page?: number; page_size?: number }) {
return httpRequest.get('/api/lab/v1/teacher/video/list', { params })
}
// 获取操作视频详情
......
<script setup lang="ts">
import type { VideoItem } from '../types'
import { CirclePlus } from '@element-plus/icons-vue'
import { useLocalStorage } from '@vueuse/core'
import AppList from '@/components/base/AppList.vue'
import ListItem from '../components/ListItem.vue'
import { getVideoList } from '../api'
import { useGetExperimentList } from '../composables/useGetExperimentList'
import { useMapStore } from '@/stores/map'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const VIEW_MODE_STORAGE_KEY = 'list-view-mode'
type ViewMode = 'card' | 'list'
const status = computed(() => useMapStore().getMapValuesByKey('system_status'))
// 实验列表
const { experiments, updateExperiments } = useGetExperimentList()
updateExperiments()
......@@ -14,12 +19,21 @@ updateExperiments()
const route = useRoute()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
const viewMode = useLocalStorage<ViewMode>(VIEW_MODE_STORAGE_KEY, 'list')
function setViewMode(mode: ViewMode) {
viewMode.value = mode
}
function isActiveStatus(statusValue: string) {
return String(statusValue) === '1'
}
// 列表配置
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getVideoList,
params: { name: '', experiment_id: route.query.experiment_id || '' }
params: { name: '', experiment_id: route.query.experiment_id || '', status: '' },
},
filters: [
{ type: 'input', prop: 'name', label: '实验操作视频名称', placeholder: '请输入实验操作视频名称' },
......@@ -30,10 +44,27 @@ const listOptions = $computed(() => {
options: experiments.value,
labelKey: 'name',
valueKey: 'id',
placeholder: '请选择关联实验'
}
placeholder: '请选择关联实验',
},
{
type: 'select',
prop: 'status',
label: '生效状态',
options: status.value,
placeholder: '请选择生效状态',
},
],
columns: [
{ label: '名称', prop: 'name' },
{ label: '文件大小', prop: 'size_name', width: 110 },
{ label: '观看次数', prop: 'pv', width: 110 },
{ label: '关联实验', prop: 'experiment_id_name' },
{ label: '生效状态', prop: 'status_name', width: 110, slots: 'table-status' },
{ label: '创建人', prop: 'created_operator_name', width: 120 },
{ label: '创建时间', prop: 'created_time', width: 180 },
{ label: '更新时间', prop: 'updated_time', width: 180 },
{ label: '操作', slots: 'table-x', width: 160 },
],
columns: [{ label: '名称', prop: 'name' }]
}
})
......@@ -62,13 +93,46 @@ function onUpdateSuccess() {
<el-button type="primary" :icon="CirclePlus" @click="handleAdd" v-permission="'v1-teacher-video-create'"
>新增操作视频</el-button
>
<div class="view-switch">
<button
type="button"
class="view-switch__button"
:class="{ 'is-active': viewMode === 'card' }"
@click="setViewMode('card')"
title="卡片展示">
<span class="view-switch__icon view-switch__icon--card" aria-hidden="true">
<span v-for="item in 4" :key="item" class="view-switch__cell"></span>
</span>
</button>
<button
type="button"
class="view-switch__button"
:class="{ 'is-active': viewMode === 'list' }"
@click="setViewMode('list')"
title="列表展示">
<span class="view-switch__icon view-switch__icon--list" aria-hidden="true">
<span v-for="item in 3" :key="item" class="view-switch__line"></span>
</span>
</button>
</div>
</template>
<template #body="{ data }">
<template #body="{ data }" v-if="viewMode === 'card'">
<div class="list-card" v-if="data.length">
<ListItem v-for="item in data" :data="item" :key="item.id" @clickEdit="handleUpdate(item)"></ListItem>
</div>
<el-empty description="暂无数据" v-else />
</template>
<template #table-x="{ row }: { row: VideoItem }">
<el-button type="primary" round v-permission="'v1-teacher-video-view'">
<router-link :to="`/admin/lab/video/${row.id}`" target="_blank">查看</router-link>
</el-button>
<el-button type="primary" round @click="handleUpdate(row)" v-permission="'v1-teacher-video-update'"
>编辑</el-button
>
</template>
<template #table-status="{ row }: { row: VideoItem }">
<span :class="isActiveStatus(row.status) ? 'status-active' : 'status-inactive'">{{ row.status_name }}</span>
</template>
</AppList>
</AppCard>
<FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog>
......@@ -80,12 +144,99 @@ function onUpdateSuccess() {
background: #fff;
border-radius: 12px;
}
:deep(.table-list-buttons) {
align-items: center;
justify-content: space-between;
}
.view-switch {
display: inline-flex;
margin-left: auto;
border: 1px solid #e6e8eb;
border-radius: 10px;
background: #fff;
overflow: hidden;
}
.view-switch__button {
width: 32px;
height: 32px;
border: 0;
padding: 0;
background: transparent;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
&:hover {
background: #f6f7f9;
}
&.is-active {
background: rgba(186, 20, 62, 0.12);
}
}
.view-switch__icon {
width: 14px;
height: 14px;
color: #7a7f87;
display: inline-flex;
}
.view-switch__button.is-active .view-switch__icon {
color: var(--main-color);
}
.view-switch__icon--card {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2px;
}
.view-switch__cell {
width: 6px;
height: 6px;
border: 1.25px solid currentColor;
border-radius: 2px;
box-sizing: border-box;
}
.view-switch__icon--list {
flex-direction: column;
justify-content: space-between;
}
.view-switch__line {
width: 14px;
height: 2px;
border-radius: 999px;
background: currentColor;
}
.status-active {
color: #67c23a;
}
.status-inactive {
color: var(--main-color);
}
.list-card {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 20px;
}
@media (max-width: 1400px) { .list-card { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 1024px) { .list-card { grid-template-columns: repeat(2, 1fr); gap: 16px; } }
@media (max-width: 767px) { .list-card { grid-template-columns: 1fr; gap: 12px; } }
@media (max-width: 1400px) {
.list-card {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 1024px) {
.list-card {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
}
@media (max-width: 767px) {
:deep(.table-list-buttons) {
justify-content: flex-start;
}
.view-switch {
margin-left: 0;
}
.list-card {
grid-template-columns: 1fr;
gap: 12px;
}
}
</style>
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论