Compare commits

...

12 Commits

Author SHA1 Message Date
Archer
9eb87ad13c Merge pull request #629 from maomao-cloud/patch-2
fix: IconPicker.vue
2025-07-22 11:34:11 +08:00
Archer
ed114dd7e8 Merge pull request #628 from maomao-cloud/patch-1
[fix] Update IconPicker.vue
2025-07-22 11:33:51 +08:00
Jack LIU
9f51e06afa fix: IconPicker.vue
fix: ElementPlusError: [el-pagination] [API] small is about to be deprecated in version 3.0.0, please use size instead.
2025-07-22 11:25:43 +08:00
Jack LIU
d84f4fc7be Update IconPicker.vue
fix: index = -1
2025-07-22 11:14:39 +08:00
Archer
ac06b7ae70 Merge pull request #615 from zhangqin2-yewu/feat_echarts
fix: 修改暗黑模式下echarts图例和标题的颜色等样式
2025-04-11 11:47:08 +08:00
zhangqin2-yewu
2dbee0c6ed fix: 修改暗黑模式下echarts图例和标题的颜色等样式 2025-04-09 15:12:09 +08:00
Archer
9f525a5277 Merge pull request #605 from xzz2021/master
fix: 修复表格列设置弹窗出现警告问题
2025-03-03 08:59:07 +08:00
xzz2021
d240b739b9 fix: 修复表格列设置弹窗出现警告问题 2025-03-01 10:17:43 +08:00
Archer
6ba8aa8acc Merge pull request #599 from lijiahong123/fix/theme_switch_comp_optimization
fix: 修复主题switch状态不统一的问题
2025-02-24 08:50:48 +08:00
Archer
00743c8b51 Merge pull request #598 from xzz2021/revert-596-master
Revert "feat: 添加表格组件及模拟数据接口,支持分页和动态列渲染"
2025-02-24 08:50:04 +08:00
lijiahong
f015fc2ff0 fix: 修复主题switch状态不统一的问题 2025-02-22 20:58:00 +08:00
xzz
e54defcc16 Revert "feat: 添加表格组件及模拟数据接口,支持分页和动态列渲染" 2025-02-22 11:29:55 +08:00
11 changed files with 78 additions and 323 deletions

View File

