简单适配手机端:

1. 工作台招呼语一行显示,多余显示省略号
2. 查询框宽度统一,需手动调整(强迫症建议)
3. 分页符更新,电脑端与手机端分页功能不同
4. 表格工具栏更新,手机端取消文字显示
5. 表格操作按钮多的时候自动叠起
This commit is contained in:
ktianc 2022-11-19 11:30:32 +08:00
parent de83388b54
commit 97df1a2e19
19 changed files with 151 additions and 337 deletions

View File

@ -96,6 +96,14 @@ github地址https://github.com/vvandk/kinit 👩‍👦‍👦
- [x] 手机验证码登录功能
- [x] 简单适配手机端:
1. 工作台招呼语一行显示,多余显示省略号
2. 查询框宽度统一,需手动调整(强迫症建议)
3. 分页符更新,电脑端与手机端分页功能不同
4. 表格工具栏更新,手机端取消文字显示
5. 表格操作按钮多的时候自动叠起
## TODO
- [ ] 考虑支持多机部署方案,如果接口使用多机,那么用户是否支持统一认证
@ -308,6 +316,12 @@ pnpm run build:pro
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/3.png)
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/4.png)
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/6.png)
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/5.png)
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/5.png)
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/7.png)
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/8.png)
![image-20221010214526082](https://gitee.com/ktianc/kinit/raw/master/images/9.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 75 KiB

BIN
images/6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
images/7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
images/8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
images/9.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -14,6 +14,9 @@ import { useIcon } from '@/hooks/web/useIcon'
import { ref, PropType } from 'vue'
import { propTypes } from '@/utils/propTypes'
import draggable from 'vuedraggable'
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
const emit = defineEmits(['getList', 'update:tableSize'])
@ -57,16 +60,20 @@ const handleCheckChange = () => {
}
}
handleCheckChange()
const mobile = appStore.getMobile
</script>
<template>
<ElRow :gutter="10">
<ElCol :span="1.5">
<ElButton link :icon="refresh" @click="handleClickRefresh">刷新数据</ElButton>
<ElButton link :icon="refresh" @click="handleClickRefresh">{{
mobile ? '' : '刷新数据'
}}</ElButton>
</ElCol>
<ElCol :span="1.5" class="pt-4px">
<ElDropdown trigger="click" @command="handleCommand">
<ElButton link :icon="spacing">密度调整</ElButton>
<ElButton link :icon="spacing">{{ mobile ? '' : '密度调整' }}</ElButton>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem command="default">默认</ElDropdownItem>
@ -104,7 +111,7 @@ handleCheckChange()
</draggable>
</ElScrollbar>
<template #reference>
<ElButton link :icon="settings">字段设置</ElButton>
<ElButton link :icon="settings">{{ mobile ? '' : '字段设置' }}</ElButton>
</template>
</ElPopover>
</ElCol>

View File

@ -8,7 +8,7 @@ import { useForm } from '@/hooks/web/useForm'
import { findIndex } from '@/utils'
import { cloneDeep } from 'lodash-es'
import { set } from 'lodash-es'
import { FormSchema } from '@/types/form'
import { FormSchema, FormSetPropsType } from '@/types/form'
const { t } = useI18n()

View File

