Merge branch 'main' of http://git.tensent.cn/molong/vueadmin
This commit is contained in:
320
src/components/scForm/index.vue
Normal file
320
src/components/scForm/index.vue
Normal file
@@ -0,0 +1,320 @@
|
||||
<template>
|
||||
<a-form :model="formData" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol" :layout="layout"
|
||||
@finish="handleFinish" @finish-failed="handleFinishFailed">
|
||||
<a-form-item v-for="item in formItems" :key="item.field" :label="item.label" :name="item.field"
|
||||
:required="item.required" :colon="item.colon">
|
||||
<!-- 输入框 -->
|
||||
<template v-if="item.type === 'input'">
|
||||
<a-input v-model:value="formData[item.field]" :placeholder="item.placeholder || `请输入${item.label}`"
|
||||
:disabled="item.disabled" :allow-clear="item.allowClear !== false" :max-length="item.maxLength"
|
||||
:type="item.inputType || 'text'" :prefix="item.prefix" :suffix="item.suffix"
|
||||
@change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 文本域 -->
|
||||
<template v-else-if="item.type === 'textarea'">
|
||||
<a-textarea v-model:value="formData[item.field]" :placeholder="item.placeholder || `请输入${item.label}`"
|
||||
:disabled="item.disabled" :allow-clear="item.allowClear !== false" :rows="item.rows || 4"
|
||||
:max-length="item.maxLength" :show-count="item.showCount"
|
||||
@change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 密码输入框 -->
|
||||
<template v-else-if="item.type === 'password'">
|
||||
<a-input-password v-model:value="formData[item.field]"
|
||||
:placeholder="item.placeholder || `请输入${item.label}`" :disabled="item.disabled"
|
||||
:max-length="item.maxLength" @change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 数字输入框 -->
|
||||
<template v-else-if="item.type === 'number'">
|
||||
<a-input-number v-model:value="formData[item.field]"
|
||||
:placeholder="item.placeholder || `请输入${item.label}`" :disabled="item.disabled" :min="item.min"
|
||||
:max="item.max" :step="item.step || 1" :precision="item.precision"
|
||||
:controls="item.controls !== false" style="width: 100%"
|
||||
@change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 下拉选择 -->
|
||||
<template v-else-if="item.type === 'select'">
|
||||
<a-select v-model:value="formData[item.field]" :placeholder="item.placeholder || `请选择${item.label}`"
|
||||
:disabled="item.disabled" :allow-clear="item.allowClear !== false" :mode="item.mode"
|
||||
:options="item.options" :field-names="item.fieldNames" style="width: 100%"
|
||||
@change="item.onChange && item.onChange(formData[item.field])">
|
||||
<template v-if="!item.options" #notFoundContent>
|
||||
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" description="暂无数据" />
|
||||
</template>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<!-- 单选框 -->
|
||||
<template v-else-if="item.type === 'radio'">
|
||||
<a-radio-group v-model:value="formData[item.field]" :disabled="item.disabled"
|
||||
:button-style="item.buttonStyle" @change="item.onChange && item.onChange(formData[item.field])">
|
||||
<template v-if="item.options">
|
||||
<a-radio v-for="opt in item.options" :key="opt.value" :value="opt.value"
|
||||
:disabled="opt.disabled">
|
||||
{{ opt.label }}
|
||||
</a-radio>
|
||||
</template>
|
||||
<template v-else-if="item.buttonStyle === 'solid'">
|
||||
<a-radio-button v-for="opt in item.options" :key="opt.value" :value="opt.value"
|
||||
:disabled="opt.disabled">
|
||||
{{ opt.label }}
|
||||
</a-radio-button>
|
||||
</template>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
|
||||
<!-- 多选框 -->
|
||||
<template v-else-if="item.type === 'checkbox'">
|
||||
<a-checkbox-group v-model:value="formData[item.field]" :disabled="item.disabled"
|
||||
@change="item.onChange && item.onChange(formData[item.field])">
|
||||
<template v-if="item.options">
|
||||
<a-checkbox v-for="opt in item.options" :key="opt.value" :value="opt.value"
|
||||
:disabled="opt.disabled">
|
||||
{{ opt.label }}
|
||||
</a-checkbox>
|
||||
</template>
|
||||
</a-checkbox-group>
|
||||
</template>
|
||||
|
||||
<!-- 开关 -->
|
||||
<template v-else-if="item.type === 'switch'">
|
||||
<a-switch v-model:checked="formData[item.field]" :disabled="item.disabled"
|
||||
:checked-children="item.checkedChildren || '开'" :un-checked-children="item.unCheckedChildren || '关'"
|
||||
@change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 日期选择 -->
|
||||
<template v-else-if="item.type === 'date'">
|
||||
<a-date-picker v-model:value="formData[item.field]"
|
||||
:placeholder="item.placeholder || `请选择${item.label}`" :disabled="item.disabled"
|
||||
:format="item.format || 'YYYY-MM-DD'" :value-format="item.valueFormat || 'YYYY-MM-DD'"
|
||||
style="width: 100%" @change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 日期范围选择 -->
|
||||
<template v-else-if="item.type === 'dateRange'">
|
||||
<a-range-picker v-model:value="formData[item.field]" :placeholder="item.placeholder || ['开始日期', '结束日期']"
|
||||
:disabled="item.disabled" :format="item.format || 'YYYY-MM-DD'"
|
||||
:value-format="item.valueFormat || 'YYYY-MM-DD'" style="width: 100%"
|
||||
@change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 时间选择 -->
|
||||
<template v-else-if="item.type === 'time'">
|
||||
<a-time-picker v-model:value="formData[item.field]"
|
||||
:placeholder="item.placeholder || `请选择${item.label}`" :disabled="item.disabled"
|
||||
:format="item.format || 'HH:mm:ss'" :value-format="item.valueFormat || 'HH:mm:ss'"
|
||||
style="width: 100%" @change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 上传 -->
|
||||
<template v-else-if="item.type === 'upload'">
|
||||
<a-upload v-model:file-list="formData[item.field]" :list-type="item.listType || 'text'"
|
||||
:action="item.action" :max-count="item.maxCount" :before-upload="item.beforeUpload"
|
||||
:custom-request="item.customRequest" :accept="item.accept" :disabled="item.disabled"
|
||||
@change="(info) => item.onChange && item.onChange(info)">
|
||||
<a-button v-if="item.listType !== 'picture-card'" type="primary">
|
||||
<UploadOutlined />
|
||||
点击上传
|
||||
</a-button>
|
||||
<div v-else>
|
||||
<PlusOutlined />
|
||||
<div class="ant-upload-text">上传</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</template>
|
||||
|
||||
<!-- 评分 -->
|
||||
<template v-else-if="item.type === 'rate'">
|
||||
<a-rate v-model:value="formData[item.field]" :disabled="item.disabled" :count="item.count || 5"
|
||||
:allow-half="item.allowHalf" @change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 滑块 -->
|
||||
<template v-else-if="item.type === 'slider'">
|
||||
<a-slider v-model:value="formData[item.field]" :disabled="item.disabled" :min="item.min || 0"
|
||||
:max="item.max || 100" :step="item.step || 1" :marks="item.marks" :range="item.range"
|
||||
@change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 级联选择 -->
|
||||
<template v-else-if="item.type === 'cascader'">
|
||||
<a-cascader v-model:value="formData[item.field]" :options="item.options"
|
||||
:placeholder="item.placeholder || `请选择${item.label}`" :disabled="item.disabled"
|
||||
:change-on-select="item.changeOnSelect" :field-names="item.fieldNames" style="width: 100%"
|
||||
@change="item.onChange && item.onChange(formData[item.field])" />
|
||||
</template>
|
||||
|
||||
<!-- 自定义插槽 -->
|
||||
<template v-else-if="item.type === 'slot'">
|
||||
<slot :name="item.slotName || item.field" :field="item.field" :value="formData[item.field]"></slot>
|
||||
</template>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<template v-if="item.tip">
|
||||
<div class="form-item-tip">{{ item.tip }}</div>
|
||||
</template>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 表单操作按钮 -->
|
||||
<a-form-item v-if="showActions" :wrapper-col="actionWrapperCol">
|
||||
<a-space>
|
||||
<a-button type="primary" html-type="submit" :loading="loading" :size="buttonSize">
|
||||
{{ submitText || '提交' }}
|
||||
</a-button>
|
||||
<a-button v-if="showReset" @click="handleReset" :size="buttonSize">
|
||||
{{ resetText || '重置' }}
|
||||
</a-button>
|
||||
<a-button v-if="showCancel" @click="handleCancel" :size="buttonSize">
|
||||
{{ cancelText || '取消' }}
|
||||
</a-button>
|
||||
<slot name="actions"></slot>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 自定义插槽 -->
|
||||
<slot></slot>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { Empty } from 'ant-design-vue'
|
||||
import { UploadOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const props = defineProps({
|
||||
// 表单项配置
|
||||
formItems: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
// 表单初始值
|
||||
initialValues: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 表单布局
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'horizontal', // horizontal, vertical, inline
|
||||
},
|
||||
// 标签宽度
|
||||
labelCol: {
|
||||
type: Object,
|
||||
default: () => ({ span: 6 }),
|
||||
},
|
||||
// 内容宽度
|
||||
wrapperCol: {
|
||||
type: Object,
|
||||
default: () => ({ span: 16 }),
|
||||
},
|
||||
// 是否显示操作按钮
|
||||
showActions: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 操作按钮布局
|
||||
actionWrapperCol: {
|
||||
type: Object,
|
||||
default: () => ({ offset: 6, span: 16 }),
|
||||
},
|
||||
// 是否显示重置按钮
|
||||
showReset: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 是否显示取消按钮
|
||||
showCancel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 按钮文字
|
||||
submitText: String,
|
||||
resetText: String,
|
||||
cancelText: String,
|
||||
// 按钮大小
|
||||
buttonSize: {
|
||||
type: String,
|
||||
default: 'middle',
|
||||
},
|
||||
// 加载状态
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['finish', 'finish-failed', 'reset', 'cancel'])
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({ ...props.initialValues })
|
||||
|
||||
// 表单验证规则
|
||||
const rules = computed(() => {
|
||||
const result = {}
|
||||
props.formItems.forEach((item) => {
|
||||
if (item.rules && item.rules.length > 0) {
|
||||
result[item.field] = item.rules
|
||||
}
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
// 监听初始值变化
|
||||
watch(
|
||||
() => props.initialValues,
|
||||
(newVal) => {
|
||||
Object.assign(formData, newVal)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
// 表单提交
|
||||
const handleFinish = (values) => {
|
||||
emit('finish', values)
|
||||
}
|
||||
|
||||
// 表单验证失败
|
||||
const handleFinishFailed = (errorInfo) => {
|
||||
emit('finish-failed', errorInfo)
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const handleReset = () => {
|
||||
Object.assign(formData, props.initialValues)
|
||||
emit('reset', formData)
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
emit('cancel')
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
formData,
|
||||
resetForm: handleReset,
|
||||
setFieldValue: (field, value) => {
|
||||
formData[field] = value
|
||||
},
|
||||
getFieldValue: (field) => {
|
||||
return formData[field]
|
||||
},
|
||||
setFieldsValue: (values) => {
|
||||
Object.assign(formData, values)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.form-item-tip {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
350
src/components/scTable/index.vue
Normal file
350
src/components/scTable/index.vue
Normal file
@@ -0,0 +1,350 @@
|
||||
<template>
|
||||
<div class="sc-table-wrapper">
|
||||
<!-- 表格操作栏 -->
|
||||
<div class="table-toolbar" v-if="showToolbar">
|
||||
<div class="toolbar-left">
|
||||
<slot name="toolbar-left"></slot>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<!-- 列设置 -->
|
||||
<a-dropdown v-if="showColumnSetting" :trigger="['click']">
|
||||
<a-button size="small">
|
||||
<SettingOutlined /> 列设置
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleColumnSetting">
|
||||
<a-menu-item v-for="column in columns" :key="column.key || column.dataIndex">
|
||||
<a-checkbox :checked="visibleColumns.includes(column.key || column.dataIndex)">
|
||||
{{ column.title }}
|
||||
</a-checkbox>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<!-- 刷新按钮 -->
|
||||
<a-button v-if="showRefresh" size="small" @click="handleRefresh">
|
||||
<ReloadOutlined />
|
||||
</a-button>
|
||||
|
||||
<slot name="toolbar-right"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格主体 -->
|
||||
<a-table v-bind="tableProps" :columns="processedColumns" :data-source="dataSource"
|
||||
:pagination="paginationConfig" :loading="loading" :row-selection="rowSelectionConfig" :scroll="scrollConfig"
|
||||
@change="handleTableChange" @row="handleRow" @row-click="handleRowClick" @row-dblclick="handleRowDblClick">
|
||||
<!-- 自定义列插槽将通过customRender在processedColumns中处理 -->
|
||||
|
||||
<!-- 操作列插槽 -->
|
||||
<template v-if="showActionColumn" #action="scope">
|
||||
<slot name="action" v-bind="scope"></slot>
|
||||
</template>
|
||||
|
||||
<!-- 空数据提示 -->
|
||||
<template #empty>
|
||||
<a-empty v-if="!error" :description="emptyText" />
|
||||
<div v-else class="table-error">
|
||||
<span>{{ errorMessage }}</span>
|
||||
<a-button size="small" type="link" @click="handleRefresh">重试</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { SettingOutlined, ReloadOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const props = defineProps({
|
||||
// 表格列配置
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
|
||||
// 数据源
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
// 加载状态
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
// 错误状态
|
||||
error: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
// 错误信息
|
||||
errorMessage: {
|
||||
type: String,
|
||||
default: '加载失败,请稍后重试',
|
||||
},
|
||||
|
||||
// 空数据提示
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '暂无数据',
|
||||
},
|
||||
|
||||
// 分页配置
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
showTotal: (total, range) => `共 ${total} 条记录,第 ${range[0]}-${range[1]} 条`,
|
||||
}),
|
||||
},
|
||||
|
||||
// 是否显示分页
|
||||
showPagination: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
// 行选择配置
|
||||
rowSelection: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: [],
|
||||
onChange: () => { },
|
||||
}),
|
||||
},
|
||||
|
||||
// 是否显示行选择
|
||||
showRowSelection: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
// 滚动配置
|
||||
scroll: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
// 表格属性
|
||||
tableProps: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
// 操作列配置
|
||||
actionColumn: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
}),
|
||||
},
|
||||
|
||||
// 是否显示操作列
|
||||
showActionColumn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
// 是否显示工具栏
|
||||
showToolbar: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
// 是否显示列设置
|
||||
showColumnSetting: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
// 是否显示刷新按钮
|
||||
showRefresh: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
// 可见列配置
|
||||
visibleColumns: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change', 'row-click', 'row-dblclick', 'refresh', 'column-change', 'selection-change', 'page-change', 'size-change', 'sort-change', 'filter-change'])
|
||||
|
||||
// 处理后的列配置
|
||||
const processedColumns = computed(() => {
|
||||
let result = [...props.columns]
|
||||
|
||||
// 过滤可见列
|
||||
if (props.visibleColumns.length > 0) {
|
||||
result = result.filter((column) => props.visibleColumns.includes(column.key || column.dataIndex))
|
||||
}
|
||||
|
||||
// 为每列添加customRender支持插槽
|
||||
result = result.map((column) => {
|
||||
const columnKey = column.key || column.dataIndex
|
||||
return {
|
||||
...column,
|
||||
customRender: (_, record, index) => {
|
||||
// 使用渲染函数返回空内容,实际内容通过插槽渲染
|
||||
return null
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// 添加操作列
|
||||
if (props.showActionColumn) {
|
||||
result.push({
|
||||
...props.actionColumn,
|
||||
customRender: (_, record, index) => {
|
||||
// 使用渲染函数返回空内容,实际内容通过插槽渲染
|
||||
return null
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
// 分页配置
|
||||
const paginationConfig = computed(() => {
|
||||
if (!props.showPagination) return false
|
||||
|
||||
return {
|
||||
...props.pagination,
|
||||
onChange: (page, pageSize) => {
|
||||
emit('page-change', page)
|
||||
emit('change', { current: page, pageSize })
|
||||
},
|
||||
onShowSizeChange: (current, pageSize) => {
|
||||
emit('size-change', pageSize)
|
||||
emit('change', { current, pageSize })
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// 行选择配置
|
||||
const rowSelectionConfig = computed(() => {
|
||||
if (!props.showRowSelection) return null
|
||||
|
||||
return {
|
||||
...props.rowSelection,
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
emit('selection-change', selectedRowKeys, selectedRows)
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// 滚动配置
|
||||
const scrollConfig = computed(() => {
|
||||
return {
|
||||
...props.scroll,
|
||||
}
|
||||
})
|
||||
|
||||
// 处理表格变化
|
||||
const handleTableChange = (pagination, filters, sorter, extra) => {
|
||||
if (sorter.field) {
|
||||
emit('sort-change', sorter)
|
||||
}
|
||||
|
||||
if (Object.keys(filters).length > 0) {
|
||||
emit('filter-change', filters)
|
||||
}
|
||||
|
||||
emit('change', { pagination, filters, sorter, extra })
|
||||
}
|
||||
|
||||
// 处理行点击
|
||||
const handleRowClick = (record, event) => {
|
||||
emit('row-click', record, event)
|
||||
}
|
||||
|
||||
// 处理行双击
|
||||
const handleRowDblClick = (record, event) => {
|
||||
emit('row-dblclick', record, event)
|
||||
}
|
||||
|
||||
// 处理行配置
|
||||
const handleRow = (record) => {
|
||||
return {
|
||||
onClick: (event) => handleRowClick(record, event),
|
||||
onDblclick: (event) => handleRowDblClick(record, event),
|
||||
}
|
||||
}
|
||||
|
||||
// 处理列设置
|
||||
const handleColumnSetting = ({ key, domEvent }) => {
|
||||
const checkbox = domEvent.target.closest('.ant-checkbox-wrapper').querySelector('.ant-checkbox-input')
|
||||
const checked = checkbox.checked
|
||||
|
||||
emit('column-change', {
|
||||
key,
|
||||
visible: checked,
|
||||
})
|
||||
}
|
||||
|
||||
// 处理刷新
|
||||
const handleRefresh = () => {
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
handleRefresh,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sc-table-wrapper {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.table-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.toolbar-left,
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 0;
|
||||
|
||||
.error-icon {
|
||||
font-size: 48px;
|
||||
color: #ff4d4f;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user