@@ -1,45 +1,8 @@
import request from '@/axios' import request from '@/axios'
import type { TableData } from './types' import type { TableData } from './types'
interface Result { export const getTableListApi = (params: any) => {
list: TableData[] return request.get({ url: '/mock/example/list', params })
total: number
}
// 生成模拟数据的辅助函数
const generateMockData = (count: number): TableData[] => {
const stages = ['开发阶段', '测试阶段', '生产阶段', '验证阶段', '量产阶段']
const carTypes = ['SUV', '轿车', 'MPV', '跑车', '新能源']
const creators = ['张三', '李四', '王五', '赵六', '钱七']
return Array.from({ length: count }, (_, index) => ({
ProjectId: `PRJ${String(index + 1).padStart(3, '0')}`,
ProjectStage: stages[Math.floor(Math.random() * stages.length)],
CarType: carTypes[Math.floor(Math.random() * carTypes.length)],
Creator: creators[Math.floor(Math.random() * creators.length)],
CreateTime: new Date(Date.now() - Math.floor(Math.random() * 90) * 24 * 60 * 60 * 1000)
.toISOString()
.split('T')[0],
id: index + 1
}))
}
export const getTableListApi = (params: any): Promise<IResponse<Result>> => {
const mockData = generateMockData(100)
const { pageIndex = 1, pageSize = 10 } = params
const start = (pageIndex - 1) * pageSize
const end = start + pageSize
return Promise.resolve({
code: 0,
data: {
list: mockData.slice(start, end),
total: mockData.length
}
})
// 如果要切换到真实API只需取消注释下面的代码
// return request.get({ url: '/mock/example/list', params })
} }
export const getCardTableListApi = (params: any) => { export const getCardTableListApi = (params: any) => {

View File

@@ -1,8 +1,9 @@
export interface TableData { export type TableData = {
ProjectId: string id: string
ProjectStage: string author: string
CarType: string title: string
Creator: string content: string
CreateTime: string importance: number
id?: number display_time: string
pageviews: number
} }

View File

@@ -57,7 +57,8 @@ const icons = [epIcons, antIcons, tIcons]
const iconName = ref(icons[0].prefix) const iconName = ref(icons[0].prefix)
const currentIconNameIndex = computed(() => { const currentIconNameIndex = computed(() => {
return icons.findIndex((item) => item.prefix === unref(iconName)) const index = icons.findIndex((item) => item.prefix === unref(iconName))
return index < 0 ? 0 : index
}) })
const tabChange = () => { const tabChange = () => {
@@ -171,7 +172,7 @@ const inputClear = () => {
v-model:current-page="currentPage" v-model:current-page="currentPage"
v-model:page-size="pageSize" v-model:page-size="pageSize"
:pager-count="5" :pager-count="5"
small size="small"
:page-sizes="[100, 200, 300, 400]" :page-sizes="[100, 200, 300, 400]"
layout="total, prev, pager, next, jumper" layout="total, prev, pager, next, jumper"
:total="filterItemIcons(icons[currentIconNameIndex].icons).length" :total="filterItemIcons(icons[currentIconNameIndex].icons).length"

View File

@@ -5,7 +5,7 @@ import { useI18n } from '@/hooks/web/useI18n'
import { ThemeSwitch } from '@/components/ThemeSwitch' import { ThemeSwitch } from '@/components/ThemeSwitch'
import { useCssVar } from '@vueuse/core' import { useCssVar } from '@vueuse/core'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { trim, setCssVar, getCssVar } from '@/utils' import { trim, setCssVar } from '@/utils'
import ColorRadioPicker from './components/ColorRadioPicker.vue' import ColorRadioPicker from './components/ColorRadioPicker.vue'
import InterfaceDisplay from './components/InterfaceDisplay.vue' import InterfaceDisplay from './components/InterfaceDisplay.vue'
import LayoutRadioPicker from './components/LayoutRadioPicker.vue' import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
@@ -147,12 +147,6 @@ const clear = () => {
storageClear() storageClear()
window.location.reload() window.location.reload()
} }
const themeChange = () => {
const color = getCssVar('--el-bg-color')
setMenuTheme(color)
setHeaderTheme(color)
}
</script> </script>
<template> <template>
@@ -172,7 +166,7 @@ const themeChange = () => {
<div class="text-center"> <div class="text-center">
<!-- 主题 --> <!-- 主题 -->
<ElDivider>{{ t('setting.theme') }}</ElDivider> <ElDivider>{{ t('setting.theme') }}</ElDivider>
<ThemeSwitch @change="themeChange" /> <ThemeSwitch />
<!-- 布局 --> <!-- 布局 -->
<ElDivider>{{ t('setting.layout') }}</ElDivider> <ElDivider>{{ t('setting.layout') }}</ElDivider>

View File

@@ -51,7 +51,7 @@ const confirm = () => {
const newColumns = cloneDeep(unref(settingColumns))?.map((item) => { const newColumns = cloneDeep(unref(settingColumns))?.map((item) => {
const fixed = unref(settingColumns)?.find((col) => col.field === item.field)?.fixed const fixed = unref(settingColumns)?.find((col) => col.field === item.field)?.fixed
item.hidden = !unref(checkColumns)?.includes(item.field) item.hidden = !unref(checkColumns)?.includes(item.field)
item.fixed = fixed ? fixed : undefined item.fixed = fixed ? fixed : false
return item return item
}) })
emit('confirm', [...unref(hiddenColumns), ...(newColumns || [])]) emit('confirm', [...unref(hiddenColumns), ...(newColumns || [])])
@@ -65,7 +65,7 @@ const restore = () => {
const initColumns = (columns: TableColumn[], isReStore = false) => { const initColumns = (columns: TableColumn[], isReStore = false) => {
const newColumns = columns?.filter((item) => { const newColumns = columns?.filter((item) => {
if (!isReStore) { if (!isReStore) {
item.fixed = item.fixed !== void 0 ? item.fixed : undefined item.fixed = item.fixed !== void 0 ? item.fixed : false
} }
return (item.type && !DEFAULT_FILTER_COLUMN.includes(item.type)) || !item.type return (item.type && !DEFAULT_FILTER_COLUMN.includes(item.type)) || !item.type
}) })
@@ -133,18 +133,18 @@ watch(
:key="item.field" :key="item.field"
class="flex items-center justify-between mt-12px" class="flex items-center justify-between mt-12px"
> >
<ElCheckbox :label="item.field"> <ElCheckbox :value="item.field">
{{ item.label }} {{ item.label }}
</ElCheckbox> </ElCheckbox>
<div class="flex items-center"> <div class="flex items-center">
<ElRadioGroup size="small" v-model="item.fixed"> <ElRadioGroup size="small" v-model="item.fixed">
<ElRadioButton label="left"> <ElRadioButton value="left">
<Icon icon="vi-ep:arrow-left" /> <Icon icon="vi-ep:arrow-left" />
</ElRadioButton> </ElRadioButton>
<ElRadioButton :label="undefined"> <ElRadioButton :value="false">
<Icon icon="vi-ep:close" /> <Icon icon="vi-ep:close" />
</ElRadioButton> </ElRadioButton>
<ElRadioButton label="right"> <ElRadioButton value="right">
<Icon icon="vi-ep:arrow-right" /> <Icon icon="vi-ep:arrow-right" />
</ElRadioButton> </ElRadioButton>
</ElRadioGroup> </ElRadioGroup>

View File

@@ -1,9 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { computed } from 'vue'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { ElSwitch } from 'element-plus' import { ElSwitch } from 'element-plus'
import { useIcon } from '@/hooks/web/useIcon' import { useIcon } from '@/hooks/web/useIcon'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { getCssVar } from '@/utils'
const { getPrefixCls } = useDesign() const { getPrefixCls } = useDesign()
@@ -18,15 +19,21 @@ const CrescentMoon = useIcon({ icon: 'vi-emojione-monotone:crescent-moon', color
const appStore = useAppStore() const appStore = useAppStore()
// 初始化获取是否是暗黑主题 // 初始化获取是否是暗黑主题
const isDark = ref(appStore.getIsDark) const isDark = computed({
get() {
return appStore.getIsDark
},
set(val: boolean) {
appStore.setIsDark(val)
const color = getCssVar('--el-bg-color')
appStore.setMenuTheme(color)
appStore.setHeaderTheme(color)
emit('change', val)
}
})
// 设置switch的背景颜色 // 设置switch的背景颜色
const blackColor = 'var(--el-color-black)' const blackColor = 'var(--el-color-black)'
const themeChange = (val: boolean) => {
appStore.setIsDark(val)
emit('change', val)
}
</script> </script>
<template> <template>
@@ -39,7 +46,6 @@ const themeChange = (val: boolean) => {
:active-color="blackColor" :active-color="blackColor"
:active-icon="Sun" :active-icon="Sun"
:inactive-icon="CrescentMoon" :inactive-icon="CrescentMoon"
@change="themeChange"
/> />
</template> </template>

View File

@@ -3,7 +3,7 @@ import PanelGroup from './components/PanelGroup.vue'
import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus' import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus'
import { Echart } from '@/components/Echart' import { Echart } from '@/components/Echart'
import { pieOptions, barOptions, lineOptions } from './echarts-data' import { pieOptions, barOptions, lineOptions } from './echarts-data'
import { ref, reactive } from 'vue' import { ref, reactive, computed, watch, onMounted } from 'vue'
import { import {
getUserAccessSourceApi, getUserAccessSourceApi,
getWeeklyUserActivityApi, getWeeklyUserActivityApi,
@@ -12,11 +12,15 @@ import {
import { set } from 'lodash-es' import { set } from 'lodash-es'
import { EChartsOption } from 'echarts' import { EChartsOption } from 'echarts'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useAppStore } from '@/store/modules/app'
const { t } = useI18n() const { t } = useI18n()
const loading = ref(true) const loading = ref(true)
const appStore = useAppStore()
const isDark = computed(() => appStore.getIsDark)
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
// 用户来源 // 用户来源
@@ -91,12 +95,40 @@ const getMonthlySales = async () => {
} }
} }
/**
* 更新 legend.textStyle
*/
const updateLegendTextStyle = (options) => {
const newTextStyle = {
color: isDark.value ? '#ccc' : '#333'
}
const inactiveColor = isDark.value ? '#abacac' : '#ccc'
set(options, 'title.textStyle', newTextStyle)
if (options !== barOptionsData) {
set(options, 'legend.textStyle', newTextStyle)
set(options, 'legend.inactiveColor', inactiveColor)
}
options === pieOptionsData && set(options, 'series[0].emptyCircleStyle.color', inactiveColor)
}
const getAllApi = async () => { const getAllApi = async () => {
await Promise.all([getUserAccessSource(), getWeeklyUserActivity(), getMonthlySales()]) await Promise.all([getUserAccessSource(), getWeeklyUserActivity(), getMonthlySales()])
loading.value = false loading.value = false
} }
getAllApi() getAllApi()
// 监听暗黑模式变化并重新更新样式
watch(isDark, () => {
updateLegendTextStyle(pieOptionsData)
updateLegendTextStyle(barOptionsData)
updateLegendTextStyle(lineOptionsData)
})
onMounted(() => {
updateLegendTextStyle(pieOptionsData)
updateLegendTextStyle(barOptionsData)
updateLegendTextStyle(lineOptionsData)
})
</script> </script>
<template> <template>

View File

@@ -1,125 +1,20 @@
<script setup lang="tsx"> <script setup lang="ts">
import { ElInput } from 'element-plus'
import { ContentWrap } from '@/components/ContentWrap' import { ContentWrap } from '@/components/ContentWrap'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { Table, TableColumn } from '@/components/Table'
import { getTableListApi } from '@/api/table'
import { TableData } from '@/api/table/types'
import { ref } from 'vue' import { ref } from 'vue'
import { ElTag, ElPagination } from 'element-plus'
import { BaseButton } from '@/components/Button'
import TableColumns from './modules/TableColumn'
interface Params { defineOptions({
pageIndex?: number name: 'Menu2'
pageSize?: number })
}
const { t } = useI18n() const { t } = useI18n()
const columns: TableColumn[] = [ const text = ref('')
...TableColumns,
{
field: 'action',
label: '操作',
slots: {
default: (data) => {
return (
<div class="flex gap-2">
<BaseButton type="primary" onClick={() => actionFn(data)}>
操作
</BaseButton>
<BaseButton type="danger" onClick={() => handleDelete(data)}>
删除
</BaseButton>
</div>
)
}
}
}
]
const loading = ref(true)
const tableDataList = ref<TableData[]>([])
// 分页相关数据
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
const getTableList = async (params?: Params) => {
loading.value = true
const res = await getTableListApi(
params || {
pageIndex: currentPage.value,
pageSize: pageSize.value
}
)
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
tableDataList.value = res.data.list
total.value = res.data.total // 设置总数
}
}
// 分页方法
const handleSizeChange = (val: number) => {
pageSize.value = val
getTableList()
}
const handleCurrentChange = (val: number) => {
currentPage.value = val
getTableList()
}
// 初始加载
getTableList()
const actionFn = (data: any) => {
console.log(data)
}
// 添加删除方法
const handleDelete = (data: any) => {
console.log('删除', data)
}
</script> </script>
<template> <template>
<ContentWrap :title="t('tableDemo.table')" :message="t('tableDemo.tableDes')"> <ContentWrap :title="t('levelDemo.menu')">
<div class="table-container"> <div class="flex items-center"> Menu2: <ElInput v-model="text" class="pl-20px" /> </div>
<Table
:columns="columns"
:data="tableDataList"
:loading="loading"
:defaultSort="{ prop: 'display_time', order: 'descending' }"
/>
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</ContentWrap> </ContentWrap>
</template> </template>
<style lang="less" scoped>
.table-container {
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
}
</style>

View File

@@ -1,26 +0,0 @@
import { ElTag } from 'element-plus'
const columns = [
{
field: 'ProjectId',
label: '項目ID'
},
{
field: 'ProjectStage',
label: '項目階段'
},
{
field: 'CarType',
label: '車輛類型'
},
{
field: 'Creator',
label: '創建人'
},
{
field: 'CreateTime',
label: '創建時間'
}
]
export default columns

View File

@@ -3,7 +3,7 @@ import { LoginForm, RegisterForm } from './components'
import { ThemeSwitch } from '@/components/ThemeSwitch' import { ThemeSwitch } from '@/components/ThemeSwitch'
import { LocaleDropdown } from '@/components/LocaleDropdown' import { LocaleDropdown } from '@/components/LocaleDropdown'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { getCssVar, underlineToHump } from '@/utils' import { underlineToHump } from '@/utils'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { ref } from 'vue' import { ref } from 'vue'
@@ -26,12 +26,6 @@ const toRegister = () => {
const toLogin = () => { const toLogin = () => {
isLogin.value = true isLogin.value = true
} }
const themeChange = () => {
const color = getCssVar('--el-bg-color')
appStore.setMenuTheme(color)
appStore.setHeaderTheme(color)
}
</script> </script>
<template> <template>
@@ -72,7 +66,7 @@ const themeChange = () => {
</div> </div>
<div class="flex justify-end items-center space-x-10px"> <div class="flex justify-end items-center space-x-10px">
<ThemeSwitch @change="themeChange" /> <ThemeSwitch />
<LocaleDropdown class="lt-xl:text-white dark:text-white" /> <LocaleDropdown class="lt-xl:text-white dark:text-white" />
</div> </div>
</div> </div>

View File

@@ -1,105 +0,0 @@
<script setup lang="tsx">
import { ContentWrap } from '@/components/ContentWrap'
import { useI18n } from '@/hooks/web/useI18n'
import { Table, TableColumn } from '@/components/Table'
import { getTableListApi } from '@/api/table'
import { TableData } from '@/api/table/types'
import { ref, h } from 'vue'
import { ElTag } from 'element-plus'
import { BaseButton } from '@/components/Button'
interface Params {
pageIndex?: number
pageSize?: number
}
const { t } = useI18n()
const columns: TableColumn[] = [
{
field: 'title',
label: t('tableDemo.title')
},
{
field: 'author',
label: t('tableDemo.author')
},
{
field: 'display_time',
label: t('tableDemo.displayTime'),
sortable: true
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return h(
ElTag,
{
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
},
() =>
cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')
)
}
},
{
field: 'pageviews',
label: t('tableDemo.pageviews')
},
{
field: 'action',
label: t('tableDemo.action'),
slots: {
default: (data) => {
return (
<BaseButton type="primary" onClick={() => actionFn(data)}>
{t('tableDemo.action')}
</BaseButton>
)
}
}
}
]
const loading = ref(true)
const tableDataList = ref<TableData[]>([])
const getTableList = async (params?: Params) => {
const res = await getTableListApi(
params || {
pageIndex: 1,
pageSize: 10
}
)
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
tableDataList.value = res.data.list
}
}
getTableList()
const actionFn = (data: any) => {
console.log(data)
}
</script>
<template>
<ContentWrap :title="t('tableDemo.table')" :message="t('tableDemo.tableDes')">
<Table
:columns="columns"
:data="tableDataList"
:loading="loading"
:defaultSort="{ prop: 'display_time', order: 'descending' }"
/>
</ContentWrap>
</template>