@ -7,6 +7,10 @@ import { getSlot } from '@/utils/tsxHelper'
import type { TableProps } from './types'
import { set } from 'lodash-es'
import { TableColumn, TableSlotDefault, Pagination, TableSetPropsType } from '../../../types/table'
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
const mobile = appStore.getMobile
export default defineComponent({
name: 'Table',
@ -112,7 +116,9 @@ export default defineComponent({
small: false,
background: false,
pagerCount: 7,
layout: 'sizes, prev, pager, next, jumper, ->, total',
layout: mobile
? 'prev, pager, next, ->, total'
: 'sizes, prev, pager, next, jumper, ->, total',
limits: [10, 20, 30, 40, 50, 100],
disabled: false,
hideOnSinglePage: false,

View File

@ -113,8 +113,8 @@ const user = wsCache.get(appStore.getUserInfo)
alt=""
class="w-70px h-70px rounded-[50%] mr-20px"
/>
<div>
<div class="text-20px text-700">
<div class="truncate">
<div class="text-20px text-700 truncate">
{{ t('workplace.goodMorning') }}{{ user.name }}{{ t('workplace.happyDay') }}
</div>
<div class="mt-10px text-14px text-gray-500">

View File

@ -1,310 +0,0 @@
import { EChartsOption } from 'echarts'
import { EChartsOption as EChartsWordOption } from 'echarts-wordcloud'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
export const lineOptions: EChartsOption = {
title: {
text: t('analysis.monthlySales'),
left: 'center'
},
xAxis: {
data: [
t('analysis.january'),
t('analysis.february'),
t('analysis.march'),
t('analysis.april'),
t('analysis.may'),
t('analysis.june'),
t('analysis.july'),
t('analysis.august'),
t('analysis.september'),
t('analysis.october'),
t('analysis.november'),
t('analysis.december')
],
boundaryGap: false,
axisTick: {
show: false
}
},
grid: {
left: 20,
right: 20,
bottom: 20,
top: 80,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: {
axisTick: {
show: false
}
},
legend: {
data: [t('analysis.estimate'), t('analysis.actual')],
top: 50
},
series: [
{
name: t('analysis.estimate'),
smooth: true,
type: 'line',
data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123],
animationDuration: 2800,
animationEasing: 'cubicInOut'
},
{
name: t('analysis.actual'),
smooth: true,
type: 'line',
itemStyle: {},
data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123],
animationDuration: 2800,
animationEasing: 'quadraticOut'
}
]
}
export const pieOptions: EChartsOption = {
title: {
text: t('analysis.userAccessSource'),
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: [
t('analysis.directAccess'),
t('analysis.mailMarketing'),
t('analysis.allianceAdvertising'),
t('analysis.videoAdvertising'),
t('analysis.searchEngines')
]
},
series: [
{
name: t('analysis.userAccessSource'),
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: [
{ value: 335, name: t('analysis.directAccess') },
{ value: 310, name: t('analysis.mailMarketing') },
{ value: 234, name: t('analysis.allianceAdvertising') },
{ value: 135, name: t('analysis.videoAdvertising') },
{ value: 1548, name: t('analysis.searchEngines') }
]
}
]
}
export const barOptions: EChartsOption = {
title: {
text: t('analysis.weeklyUserActivity'),
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: 50,
right: 20,
bottom: 20
},
xAxis: {
type: 'category',
data: [
t('analysis.monday'),
t('analysis.tuesday'),
t('analysis.wednesday'),
t('analysis.thursday'),
t('analysis.friday'),
t('analysis.saturday'),
t('analysis.sunday')
],
axisTick: {
alignWithLabel: true
}
},
yAxis: {
type: 'value'
},
series: [
{
name: t('analysis.activeQuantity'),
data: [13253, 34235, 26321, 12340, 24643, 1322, 1324],
type: 'bar'
}
]
}
export const radarOption: EChartsOption = {
legend: {
data: [t('workplace.personal'), t('workplace.team')]
},
radar: {
// shape: 'circle',
indicator: [
{ name: t('workplace.quote'), max: 65 },
{ name: t('workplace.contribution'), max: 160 },
{ name: t('workplace.hot'), max: 300 },
{ name: t('workplace.yield'), max: 130 },
{ name: t('workplace.follow'), max: 100 }
]
},
series: [
{
name: `xxx${t('workplace.index')}`,
type: 'radar',
data: [
{
value: [42, 30, 20, 35, 80],
name: t('workplace.personal')
},
{
value: [50, 140, 290, 100, 90],
name: t('workplace.team')
}
]
}
]
}
export const wordOptions: EChartsWordOption = {
series: [
{
type: 'wordCloud',
gridSize: 2,
sizeRange: [12, 50],
rotationRange: [-90, 90],
shape: 'pentagon',
width: 600,
height: 400,
drawOutOfBound: true,
textStyle: {
color: function () {
return (
'rgb(' +
[
Math.round(Math.random() * 160),
Math.round(Math.random() * 160),
Math.round(Math.random() * 160)
].join(',') +
')'
)
}
},
emphasis: {
textStyle: {
shadowBlur: 10,
shadowColor: '#333'
}
},
data: [
{
name: 'Sam S Club',
value: 10000,
textStyle: {
color: 'black'
},
emphasis: {
textStyle: {
color: 'red'
}
}
},
{
name: 'Macys',
value: 6181
},
{
name: 'Amy Schumer',
value: 4386
},
{
name: 'Jurassic World',
value: 4055
},
{
name: 'Charter Communications',
value: 2467
},
{
name: 'Chick Fil A',
value: 2244
},
{
name: 'Planet Fitness',
value: 1898
},
{
name: 'Pitch Perfect',
value: 1484
},
{
name: 'Express',
value: 1112
},
{
name: 'Home',
value: 965
},
{
name: 'Johnny Depp',
value: 847
},
{
name: 'Lena Dunham',
value: 582
},
{
name: 'Lewis Hamilton',
value: 555
},
{
name: 'KXAN',
value: 550
},
{
name: 'Mary Ellen Mark',
value: 462
},
{
name: 'Farrah Abraham',
value: 366
},
{
name: 'Rita Ora',
value: 360
},
{
name: 'Serena Williams',
value: 282
},
{
name: 'NCAA baseball tournament',
value: 273
},
{
name: 'Point Break',
value: 265
}
]
}
]
}

View File

@ -149,7 +149,10 @@ export const searchSchema = reactive<FormSchema[]>([
label: '角色名称',
component: 'Input',
componentProps: {
clearable: false
clearable: false,
style: {
width: '214px'
}
}
},
{
@ -157,7 +160,10 @@ export const searchSchema = reactive<FormSchema[]>([
label: '权限字符',
component: 'Input',
componentProps: {
clearable: false
clearable: false,
style: {
width: '214px'
}
}
},
{
@ -166,7 +172,7 @@ export const searchSchema = reactive<FormSchema[]>([
component: 'Select',
componentProps: {
style: {
width: '100%'
width: '214px'
},
options: [
{

View File

@ -39,7 +39,7 @@ export const columns = reactive<TableColumn[]>([
},
{
field: 'last_login',
label: '最近一次登录时间',
label: '最近登录时间',
show: true
},
{
@ -62,7 +62,12 @@ export const schema = reactive<FormSchema[]>([
colProps: {
span: 12
},
component: 'Input'
component: 'Input',
componentProps: {
style: {
width: '100%'
}
}
},
{
field: 'telephone',
@ -70,7 +75,12 @@ export const schema = reactive<FormSchema[]>([
colProps: {
span: 12
},
component: 'Input'
component: 'Input',
componentProps: {
style: {
width: '100%'
}
}
},
{
field: 'nickname',
@ -78,7 +88,12 @@ export const schema = reactive<FormSchema[]>([
colProps: {
span: 12
},
component: 'Input'
component: 'Input',
componentProps: {
style: {
width: '100%'
}
}
},
{
field: 'gender',
@ -156,7 +171,10 @@ export const searchSchema = reactive<FormSchema[]>([
label: '姓名',
component: 'Input',
componentProps: {
clearable: false
clearable: false,
style: {
width: '214px'
}
}
},
{
@ -164,7 +182,10 @@ export const searchSchema = reactive<FormSchema[]>([
label: '手机号',
component: 'Input',
componentProps: {
clearable: false
clearable: false,
style: {
width: '214px'
}
}
},
{
@ -173,7 +194,7 @@ export const searchSchema = reactive<FormSchema[]>([
component: 'Select',
componentProps: {
style: {
width: '100%'
width: '214px'
},
options: [
{

View File

@ -16,7 +16,17 @@ import Write from './components/Write.vue'
import Import from './components/Import.vue'
import Password from './components/Password.vue'
import { Dialog } from '@/components/Dialog'
import { ElButton, ElMessage, ElSwitch, ElRow, ElCol } from 'element-plus'
import {
ElButton,
ElMessage,
ElSwitch,
ElRow,
ElCol,
ElDropdown,
ElDropdownMenu,
ElDropdownItem,
ElIcon
} from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { selectDictLabel, DictDetail } from '@/utils/dict'
import { useDictStore } from '@/store/modules/dict'
@ -24,6 +34,9 @@ import { useAuthStoreWithOut } from '@/store/modules/auth'
import { useRouter } from 'vue-router'
import { RightToolbar } from '@/components/RightToolbar'
import { Search } from '@/components/Search'
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
const { t } = useI18n()
@ -171,6 +184,21 @@ const sendPasswordToSMS = async () => {
return ElMessage.warning('请先选择数据')
}
}
const mobile = appStore.getMobile
//
const handleCommand = (command: string) => {
if (command === 'a') {
importList()
} else if (command === 'b') {
exportQueryList()
} else if (command === 'c') {
sendPasswordToSMS()
} else if (command === 'd') {
delDatas(null, true)
}
}
</script>
<template>
@ -182,18 +210,44 @@ const sendPasswordToSMS = async () => {
<ElCol :span="1.5" v-hasPermi="['auth.user.create']">
<ElButton type="primary" @click="AddAction">新增用户</ElButton>
</ElCol>
<ElCol :span="1.5" v-hasPermi="['auth.user.import']">
<ElCol :span="1.5" v-hasPermi="['auth.user.import']" v-if="!mobile">
<ElButton @click="importList">批量导入用户</ElButton>
</ElCol>
<ElCol :span="1.5" v-hasPermi="['auth.user.export']">
<ElCol :span="1.5" v-hasPermi="['auth.user.export']" v-if="!mobile">
<ElButton @click="exportQueryList">导出筛选用户</ElButton>
</ElCol>
<ElCol :span="1.5" v-hasPermi="['auth.user.reset']">
<ElCol :span="1.5" v-hasPermi="['auth.user.reset']" v-if="!mobile">
<ElButton @click="sendPasswordToSMS">重置密码通知短信</ElButton>
</ElCol>
<ElCol :span="1.5" v-hasPermi="['auth.user.delete']">
<ElCol :span="1.5" v-hasPermi="['auth.user.delete']" v-if="!mobile">
<ElButton type="danger" @click="delDatas(null, true)">批量删除</ElButton>
</ElCol>
<ElCol :span="1.5" v-if="mobile">
<ElDropdown trigger="click" @command="handleCommand">
<ElButton>
更多
<el-icon class="el-icon--right">
<Icon icon="mdi:keyboard-arrow-down" />
</el-icon>
</ElButton>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem command="a" v-hasPermi="['auth.user.import']"
>批量导入用户</ElDropdownItem
>
<ElDropdownItem command="b" v-hasPermi="['auth.user.export']"
>导出筛选用户</ElDropdownItem
>
<ElDropdownItem command="c" v-hasPermi="['auth.user.reset']"
>重置密码通知短信</ElDropdownItem
>
<ElDropdownItem command="d" v-hasPermi="['auth.user.delete']"
>批量删除</ElDropdownItem
>
</ElDropdownMenu>
</template>
</ElDropdown>
</ElCol>
</ElRow>
<RightToolbar @get-list="getList" v-model:table-size="tableSize" v-model:columns="columns" />
</div>
@ -272,3 +326,12 @@ const sendPasswordToSMS = async () => {
</Dialog>
</ContentWrap>
</template>
<style scoped>
.el-dropdown-link {
cursor: pointer;
color: var(--el-color-primary);
display: flex;
align-items: center;
}
</style>

View File

@ -1,3 +1,4 @@
import { FormSchema } from '@/types/form'
import { TableColumn } from '@/types/table'
import { reactive } from 'vue'
@ -96,7 +97,10 @@ export const searchSchema = reactive<FormSchema[]>([
label: '手机号',
component: 'Input',
componentProps: {
clearable: false
clearable: false,
style: {
width: '214px'
}
}
},
{
@ -104,7 +108,10 @@ export const searchSchema = reactive<FormSchema[]>([
label: '登陆地址',
component: 'Input',
componentProps: {
clearable: false
clearable: false,
style: {
width: '214px'
}
}
},
{
@ -113,7 +120,7 @@ export const searchSchema = reactive<FormSchema[]>([
component: 'Select',
componentProps: {
style: {
width: '100%'
width: '214px'
},
options: [
{