commit
9cbccfae96
|
@ -106,7 +106,9 @@
|
||||||
"vite-plugin-purge-icons": "^0.9.2",
|
"vite-plugin-purge-icons": "^0.9.2",
|
||||||
"vite-plugin-style-import": "2.0.0",
|
"vite-plugin-style-import": "2.0.0",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vue-tsc": "^1.8.8"
|
"vue-tsc": "^1.8.8",
|
||||||
|
"vue-draggable-plus": "^0.2.6",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14.18.0"
|
"node": ">= 14.18.0"
|
||||||
|
|
|
@ -17,6 +17,9 @@ import { getSlot } from '@/utils/tsxHelper'
|
||||||
import TableActions from './components/TableActions.vue'
|
import TableActions from './components/TableActions.vue'
|
||||||
// import Sortable from 'sortablejs'
|
// import Sortable from 'sortablejs'
|
||||||
// import { Icon } from '@/components/Icon'
|
// import { Icon } from '@/components/Icon'
|
||||||
|
import { useAppStore } from '@/store/modules/app'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Table',
|
name: 'Table',
|
||||||
|
@ -121,7 +124,9 @@ export default defineComponent({
|
||||||
default: () => undefined
|
default: () => undefined
|
||||||
},
|
},
|
||||||
rowKey: propTypes.string.def('id'),
|
rowKey: propTypes.string.def('id'),
|
||||||
emptyText: propTypes.string.def('No Data'),
|
emptyText: propTypes.string.def('暂无数据'),
|
||||||
|
// 表格工具栏缓存唯一标识符
|
||||||
|
activeUID: propTypes.string.def(''),
|
||||||
defaultExpandAll: propTypes.bool.def(false),
|
defaultExpandAll: propTypes.bool.def(false),
|
||||||
expandRowKeys: {
|
expandRowKeys: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<string[]>,
|
||||||
|
@ -345,7 +350,7 @@ export default defineComponent({
|
||||||
const renderTreeTableColumn = (columnsChildren: TableColumn[]) => {
|
const renderTreeTableColumn = (columnsChildren: TableColumn[]) => {
|
||||||
const { align, headerAlign, showOverflowTooltip, preview } = unref(getProps)
|
const { align, headerAlign, showOverflowTooltip, preview } = unref(getProps)
|
||||||
return columnsChildren.map((v) => {
|
return columnsChildren.map((v) => {
|
||||||
if (v.hidden) return null
|
if (v.show === false) return null
|
||||||
const props = { ...v } as any
|
const props = { ...v } as any
|
||||||
if (props.children) delete props.children
|
if (props.children) delete props.children
|
||||||
|
|
||||||
|
@ -417,7 +422,7 @@ export default defineComponent({
|
||||||
} = unref(getProps)
|
} = unref(getProps)
|
||||||
|
|
||||||
return (columnsChildren || columns).map((v) => {
|
return (columnsChildren || columns).map((v) => {
|
||||||
if (v.hidden) return null
|
if (v.show === false) return null
|
||||||
if (v.type === 'index') {
|
if (v.type === 'index') {
|
||||||
return (
|
return (
|
||||||
<ElTableColumn
|
<ElTableColumn
|
||||||
|
@ -429,6 +434,7 @@ export default defineComponent({
|
||||||
headerAlign={v.headerAlign || headerAlign}
|
headerAlign={v.headerAlign || headerAlign}
|
||||||
label={v.label}
|
label={v.label}
|
||||||
width="65px"
|
width="65px"
|
||||||
|
fixed="left"
|
||||||
></ElTableColumn>
|
></ElTableColumn>
|
||||||
)
|
)
|
||||||
} else if (v.type === 'selection') {
|
} else if (v.type === 'selection') {
|
||||||
|
@ -494,6 +500,7 @@ export default defineComponent({
|
||||||
if (getSlot(slots, 'append')) {
|
if (getSlot(slots, 'append')) {
|
||||||
tableSlots['append'] = (...args: any[]) => getSlot(slots, 'append', args)
|
tableSlots['append'] = (...args: any[]) => getSlot(slots, 'append', args)
|
||||||
}
|
}
|
||||||
|
const toolbar = getSlot(slots, 'toolbar')
|
||||||
|
|
||||||
// const { sortable } = unref(getProps)
|
// const { sortable } = unref(getProps)
|
||||||
|
|
||||||
|
@ -511,14 +518,31 @@ export default defineComponent({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div v-loading={unref(getProps).loading}>
|
<div v-loading={unref(getProps).loading}>
|
||||||
|
<div class="flex justify-between mb-1">
|
||||||
|
<div>{toolbar}</div>
|
||||||
|
<div class="pt-2">
|
||||||
{unref(getProps).showAction ? (
|
{unref(getProps).showAction ? (
|
||||||
<TableActions
|
<TableActions
|
||||||
|
activeUID={unref(getProps).activeUID}
|
||||||
columns={unref(getProps).columns}
|
columns={unref(getProps).columns}
|
||||||
|
el-table-ref={elTableRef}
|
||||||
onChangSize={changSize}
|
onChangSize={changSize}
|
||||||
onRefresh={refresh}
|
onRefresh={refresh}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<ElTable ref={elTableRef} data={unref(getProps).data} {...unref(getBindValue)}>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ElTable
|
||||||
|
ref={elTableRef}
|
||||||
|
data={unref(getProps).data}
|
||||||
|
{...unref(getBindValue)}
|
||||||
|
header-cell-style={
|
||||||
|
appStore.getIsDark
|
||||||
|
? { color: '#CFD3DC', 'background-color': '#000' }
|
||||||
|
: { color: '#000', 'background-color': '#f5f7fa' }
|
||||||
|
}
|
||||||
|
>
|
||||||
{{
|
{{
|
||||||
default: () => renderTableColumn(),
|
default: () => renderTableColumn(),
|
||||||
...tableSlots
|
...tableSlots
|
||||||
|
|
|
@ -1,24 +1,34 @@
|
||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import { defineComponent, unref, computed, PropType, watch } from 'vue'
|
import { defineComponent, unref, computed, PropType, watch, ref, nextTick } from 'vue'
|
||||||
import {
|
import {
|
||||||
ElTooltip,
|
ElTooltip,
|
||||||
ElDropdown,
|
ElDropdown,
|
||||||
ElDropdownMenu,
|
ElDropdownMenu,
|
||||||
ElDropdownItem,
|
ElDropdownItem,
|
||||||
ComponentSize
|
ComponentSize,
|
||||||
// ElPopover,
|
ElPopover,
|
||||||
// ElTree
|
ElCheckbox,
|
||||||
|
ElScrollbar,
|
||||||
|
ElButton,
|
||||||
|
ElTable,
|
||||||
|
ElDivider
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import { Icon } from '@/components/Icon'
|
import { Icon } from '@/components/Icon'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { TableColumn } from '../types'
|
import { TableColumn } from '../types'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { VueDraggable } from 'vue-draggable-plus'
|
||||||
// import { eachTree } from '@/utils/tree'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useStorage } from '@/hooks/web/useStorage'
|
||||||
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import { moveElementToIndex } from '@/utils/index'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const sizeMap = computed(() => appStore.sizeMap)
|
const sizeMap = computed(() => appStore.sizeMap)
|
||||||
|
|
||||||
|
const { setStorage, getStorage, removeStorage } = useStorage()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -27,7 +37,13 @@ export default defineComponent({
|
||||||
columns: {
|
columns: {
|
||||||
type: Array as PropType<TableColumn[]>,
|
type: Array as PropType<TableColumn[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
},
|
||||||
|
elTableRef: {
|
||||||
|
type: Object as PropType<ComponentRef<typeof ElTable>>,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
// 表格工具栏缓存唯一标识符
|
||||||
|
activeUID: propTypes.string.def('')
|
||||||
},
|
},
|
||||||
emits: ['refresh', 'changSize'],
|
emits: ['refresh', 'changSize'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
@ -39,25 +55,142 @@ export default defineComponent({
|
||||||
emit('changSize', size)
|
emit('changSize', size)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = computed(() => {
|
const tableColumns = ref(props.columns)
|
||||||
return cloneDeep(props.columns).filter((v) => {
|
const elTableRef = ref(props.elTableRef)
|
||||||
// 去掉type为selection的列和expand的列
|
const activeUID = ref(props.activeUID)
|
||||||
if (v.type !== 'selection' && v.type !== 'expand') {
|
const numberColumnStatus = ref(false)
|
||||||
return v
|
|
||||||
|
// 获取 table columns 是否已经存在序号列,如果存在则更新初始状态,如果不存在则添加
|
||||||
|
const numberColumnField = tableColumns.value.find((item) => item.type === 'index')
|
||||||
|
if (numberColumnField === undefined) {
|
||||||
|
tableColumns.value.unshift({
|
||||||
|
field: '_serial_number',
|
||||||
|
label: '序号',
|
||||||
|
type: 'index',
|
||||||
|
show: false,
|
||||||
|
disabled: true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
numberColumnStatus.value = numberColumnField.show
|
||||||
|
}
|
||||||
|
|
||||||
|
// 备份最初的 table columns
|
||||||
|
const oldTableColumns = cloneDeep(unref(tableColumns))
|
||||||
|
|
||||||
|
const checkAll = ref(false)
|
||||||
|
// 如果为True,则表示为半选状态
|
||||||
|
const isIndeterminate = ref(true)
|
||||||
|
|
||||||
|
// 全选状态改变事件
|
||||||
|
const handleCheckAllChange = (val: boolean) => {
|
||||||
|
tableColumns.value.forEach((item) => {
|
||||||
|
if (item.disabled !== true) {
|
||||||
|
item.show = val
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
isIndeterminate.value = tableColumns.value
|
||||||
|
.filter((item) => !item.disabled)
|
||||||
|
.some((item) => item.show)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列选中状态改变事件
|
||||||
|
const handleCheckChange = () => {
|
||||||
|
checkAll.value = tableColumns.value
|
||||||
|
.filter((item) => !item.disabled)
|
||||||
|
.every((item) => item.show)
|
||||||
|
if (checkAll.value) {
|
||||||
|
isIndeterminate.value = false
|
||||||
|
} else {
|
||||||
|
isIndeterminate.value = tableColumns.value
|
||||||
|
.filter((item) => !item.disabled)
|
||||||
|
.some((item) => item.show)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 table columns 中的序号列状态
|
||||||
|
const updateNumberColumnStatus = (syns: boolean, status: boolean = false) => {
|
||||||
|
const numberColumnField = tableColumns.value.find((item) => item.type === 'index')
|
||||||
|
if (numberColumnField) {
|
||||||
|
if (syns) {
|
||||||
|
numberColumnStatus.value = numberColumnField.show
|
||||||
|
} else {
|
||||||
|
numberColumnField.show = status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { currentRoute } = useRouter()
|
||||||
|
const fullPath = currentRoute.value.fullPath
|
||||||
|
const cacheTableHeadersKey = `${fullPath}_${activeUID.value}`
|
||||||
|
|
||||||
|
if (cacheTableHeadersKey) {
|
||||||
|
// 获取缓存中的 table columns 状态并覆盖到当前的 table columns 中
|
||||||
|
const cacheData = JSON.parse(getStorage(cacheTableHeadersKey))
|
||||||
|
if (cacheData) {
|
||||||
|
tableColumns.value.forEach((item) => {
|
||||||
|
const fieldData = cacheData[item.field]
|
||||||
|
item._index = fieldData.index
|
||||||
|
item.show = fieldData.show
|
||||||
|
item.fixed = fieldData.fixed
|
||||||
})
|
})
|
||||||
|
tableColumns.value.sort((a, b) => a._index - b._index)
|
||||||
|
updateNumberColumnStatus(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => columns.value,
|
() => tableColumns.value,
|
||||||
(newColumns) => {
|
async (val) => {
|
||||||
console.log('columns change:', newColumns)
|
const cacheData = {}
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
const item = val[i]
|
||||||
|
cacheData[item.field] = {
|
||||||
|
show: item.show,
|
||||||
|
index: i,
|
||||||
|
fixed: item.fixed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setStorage(cacheTableHeadersKey, JSON.stringify(cacheData))
|
||||||
|
handleCheckChange()
|
||||||
|
await nextTick()
|
||||||
|
elTableRef.value?.doLayout()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => numberColumnStatus.value,
|
||||||
|
async (val) => {
|
||||||
|
updateNumberColumnStatus(false, val)
|
||||||
|
await nextTick()
|
||||||
|
elTableRef.value?.doLayout()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 重置所有状态
|
||||||
|
const resetTableColumns = async () => {
|
||||||
|
Object.assign(tableColumns.value, cloneDeep(oldTableColumns))
|
||||||
|
updateNumberColumnStatus(true)
|
||||||
|
await nextTick()
|
||||||
|
// 删除缓存
|
||||||
|
removeStorage(cacheTableHeadersKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新元素顺序
|
||||||
|
const updateColumnsIndex = (val) => {
|
||||||
|
Object.assign(
|
||||||
|
tableColumns.value,
|
||||||
|
cloneDeep(moveElementToIndex(tableColumns.value, val.oldIndex, val.newIndex))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCheckChange()
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
<div class="text-right h-28px flex items-center justify-end">
|
<div class="text-right h-28px flex items-center justify-end">
|
||||||
|
@ -71,7 +204,7 @@ export default defineComponent({
|
||||||
</span>
|
</span>
|
||||||
</ElTooltip>
|
</ElTooltip>
|
||||||
|
|
||||||
<ElTooltip content={t('common.size')} placement="top">
|
<ElTooltip content={t('common.density')} placement="top">
|
||||||
<ElDropdown trigger="click" onCommand={changSize}>
|
<ElDropdown trigger="click" onCommand={changSize}>
|
||||||
{{
|
{{
|
||||||
default: () => {
|
default: () => {
|
||||||
|
@ -106,28 +239,76 @@ export default defineComponent({
|
||||||
</ElDropdown>
|
</ElDropdown>
|
||||||
</ElTooltip>
|
</ElTooltip>
|
||||||
|
|
||||||
{/* <ElTooltip content={t('common.columnSetting')} placement="top"> */}
|
<ElTooltip content={t('common.columnSetting')} placement="top">
|
||||||
{/* <ElPopover trigger="click" placement="left">
|
<ElPopover trigger="click" placement="bottom" width="300px">
|
||||||
{{
|
{{
|
||||||
default: () => {
|
default: () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ElTree
|
<div style="border-bottom: 1px solid #d4d7de" class="flex justify-between">
|
||||||
data={unref(columns)}
|
<div>
|
||||||
show-checkbox
|
<ElCheckbox
|
||||||
default-checked-keys={unref(defaultCheckeds)}
|
v-model={checkAll.value}
|
||||||
draggable
|
indeterminate={isIndeterminate.value}
|
||||||
node-key="field"
|
onChange={handleCheckAllChange}
|
||||||
allow-drop={(_draggingNode: any, _dropNode: any, type: string) => {
|
>
|
||||||
if (type === 'inner') {
|
{t('common.selectAll')}
|
||||||
return false
|
</ElCheckbox>
|
||||||
} else {
|
<ElCheckbox v-model={numberColumnStatus.value}>
|
||||||
return true
|
{t('common.SerialNumberColumn')}
|
||||||
}
|
</ElCheckbox>
|
||||||
|
</div>
|
||||||
|
<ElButton type="primary" link onClick={resetTableColumns}>
|
||||||
|
{t('common.reset')}
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<ElScrollbar max-height="400px">
|
||||||
|
<VueDraggable
|
||||||
|
modelValue={tableColumns.value}
|
||||||
|
onEnd={updateColumnsIndex}
|
||||||
|
handle=".cursor-move"
|
||||||
|
>
|
||||||
|
{tableColumns.value.map((element) => {
|
||||||
|
if (element.type === 'index') return null
|
||||||
|
return (
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div>
|
||||||
|
<span class="cursor-move mr-10px">
|
||||||
|
<Icon icon="akar-icons:drag-vertical" />
|
||||||
|
</span>
|
||||||
|
<ElCheckbox
|
||||||
|
v-model={element.show}
|
||||||
|
disabled={element.disabled === true}
|
||||||
|
onChange={handleCheckChange}
|
||||||
|
>
|
||||||
|
{element.label}
|
||||||
|
</ElCheckbox>
|
||||||
|
</div>
|
||||||
|
<div class="mt-7px mr-9px">
|
||||||
|
<span
|
||||||
|
class={element.fixed === 'left' ? 'color-[#409eff]' : ''}
|
||||||
|
onClick={() => {
|
||||||
|
element.fixed = element.fixed === 'left' ? undefined : 'left'
|
||||||
}}
|
}}
|
||||||
onNode-drag-end={onNodeDragEnd}
|
>
|
||||||
onCheck-change={onCheckChange}
|
<Icon icon="radix-icons:pin-left" class="cursor-pointer" />
|
||||||
/>
|
</span>
|
||||||
|
<ElDivider direction="vertical" />
|
||||||
|
<span
|
||||||
|
class={element.fixed === 'right' ? 'color-[#409eff]' : ''}
|
||||||
|
onClick={() => {
|
||||||
|
element.fixed =
|
||||||
|
element.fixed === 'right' ? undefined : 'right'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="radix-icons:pin-right" class="cursor-pointer" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</VueDraggable>
|
||||||
|
</ElScrollbar>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -141,8 +322,8 @@ export default defineComponent({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
</ElPopover> */}
|
</ElPopover>
|
||||||
{/* </ElTooltip> */}
|
</ElTooltip>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,9 +4,9 @@ export interface TableColumn {
|
||||||
label?: string
|
label?: string
|
||||||
type?: string
|
type?: string
|
||||||
/**
|
/**
|
||||||
* 是否隐藏
|
* 是否显示,必填
|
||||||
*/
|
*/
|
||||||
hidden?: boolean
|
show: boolean
|
||||||
children?: TableColumn[]
|
children?: TableColumn[]
|
||||||
slots?: {
|
slots?: {
|
||||||
default?: (...args: any[]) => JSX.Element | JSX.Element[] | null
|
default?: (...args: any[]) => JSX.Element | JSX.Element[] | null
|
||||||
|
|
|
@ -44,11 +44,14 @@ export default {
|
||||||
refresh: 'Refresh',
|
refresh: 'Refresh',
|
||||||
fullscreen: 'Fullscreen',
|
fullscreen: 'Fullscreen',
|
||||||
size: 'Size',
|
size: 'Size',
|
||||||
|
density: 'Density',
|
||||||
columnSetting: 'Column setting',
|
columnSetting: 'Column setting',
|
||||||
lengthRange: 'The length should be between {min} and {max}',
|
lengthRange: 'The length should be between {min} and {max}',
|
||||||
notSpace: 'Spaces are not allowed',
|
notSpace: 'Spaces are not allowed',
|
||||||
notSpecialCharacters: 'Special characters are not allowed',
|
notSpecialCharacters: 'Special characters are not allowed',
|
||||||
isEqual: 'The two are not equal'
|
isEqual: 'The two are not equal',
|
||||||
|
selectAll: 'Select all',
|
||||||
|
SerialNumberColumn: 'Index column'
|
||||||
},
|
},
|
||||||
lock: {
|
lock: {
|
||||||
lockScreen: 'Lock screen',
|
lockScreen: 'Lock screen',
|
||||||
|
@ -108,18 +111,27 @@ export default {
|
||||||
welcome: 'Welcome to the system',
|
welcome: 'Welcome to the system',
|
||||||
message: 'Backstage management system',
|
message: 'Backstage management system',
|
||||||
username: 'Username',
|
username: 'Username',
|
||||||
|
telephone: 'Telephone',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
register: 'Register',
|
register: 'Register',
|
||||||
checkPassword: 'Confirm password',
|
checkPassword: 'Confirm password',
|
||||||
login: 'Sign in',
|
login: 'Sign in',
|
||||||
|
passwordLogin: 'Password login',
|
||||||
|
smsLogin: 'SMS code login',
|
||||||
otherLogin: 'Sign in with',
|
otherLogin: 'Sign in with',
|
||||||
remember: 'Remember me',
|
remember: 'Remember me',
|
||||||
hasUser: 'Existing account? Go to login',
|
hasUser: 'Existing account? Go to login',
|
||||||
forgetPassword: 'Forget password',
|
forgetPassword: 'Forget password',
|
||||||
usernamePlaceholder: 'Please input username',
|
usernamePlaceholder: 'Please input username',
|
||||||
|
telephonePlaceholder: 'Please input telephone',
|
||||||
passwordPlaceholder: 'Please input password',
|
passwordPlaceholder: 'Please input password',
|
||||||
code: 'Verification code',
|
code: 'Verification code',
|
||||||
codePlaceholder: 'Please input verification code'
|
getCode: 'Get code',
|
||||||
|
codePlaceholder: 'Please input verification code',
|
||||||
|
SMSCode: 'sms code',
|
||||||
|
getSMSCode: 'get sms code',
|
||||||
|
SMSCodePlaceholder: 'Please input sms code',
|
||||||
|
SMSCodeRetry: 'S retry'
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
login: 'Login',
|
login: 'Login',
|
||||||
|
@ -442,6 +454,7 @@ export default {
|
||||||
changeTitle: 'Change title',
|
changeTitle: 'Change title',
|
||||||
header: 'Header',
|
header: 'Header',
|
||||||
selectAllNone: 'Select all / none',
|
selectAllNone: 'Select all / none',
|
||||||
|
selectAll: 'Select all',
|
||||||
delOrAddAction: 'Delete or add action',
|
delOrAddAction: 'Delete or add action',
|
||||||
showOrHiddenStripe: 'Show or hidden stripe',
|
showOrHiddenStripe: 'Show or hidden stripe',
|
||||||
showOrHiddenBorder: 'Show or hidden border',
|
showOrHiddenBorder: 'Show or hidden border',
|
||||||
|
|
|
@ -44,11 +44,14 @@ export default {
|
||||||
refresh: '刷新',
|
refresh: '刷新',
|
||||||
fullscreen: '全屏',
|
fullscreen: '全屏',
|
||||||
size: '尺寸',
|
size: '尺寸',
|
||||||
|
density: '密度',
|
||||||
columnSetting: '列设置',
|
columnSetting: '列设置',
|
||||||
lengthRange: '长度在 {min} 到 {max} 个字符',
|
lengthRange: '长度在 {min} 到 {max} 个字符',
|
||||||
notSpace: '不能包含空格',
|
notSpace: '不能包含空格',
|
||||||
notSpecialCharacters: '不能包含特殊字符',
|
notSpecialCharacters: '不能包含特殊字符',
|
||||||
isEqual: '两次输入不一致'
|
isEqual: '两次输入不一致',
|
||||||
|
selectAll: '全选',
|
||||||
|
SerialNumberColumn: '序号列'
|
||||||
},
|
},
|
||||||
lock: {
|
lock: {
|
||||||
lockScreen: '锁定屏幕',
|
lockScreen: '锁定屏幕',
|
||||||
|
@ -107,18 +110,27 @@ export default {
|
||||||
welcome: '欢迎使用本系统',
|
welcome: '欢迎使用本系统',
|
||||||
message: '开箱即用的中后台管理系统',
|
message: '开箱即用的中后台管理系统',
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
|
telephone: '手机号',
|
||||||
password: '密码',
|
password: '密码',
|
||||||
register: '注册',
|
register: '注册',
|
||||||
checkPassword: '确认密码',
|
checkPassword: '确认密码',
|
||||||
login: '登录',
|
login: '登录',
|
||||||
|
passwordLogin: '密码登录',
|
||||||
|
smsLogin: '短信验证码登录',
|
||||||
otherLogin: '其它登录方式',
|
otherLogin: '其它登录方式',
|
||||||
remember: '记住我',
|
remember: '记住我',
|
||||||
hasUser: '已有账号?去登录',
|
hasUser: '已有账号?去登录',
|
||||||
forgetPassword: '忘记密码',
|
forgetPassword: '忘记密码',
|
||||||
usernamePlaceholder: '请输入用户名',
|
usernamePlaceholder: '请输入用户名',
|
||||||
|
telephonePlaceholder: '请输入手机号',
|
||||||
passwordPlaceholder: '请输入密码',
|
passwordPlaceholder: '请输入密码',
|
||||||
code: '验证码',
|
code: '验证码',
|
||||||
codePlaceholder: '请输入验证码'
|
getCode: '获取验证码',
|
||||||
|
codePlaceholder: '请输入验证码',
|
||||||
|
SMSCode: '短信验证码',
|
||||||
|
getSMSCode: '获取短信验证码',
|
||||||
|
SMSCodePlaceholder: '请输入短信验证码',
|
||||||
|
SMSCodeRetry: 'S后重新'
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
login: '登录',
|
login: '登录',
|
||||||
|
@ -435,6 +447,7 @@ export default {
|
||||||
changeTitle: '修改标题',
|
changeTitle: '修改标题',
|
||||||
header: '头部',
|
header: '头部',
|
||||||
selectAllNone: '全选/全不选',
|
selectAllNone: '全选/全不选',
|
||||||
|
selectAll: '全选',
|
||||||
delOrAddAction: '删除/添加操作列',
|
delOrAddAction: '删除/添加操作列',
|
||||||
showOrHiddenStripe: '显示/隐藏斑马纹',
|
showOrHiddenStripe: '显示/隐藏斑马纹',
|
||||||
showOrHiddenBorder: '显示/隐藏边框',
|
showOrHiddenBorder: '显示/隐藏边框',
|
||||||
|
|
|
@ -122,3 +122,145 @@ export function toAnyString() {
|
||||||
export function firstUpperCase(str: string) {
|
export function firstUpperCase(str: string) {
|
||||||
return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
|
return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据当前时间获取祝福语
|
||||||
|
export const getGreeting = (): string => {
|
||||||
|
const now = new Date()
|
||||||
|
const hour = now.getHours()
|
||||||
|
|
||||||
|
if (hour >= 6 && hour < 10) {
|
||||||
|
return '早上好'
|
||||||
|
} else if (hour >= 10 && hour < 13) {
|
||||||
|
return '中午好'
|
||||||
|
} else if (hour >= 13 && hour < 18) {
|
||||||
|
return '下午好'
|
||||||
|
} else {
|
||||||
|
return '晚上好'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前星期几
|
||||||
|
export const getDayOfWeek = (): string => {
|
||||||
|
const daysOfWeek: string[] = [
|
||||||
|
'星期日',
|
||||||
|
'星期一',
|
||||||
|
'星期二',
|
||||||
|
'星期三',
|
||||||
|
'星期四',
|
||||||
|
'星期五',
|
||||||
|
'星期六'
|
||||||
|
]
|
||||||
|
const date: Date = new Date()
|
||||||
|
const dayOfWeekIndex: number = date.getDay()
|
||||||
|
return daysOfWeek[dayOfWeekIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数字转金额
|
||||||
|
// 作者:时光足迹
|
||||||
|
// 链接:https://juejin.cn/post/7028086399601475591
|
||||||
|
// 来源:稀土掘金
|
||||||
|
export const formatMoney = (amount, currency = true): string => {
|
||||||
|
const formatter = new Intl.NumberFormat('zh-CN', {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
useGrouping: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const formattedAmount = formatter.format(amount)
|
||||||
|
|
||||||
|
if (currency) {
|
||||||
|
return `¥${formattedAmount}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小数转折扣
|
||||||
|
* 例子:0.85 -> 8.5折
|
||||||
|
* 例子:0.5 -> 5折
|
||||||
|
*/
|
||||||
|
export const convertToDiscount = (decimal: number | undefined): string => {
|
||||||
|
if (decimal === undefined) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const discount = decimal * 10
|
||||||
|
if (discount === 10) {
|
||||||
|
return '无折扣'
|
||||||
|
}
|
||||||
|
return discount % 1 === 0 ? `${discount}折` : `${discount.toFixed(1)}折`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间
|
||||||
|
* 返回:yyyy-MM-dd HH:mm:ss
|
||||||
|
*/
|
||||||
|
export const getCurrentDateTime = (): string => {
|
||||||
|
const now: Date = new Date()
|
||||||
|
|
||||||
|
const year: number = now.getFullYear()
|
||||||
|
const month: number = now.getMonth() + 1
|
||||||
|
const day: number = now.getDate()
|
||||||
|
const hours: number = now.getHours()
|
||||||
|
const minutes: number = now.getMinutes()
|
||||||
|
const seconds: number = now.getSeconds()
|
||||||
|
|
||||||
|
// 格式化为字符串
|
||||||
|
const formattedDateTime = `${year}-${padZero(month)}-${padZero(day)} ${padZero(hours)}:${padZero(
|
||||||
|
minutes
|
||||||
|
)}:${padZero(seconds)}`
|
||||||
|
|
||||||
|
return formattedDateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前日期
|
||||||
|
* 返回:yyyy-MM-dd HH:mm:ss
|
||||||
|
*/
|
||||||
|
export const getCurrentDate = (): string => {
|
||||||
|
const now: Date = new Date()
|
||||||
|
|
||||||
|
const year: number = now.getFullYear()
|
||||||
|
const month: number = now.getMonth() + 1
|
||||||
|
const day: number = now.getDate()
|
||||||
|
|
||||||
|
// 格式化为字符串
|
||||||
|
const formattedDate = `${year}-${padZero(month)}-${padZero(day)}`
|
||||||
|
|
||||||
|
return formattedDate
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:在数字小于10时,在前面补零
|
||||||
|
export const padZero = (num: number): string => {
|
||||||
|
return num < 10 ? `0${num}` : `${num}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将base64编码的字符串转换为文件
|
||||||
|
export const base64ToFile = (dataURI, filename): File => {
|
||||||
|
const arr = dataURI.split(',')
|
||||||
|
const mime = arr[0].match(/:(.*?);/)[1]
|
||||||
|
const bstr = atob(arr[1])
|
||||||
|
let n = bstr.length
|
||||||
|
const u8arr = new Uint8Array(n)
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n)
|
||||||
|
}
|
||||||
|
return new File([u8arr], filename, { type: mime })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将指定索引的元素移动到目标索引的函数
|
||||||
|
export const moveElementToIndex = (array: any[], fromIndex: number, toIndex: number) => {
|
||||||
|
const clonedArray = [...array] // 克隆数组以避免修改原始数组
|
||||||
|
|
||||||
|
if (
|
||||||
|
fromIndex >= 0 &&
|
||||||
|
fromIndex < clonedArray.length &&
|
||||||
|
toIndex >= 0 &&
|
||||||
|
toIndex < clonedArray.length
|
||||||
|
) {
|
||||||
|
const [element] = clonedArray.splice(fromIndex, 1) // 移除指定索引的元素
|
||||||
|
clonedArray.splice(toIndex, 0, element) // 将元素插入目标索引位置
|
||||||
|
}
|
||||||
|
|
||||||
|
return clonedArray
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue