Browse Source

vue3.0初始化

master
ahbmz 2 years ago
parent
commit
26079858ae
  1. 2
      .env
  2. 2
      .env.staging
  3. 14
      index.html
  4. 970
      package-lock.json
  5. 42
      package.json
  6. 22
      src/App.vue
  7. 17
      src/components/DictData/index.vue
  8. 34
      src/components/ProForm/components/pro-form-item.vue
  9. 4
      src/components/ProForm/util.ts
  10. 155
      src/components/RegionsSelect/index.vue
  11. 139
      src/components/RegionsSelect/util.ts
  12. 2
      src/components/RouterLayout/index.vue
  13. 64
      src/config/use-global-config.ts
  14. 3
      src/i18n/lang/en.ts
  15. 3
      src/i18n/lang/zh_CN.ts
  16. 3
      src/i18n/lang/zh_TW.ts
  17. 82
      src/layout/components/setting-drawer.vue
  18. 24
      src/layout/index.vue
  19. 6
      src/router/index.ts
  20. 38
      src/store/modules/theme.ts
  21. 3
      src/store/modules/user.ts
  22. 11
      src/styles/index.scss
  23. 4
      src/utils/common.ts
  24. 8
      src/views/dashboard/analysis/components/sale-card.vue
  25. 59
      src/views/example/components/demo-component.vue
  26. 72
      src/views/example/components/menu-group.vue
  27. 14
      src/views/extension/bar-code/index.vue
  28. 5
      src/views/extension/editor/index.vue
  29. 2
      src/views/extension/excel/components/excel-export.vue
  30. 1
      src/views/extension/file/components/file-list.vue
  31. 22
      src/views/extension/map/components/demo-picker.vue
  32. 21
      src/views/extension/menu/index.vue
  33. 24
      src/views/extension/message/index.vue
  34. 12
      src/views/extension/modal/components/demo-modal.vue
  35. 10
      src/views/extension/modal/components/multiple-modal.vue
  36. 1
      src/views/extension/player/components/demo-music.vue
  37. 18
      src/views/extension/printer/index.vue
  38. 16
      src/views/extension/regions/index.vue
  39. 16
      src/views/extension/split/components/demo-basic.vue
  40. 16
      src/views/extension/split/components/demo-table.vue
  41. 9
      src/views/extension/steps/index.vue
  42. 114
      src/views/extension/table-select/components/demo-advanced.vue
  43. 10
      src/views/extension/table-select/components/demo-basic-page.vue
  44. 4
      src/views/extension/table-select/components/demo-multiple.vue
  45. 19
      src/views/extension/table/components/child-table.vue
  46. 4
      src/views/extension/table/components/default-sorter.vue
  47. 117
      src/views/extension/table/components/demo-base.vue
  48. 199
      src/views/extension/table/components/demo-selections.vue
  49. 4
      src/views/extension/table/components/expandable-table.vue
  50. 36
      src/views/extension/table/components/nested-table.vue
  51. 75
      src/views/extension/table/components/sortable-table.vue
  52. 14
      src/views/extension/table/components/virtual-base.vue
  53. 6
      src/views/extension/table/index.vue
  54. 42
      src/views/extension/tabs/components/demo-list.vue
  55. 85
      src/views/extension/tabs/index.vue
  56. 42
      src/views/extension/tag/index.vue
  57. 22
      src/views/extension/text/index.vue
  58. 8
      src/views/extension/tree-select/components/demo-basic.vue
  59. 12
      src/views/extension/tree-select/components/demo-multiple.vue
  60. 28
      src/views/extension/tree-select/components/demo-strategy.vue
  61. 6
      src/views/extension/upload/components/demo-advanced.vue
  62. 8
      src/views/extension/upload/components/demo-basic.vue
  63. 2
      src/views/extension/upload/components/demo-customer.vue
  64. 2
      src/views/extension/upload/components/demo-file.vue
  65. 2
      src/views/extension/upload/components/demo-form1.vue
  66. 2
      src/views/extension/upload/components/demo-form2.vue
  67. 4
      src/views/extension/upload/components/demo-multiple.vue
  68. 6
      src/views/form/basic/index.vue
  69. 4
      src/views/form/build/components/code-preview.vue
  70. 2
      src/views/form/build/components/form-preview.vue
  71. 4
      src/views/form/build/components/item-edit.vue
  72. 11
      src/views/form/build/components/util.ts
  73. 2
      src/views/form/build/index.vue
  74. 13
      src/views/form/step/components/step-edit.vue
  75. 2
      src/views/form/step/model/index.ts
  76. 11
      src/views/list/advanced/index.vue
  77. 6
      src/views/list/basic/components/nickname-filter.vue
  78. 73
      src/views/list/basic/index.vue
  79. 54
      src/views/list/card/article/components/img-grid.vue
  80. 93
      src/views/list/card/article/index.vue
  81. 4
      src/views/login/index.vue
  82. 12
      src/views/system/dictionary/components/dict-data-list.vue
  83. 11
      src/views/system/file/index.vue
  84. 14
      src/views/system/login-record/index.vue
  85. 2
      src/views/system/menu/components/icon-select.vue
  86. 12
      src/views/system/menu/components/menu-edit.vue
  87. 7
      src/views/system/menu/index.vue
  88. 78
      src/views/system/operation-record/components/operation-record-detail.vue
  89. 13
      src/views/system/operation-record/index.vue
  90. 20
      src/views/system/organization/components/org-user-edit.vue
  91. 12
      src/views/system/organization/components/org-user-list.vue
  92. 11
      src/views/system/role/index.vue
  93. 11
      src/views/system/user/components/user-edit.vue
  94. 13
      src/views/user/message/components/message-letter.vue
  95. 11
      src/views/user/message/components/message-notice.vue
  96. 11
      src/views/user/message/components/message-todo.vue
  97. 2
      vite.config.ts

2
.env

@ -2,5 +2,3 @@
VITE_LICENSE=dk9mcwJyetRWQlxWRiojIiwiIzVHbQ5Wa6ICdjVmaiV3culWYt9GZiwSMl5yc1xGciojIj5ibp1GZhVGb6ICZpJCLi02brJUbhVlY58kIvl2cyVmdiwiIgISMuEjI6IibQf0NW== VITE_LICENSE=dk9mcwJyetRWQlxWRiojIiwiIzVHbQ5Wa6ICdjVmaiV3culWYt9GZiwSMl5yc1xGciojIj5ibp1GZhVGb6ICZpJCLi02brJUbhVlY58kIvl2cyVmdiwiIgISMuEjI6IibQf0NW==
# 项目名 # 项目名
VITE_APP_NAME=Ele Admin Plus VITE_APP_NAME=Ele Admin Plus
# 接口地址
VITE_API_URL=https://v2.eleadmin.com/api

2
.env.staging

@ -0,0 +1,2 @@
# 预发布环境接口地址
VITE_API_URL=https://v2.eleadmin.com/api

14
index.html

@ -13,16 +13,22 @@
(function () { (function () {
var metaEl = document.createElement('meta'); var metaEl = document.createElement('meta');
metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('name', 'viewport');
if ( var responsive = true;
JSON.parse(localStorage.getItem('theme') || '{}').responsive === false try {
) { responsive = JSON.parse(
localStorage.getItem('theme') || '{}'
).responsive;
} catch (e) {
console.error(e);
}
if (responsive === false) {
metaEl.setAttribute('content', 'width=1200, initial-scale=1.0'); metaEl.setAttribute('content', 'width=1200, initial-scale=1.0');
} else { } else {
metaEl.setAttribute( metaEl.setAttribute(
'content', 'content',
'width=device-width, initial-scale=1.0' 'width=device-width, initial-scale=1.0'
); );
document.documentElement.style.overflow = 'hidden'; document.documentElement.style.overflowX = 'hidden';
} }
document.head.appendChild(metaEl); document.head.appendChild(metaEl);
if (navigator.userAgent.includes('WebKit')) { if (navigator.userAgent.includes('WebKit')) {

970
package-lock.json

File diff suppressed because it is too large

42
package.json

@ -1,13 +1,15 @@
{ {
"name": "ele-admin-plus-ts", "name": "ele-admin-plus-ts",
"version": "1.1.8", "version": "1.1.9",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"serve": "vite build && vite preview --host", "serve": "vite build && vite preview --host",
"build": "vite build", "build": "vite build",
"lint:eslint": "eslint --cache --max-warnings 0 \"src/**/*.{vue,ts}\" --fix", "serve:staging": "vite build --mode staging && vite preview --host",
"build:staging": "vite build --mode staging",
"lint:eslint": "eslint --cache --max-warnings 0 \"src/**/*.{vue,ts,js}\" --fix",
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite/", "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite/",
"clean:lib": "rimraf node_modules" "clean:lib": "rimraf node_modules"
}, },
@ -17,15 +19,15 @@
"@bytemd/plugin-gfm": "^1.21.0", "@bytemd/plugin-gfm": "^1.21.0",
"@bytemd/plugin-highlight": "^1.21.0", "@bytemd/plugin-highlight": "^1.21.0",
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.7", "axios": "^1.6.8",
"bytemd": "^1.21.0", "bytemd": "^1.21.0",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"cropperjs": "^1.6.1", "cropperjs": "^1.6.1",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"ele-admin-plus": "^1.1.8", "ele-admin-plus": "^1.1.9",
"element-plus": "^2.5.6", "element-plus": "^2.7.0",
"github-markdown-css": "^5.5.1", "github-markdown-css": "^5.5.1",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"jsbarcode": "^3.11.6", "jsbarcode": "^3.11.6",
@ -33,37 +35,37 @@
"pinia": "^2.1.7", "pinia": "^2.1.7",
"sortablejs": "^1.15.2", "sortablejs": "^1.15.2",
"tinymce": "^5.10.9", "tinymce": "^5.10.9",
"vue": "^3.4.19", "vue": "^3.4.21",
"vue-echarts": "^6.6.9", "vue-echarts": "^6.6.9",
"vue-i18n": "^9.9.1", "vue-i18n": "^9.12.0",
"vue-router": "^4.3.0", "vue-router": "^4.3.0",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"xgplayer": "^3.0.13", "xgplayer": "^3.0.16",
"xgplayer-hls": "^3.0.13", "xgplayer-hls": "^3.0.16",
"xgplayer-music": "^3.0.13", "xgplayer-music": "^3.0.16",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.20", "@types/node": "^20.12.7",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^7.0.2", "@typescript-eslint/eslint-plugin": "^7.6.0",
"@typescript-eslint/parser": "^7.0.2", "@typescript-eslint/parser": "^7.6.0",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/compiler-sfc": "^3.4.19", "@vue/compiler-sfc": "^3.4.21",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.22.0", "eslint-plugin-vue": "^9.24.1",
"postcss": "^8.4.35", "postcss": "^8.4.38",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"sass": "^1.71.1", "sass": "^1.75.0",
"typescript": "^5.3.3", "typescript": "^5.4.5",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.26.0",
"vite": "^5.1.4", "vite": "^5.2.8",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vue-eslint-parser": "^9.4.2", "vue-eslint-parser": "^9.4.2",
"vue-tsc": "^1.8.27" "vue-tsc": "^2.0.13"
} }
} }

22
src/App.vue

@ -2,8 +2,8 @@
<el-config-provider :locale="elLocale"> <el-config-provider :locale="elLocale">
<ele-config-provider <ele-config-provider
:locale="eleLocale" :locale="eleLocale"
:map-key="MAP_KEY"
:table="tableConfig" :table="tableConfig"
:map-key="MAP_KEY"
:license="LICENSE_CODE" :license="LICENSE_CODE"
> >
<ele-app> <ele-app>
@ -14,24 +14,18 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import type { TableGlobalConfig } from 'ele-admin-plus/es/ele-config-provider/types';
import { useThemeStore } from '@/store/modules/theme';
import { MAP_KEY, LICENSE_CODE } from '@/config/setting'; import { MAP_KEY, LICENSE_CODE } from '@/config/setting';
import { useGlobalConfig } from '@/config/use-global-config';
import { useThemeStore } from '@/store/modules/theme';
import { useLocale } from '@/i18n/use-locale'; import { useLocale } from '@/i18n/use-locale';
// /** 组件全局配置 */
const { tableConfig } = useGlobalConfig();
/** 恢复缓存主题 */
const themeStore = useThemeStore(); const themeStore = useThemeStore();
themeStore.recoverTheme(); themeStore.recoverTheme();
// /** 国际化配置 */
const { elLocale, eleLocale } = useLocale(); const { elLocale, eleLocale } = useLocale();
//
const tableConfig = ref<TableGlobalConfig>({
response: {
dataName: 'list',
countName: 'count'
}
});
</script> </script>

17
src/components/DictData/index.vue

@ -24,10 +24,9 @@
<el-radio <el-radio
v-for="item in data" v-for="item in data"
:key="item.dictDataCode" :key="item.dictDataCode"
:label="item.dictDataCode" :value="item.dictDataCode"
> :label="item.dictDataName"
{{ item.dictDataName }} />
</el-radio>
</el-radio-group> </el-radio-group>
<el-checkbox-group <el-checkbox-group
v-else-if="type === 'checkbox'" v-else-if="type === 'checkbox'"
@ -38,10 +37,9 @@
<el-checkbox <el-checkbox
v-for="item in data" v-for="item in data"
:key="item.dictDataCode" :key="item.dictDataCode"
:label="item.dictDataCode" :value="item.dictDataCode"
> :label="item.dictDataName"
{{ item.dictDataName }} />
</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
<el-select <el-select
v-else v-else
@ -52,6 +50,7 @@
:placeholder="placeholder" :placeholder="placeholder"
:multiple="type === 'multipleSelect'" :multiple="type === 'multipleSelect'"
:teleported="teleported" :teleported="teleported"
:filterable="filterable"
class="ele-fluid" class="ele-fluid"
> >
<el-option <el-option
@ -95,6 +94,8 @@
placeholder?: string; placeholder?: string;
/** select的下拉是否插入到body下 */ /** select的下拉是否插入到body下 */
teleported?: boolean; teleported?: boolean;
/** select是否可搜索 */
filterable?: boolean;
}>(), }>(),
{ {
teleported: true teleported: true

34
src/components/ProForm/components/pro-form-item.vue

@ -110,11 +110,10 @@
<ElRadio <ElRadio
v-for="opt in item.options || []" v-for="opt in item.options || []"
:key="opt.key ?? opt.value" :key="opt.key ?? opt.value"
:label="opt.value" :value="opt.value"
:label="opt.label"
:disabled="opt.disabled" :disabled="opt.disabled"
> />
{{ opt.label }}
</ElRadio>
</ElRadioGroup> </ElRadioGroup>
<ElRadioGroup <ElRadioGroup
v-else-if="item.type === 'radioButton'" v-else-if="item.type === 'radioButton'"
@ -125,11 +124,10 @@
<ElRadioButton <ElRadioButton
v-for="opt in item.options || []" v-for="opt in item.options || []"
:key="opt.key ?? opt.value" :key="opt.key ?? opt.value"
:label="opt.value" :value="opt.value"
:label="opt.label"
:disabled="opt.disabled" :disabled="opt.disabled"
> />
{{ opt.label }}
</ElRadioButton>
</ElRadioGroup> </ElRadioGroup>
<ElCheckboxGroup <ElCheckboxGroup
v-else-if="item.type === 'checkbox'" v-else-if="item.type === 'checkbox'"
@ -140,11 +138,10 @@
<ElCheckbox <ElCheckbox
v-for="opt in item.options || []" v-for="opt in item.options || []"
:key="opt.key ?? opt.value" :key="opt.key ?? opt.value"
:label="opt.value" :value="opt.value"
:label="opt.label"
:disabled="opt.disabled" :disabled="opt.disabled"
> />
{{ opt.label }}
</ElCheckbox>
</ElCheckboxGroup> </ElCheckboxGroup>
<ElCheckboxGroup <ElCheckboxGroup
v-else-if="item.type === 'checkboxButton'" v-else-if="item.type === 'checkboxButton'"
@ -155,11 +152,10 @@
<ElCheckboxButton <ElCheckboxButton
v-for="opt in item.options || []" v-for="opt in item.options || []"
:key="opt.key ?? opt.value" :key="opt.key ?? opt.value"
:label="opt.value" :value="opt.value"
:label="opt.label"
:disabled="opt.disabled" :disabled="opt.disabled"
> />
{{ opt.label }}
</ElCheckboxButton>
</ElCheckboxGroup> </ElCheckboxGroup>
<ElDatePicker <ElDatePicker
v-else-if="item.type === 'date'" v-else-if="item.type === 'date'"
@ -724,7 +720,7 @@
import FileUpload from '@/components/FileUpload/index.vue'; import FileUpload from '@/components/FileUpload/index.vue';
import { import {
uploadTypes, uploadTypes,
getRuleType, //getRuleType,
getRuleTrigger, getRuleTrigger,
getRuleMessage getRuleMessage
} from '../util'; } from '../util';
@ -777,14 +773,14 @@
const rRule: FormItemRule = { const rRule: FormItemRule = {
required: true, required: true,
message: getRuleMessage(props.item.type, props.item.label), message: getRuleMessage(props.item.type, props.item.label),
type: getRuleType(props.item.type), //type: getRuleType(props.item.type),
trigger: getRuleTrigger(props.item.type) trigger: getRuleTrigger(props.item.type)
}; };
rules.unshift(rRule); rules.unshift(rRule);
} }
if (props.item.type && uploadTypes.includes(props.item.type)) { if (props.item.type && uploadTypes.includes(props.item.type)) {
const uRule: FormItemRule = { const uRule: FormItemRule = {
type: getRuleType(props.item.type), //type: getRuleType(props.item.type),
trigger: getRuleTrigger(props.item.type), trigger: getRuleTrigger(props.item.type),
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string, callback: any) => {
if (value && !uploadIsDone()) { if (value && !uploadIsDone()) {

4
src/components/ProForm/util.ts

@ -101,7 +101,7 @@ export const uploadTypes = ['imageUpload', 'fileUpload'];
* *
* @param type * @param type
*/ */
export function getRuleType(type?: string) { /* export function getRuleType(type?: string) {
if (type) { if (type) {
if (arrayTypes.includes(type)) { if (arrayTypes.includes(type)) {
return 'array'; return 'array';
@ -111,7 +111,7 @@ export function getRuleType(type?: string) {
} }
} }
return 'string'; return 'string';
} } */
/** /**
* *

155
src/components/RegionsSelect/index.vue

@ -1,11 +1,27 @@
<!-- 省市区级联选择 --> <!-- 省市区级联选择 -->
<template> <template>
<template v-if="component === 'text'">
<span v-if="typeof valueLabels === 'string'">{{ valueLabels }}</span>
<el-space v-else :wrap="true">
<el-tag
v-for="label in valueLabels"
:key="label"
:disable-transitions="true"
size="small"
type="info"
>
{{ label }}
</el-tag>
</el-space>
</template>
<el-cascader <el-cascader
v-else
ref="cascaderRef"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
:clearable="clearable" :clearable="clearable"
:model-value="modelValue" :model-value="modelValue"
:options="regionsData" :options="cascaderData"
:filterable="filterable" :filterable="filterable"
:placeholder="placeholder" :placeholder="placeholder"
:props="cascaderProps" :props="cascaderProps"
@ -19,22 +35,32 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch } from 'vue'; import { ref, computed } from 'vue';
import type { CascaderProps } from 'element-plus'; import type {
ElCascaderProps,
ElCascaderInstance
} from 'ele-admin-plus/es/ele-app/el';
import type { RegionsData } from './util'; import type { RegionsData } from './util';
import { getRegionsData } from './util'; import { useRegionsData, filterData, getValueLabel } from './util';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
modelValue?: string[]; /** 选中值 */
placeholder?: string; modelValue?: string[] | Array<string[]>;
/** 自定义省市区数据 */
options?: RegionsData[]; options?: RegionsData[];
valueField?: 'label'; /** 选中值对应的字段名 */
valueField?: 'value' | 'label';
/** 类型, 省市选择或省选择 */
type?: 'provinceCity' | 'province'; type?: 'provinceCity' | 'province';
/** 组件类型 */
component?: 'text' | 'cascader';
/** 级联选择器属性 */
placeholder?: string;
disabled?: boolean; disabled?: boolean;
clearable?: boolean; clearable?: boolean;
filterable?: boolean; filterable?: boolean;
cascaderProps?: CascaderProps; cascaderProps?: ElCascaderProps;
collapseTags?: boolean; collapseTags?: boolean;
maxCollapseTags?: number; maxCollapseTags?: number;
collapseTagsTooltip?: boolean; collapseTagsTooltip?: boolean;
@ -52,94 +78,45 @@
(e: 'update:modelValue', value?: string[]): void; (e: 'update:modelValue', value?: string[]): void;
}>(); }>();
/** 级联选择器数据 */ /** 省市区数据 */
const regionsData = ref<RegionsData[]>([]); const regionsData = useRegionsData();
/** 更新value */ /** 级联选择器实例 */
const updateValue = (value: string[]) => { const cascaderRef = ref<ElCascaderInstance>(null);
emit('update:modelValue', value);
}; /** 级联选择器数据 */
const cascaderData = computed<any>(() => {
const data = props.options ?? regionsData.value ?? [];
return filterData(data, props.type, props.valueField);
});
/** 级联选择器数据value处理 */ /** 选中值对应的文本 */
const formatData = (data: RegionsData[]) => { const valueLabels = computed<string | string[]>(() => {
if (props.valueField === 'label') { const separator = ' / ';
return data.map((d) => { const values = props.modelValue;
const item: RegionsData = { if (values && values.length && Array.isArray(values[0])) {
label: d.label, const result: string[] = [];
value: d.label (values as Array<string[]>).forEach((v) => {
}; const labels = getValueLabel(v, cascaderData.value);
if (d.children) { result.push(labels.join(separator));
item.children = d.children.map((c) => {
const cItem: RegionsData = {
label: c.label,
value: c.label
};
if (c.children) {
cItem.children = c.children.map((cc) => {
return {
label: cc.label,
value: cc.label
};
});
}
return cItem;
});
}
return item;
}); });
} else { return result;
return data;
} }
}; const labels = getValueLabel(values as string[], cascaderData.value);
return labels.join(separator);
});
/** 省市区数据筛选 */ /** 更新modelValue */
const filterData = (data: RegionsData[]) => { const updateValue = (value: string[]) => {
if (props.type === 'provinceCity') { emit('update:modelValue', value);
return formatData(
data.map((d) => {
const item: RegionsData = {
label: d.label,
value: d.value
};
if (d.children) {
item.children = d.children.map((c) => {
return {
label: c.label,
value: c.value
};
});
}
return item;
})
);
} else if (props.type === 'province') {
return formatData(
data.map((d) => {
return {
label: d.label,
value: d.value
};
})
);
} else {
return formatData(data);
}
}; };
watch( defineExpose({
() => props.options, cascaderRef,
(options) => { getCheckedNodes: (leafOnly?: boolean) => {
regionsData.value = filterData(options ?? []); return cascaderRef.value?.getCheckedNodes?.(!!leafOnly);
if (!options) {
getRegionsData().then((data) => {
regionsData.value = filterData(data ?? []);
});
}
},
{
immediate: true
} }
); });
</script> </script>
<script lang="ts"> <script lang="ts">

139
src/components/RegionsSelect/util.ts

@ -1,6 +1,7 @@
import { defineStore, storeToRefs } from 'pinia';
import { EleMessage } from 'ele-admin-plus/es';
import request from '@/utils/request'; import request from '@/utils/request';
const BASE_URL = import.meta.env.BASE_URL; const BASE_URL = import.meta.env.BASE_URL;
let reqPromise: Promise<RegionsData[]>;
/** /**
* *
@ -19,22 +20,132 @@ export interface RegionsData {
} }
/** /**
*
*/
export const useRegionsStore = defineStore('regions', {
state: () => ({
regionsData: null as RegionsData[] | null
}),
actions: {
setRegionsData(data: RegionsData[]) {
this.regionsData = data;
}
}
});
/**
* hook
*/
export function useRegionsData() {
const regionsStore = useRegionsStore();
const { regionsData } = storeToRefs(regionsStore);
// 若还未缓存过则获取省市区数据
if (regionsData.value == null) {
regionsStore.setRegionsData([]);
getRegionsData()
.then((data) => {
regionsStore.setRegionsData(data);
})
.catch((e) => {
EleMessage.error(e.message);
});
}
return regionsData;
}
/**
* *
*/ */
export function getRegionsData() { export async function getRegionsData() {
if (!reqPromise) { const res = await request.get<RegionsData[]>(
reqPromise = new Promise<RegionsData[]>((resolve, reject) => { BASE_URL + 'json/regions-data.json',
request { baseURL: '' }
.get<RegionsData[]>(BASE_URL + 'json/regions-data.json', { );
baseURL: '' return res.data ?? [];
}) }
.then((res) => {
resolve(res.data ?? []); /**
}) * value处理
.catch((e) => { * @param data
reject(e); * @param field value字段名
*/
export function formatData(data: RegionsData[], field?: string) {
if (field === 'label') {
return data.map((d) => {
const item: RegionsData = { label: d.label, value: d.label };
if (d.children) {
item.children = d.children.map((c) => {
const cItem: RegionsData = { label: c.label, value: c.label };
if (c.children) {
cItem.children = c.children.map((cc) => {
return { label: cc.label, value: cc.label };
});
}
return cItem;
}); });
}
return item;
});
}
return data;
}
/**
*
* @param data
* @param type
* @param field value字段名
*/
export function filterData(data: RegionsData[], type?: string, field?: string) {
if (type === 'provinceCity') {
return formatData(
data.map((d) => {
const item: RegionsData = { label: d.label, value: d.value };
if (d.children) {
item.children = d.children.map((c) => {
return { label: c.label, value: c.value };
});
}
return item;
}),
field
);
} else if (type === 'province') {
return formatData(
data.map((d) => ({ label: d.label, value: d.value })),
field
);
}
return formatData(data, field);
}
/**
*
* @param values
* @param data
*/
export function getValueLabel(values?: string[], data?: RegionsData[]) {
const labels: string[] = [];
if (values && values.length && data) {
data.forEach((d) => {
if (d.value === values[0]) {
labels.push(d.label);
if (d.children && d.children.length) {
d.children.forEach((c) => {
if (c.value === values[1]) {
labels.push(c.label);
}
if (c.children && c.children.length) {
c.children.forEach((cc) => {
if (cc.value === values[2]) {
labels.push(cc.label);
}
});
}
});
}
}
}); });
} }
return reqPromise; return labels;
} }

2
src/components/RouterLayout/index.vue

@ -9,7 +9,7 @@
> >
<component :key="route.path" :is="Component" /> <component :key="route.path" :is="Component" />
</keep-alive> </keep-alive>
<component v-else :is="Component" /> <component v-else :key="route.path" :is="Component" />
</transition> </transition>
</router-view> </router-view>
</template> </template>

64
src/config/use-global-config.ts

@ -0,0 +1,64 @@
import { ref } from 'vue';
import type { TableGlobalConfig } from 'ele-admin-plus/es/ele-config-provider/types';
import { utils, writeFile } from 'xlsx';
import type { ColInfo, Range } from 'xlsx';
/**
*
*/
export function useGlobalConfig() {
/** 高级表格全局配置 */
const tableConfig = ref<TableGlobalConfig>({
response: {
dataName: 'list',
countName: 'count'
},
tools: ['reload', 'export', 'print', 'size', 'columns', 'maximized'],
exportConfig: {
// 使用 xlsx 进行导出
beforeExport: ({
headerData,
bodyData,
footerData,
bodyCols,
fileName,
closeModal
}) => {
const sheetRows: string[][] = [];
const sheetMerges: Range[] = [];
const sheetSizes: ColInfo[] = [];
[...headerData, ...bodyData, ...footerData].forEach((item, index) => {
const sheetCols: string[] = [];
item.forEach((d, colIndex) => {
sheetCols.push(d.text ?? '');
if ((d.rowspan && d.rowspan > 1) || (d.colspan && d.colspan > 1)) {
sheetMerges.push({
s: { r: index, c: colIndex },
e: {
r: index + (d.rowspan || 1) - 1,
c: colIndex + (d.colspan || 1) - 1
}
});
}
});
sheetRows.push(sheetCols);
});
bodyCols.forEach((col) => {
const width = col.width ?? col.minWidth;
sheetSizes.push({ wch: width == null ? void 0 : width / 8 });
});
const sheet = utils.aoa_to_sheet(sheetRows);
sheet['!merges'] = sheetMerges;
sheet['!cols'] = sheetSizes;
writeFile(
{ SheetNames: ['Sheet1'], Sheets: { Sheet1: sheet } },
`${fileName}.xlsx`
);
closeModal();
return false;
}
}
});
return { tableConfig };
}

3
src/i18n/lang/en.ts

@ -69,12 +69,15 @@ export const en: Locale = {
weakMode: 'Weak Mode', weakMode: 'Weak Mode',
showTabs: 'Show Tabs', showTabs: 'Show Tabs',
fixedHome: 'Fixed Home Tab', fixedHome: 'Fixed Home Tab',
tabInHeader: 'Tab In Header',
tabStyle: 'Tab Style', tabStyle: 'Tab Style',
tabStyles: { tabStyles: {
default: 'Default', default: 'Default',
dot: 'Dot', dot: 'Dot',
tag: 'Tag',
card: 'Card' card: 'Card'
}, },
menuItemTrigger: 'Menu Division',
transitionName: 'Transition', transitionName: 'Transition',
transitions: { transitions: {
slideRight: 'Slide Right', slideRight: 'Slide Right',

3
src/i18n/lang/zh_CN.ts

@ -67,12 +67,15 @@ export const zh_CN = {
weakMode: '开启色弱模式', weakMode: '开启色弱模式',
showTabs: '开启多页签栏', showTabs: '开启多页签栏',
fixedHome: '固定主页页签', fixedHome: '固定主页页签',
tabInHeader: '页签置于顶栏',
tabStyle: '页签显示风格', tabStyle: '页签显示风格',
tabStyles: { tabStyles: {
default: '默认', default: '默认',
dot: '圆点', dot: '圆点',
tag: '标签',
card: '卡片' card: '卡片'
}, },
menuItemTrigger: '混合菜单分割',
transitionName: '路由切换动画', transitionName: '路由切换动画',
transitions: { transitions: {
slideRight: '滑动消退', slideRight: '滑动消退',

3
src/i18n/lang/zh_TW.ts

@ -69,12 +69,15 @@ export const zh_TW: Locale = {
weakMode: '開啟色弱模式', weakMode: '開啟色弱模式',
showTabs: '開啟多頁簽欄', showTabs: '開啟多頁簽欄',
fixedHome: '固定主頁頁簽', fixedHome: '固定主頁頁簽',
tabInHeader: '頁簽置於頂欄',
tabStyle: '頁簽顯示風格', tabStyle: '頁簽顯示風格',
tabStyles: { tabStyles: {
default: '默認', default: '默認',
dot: '圓點', dot: '圓點',
tag: '标签',
card: '卡片' card: '卡片'
}, },
menuItemTrigger: '混合菜單分割',
transitionName: '路由切換動畫', transitionName: '路由切換動畫',
transitions: { transitions: {
slideRight: '滑動消退', slideRight: '滑動消退',

82
src/layout/components/setting-drawer.vue

@ -361,6 +361,20 @@
/> />
</div> </div>
</div> </div>
<!-- 页签置于顶栏 -->
<div class="setting-item">
<div class="setting-item-title">
{{ t('layout.setting.tabInHeader') }}
</div>
<div class="setting-item-control">
<el-switch
size="small"
:disabled="!tabBar"
:model-value="tabInHeader"
@update:modelValue="updateTabInHeader"
/>
</div>
</div>
<!-- 页签风格 --> <!-- 页签风格 -->
<div class="setting-item"> <div class="setting-item">
<div class="setting-item-title">{{ t('layout.setting.tabStyle') }}</div> <div class="setting-item-title">{{ t('layout.setting.tabStyle') }}</div>
@ -379,6 +393,7 @@
value="indicator" value="indicator"
:label="t('layout.setting.tabStyles.dot')" :label="t('layout.setting.tabStyles.dot')"
/> />
<el-option value="tag" :label="t('layout.setting.tabStyles.tag')" />
<el-option <el-option
value="button" value="button"
:label="t('layout.setting.tabStyles.card')" :label="t('layout.setting.tabStyles.card')"
@ -386,6 +401,24 @@
</el-select> </el-select>
</div> </div>
</div> </div>
<!-- 混合菜单分割 -->
<div class="setting-item">
<div class="setting-item-title">
{{ t('layout.setting.menuItemTrigger') }}
</div>
<div class="setting-item-control" style="width: 100px">
<el-select
size="small"
:disabled="sidebarLayout !== 'mix' && layout !== 'mix'"
:model-value="menuItemTrigger"
@update:modelValue="updateMenuItemTrigger"
>
<el-option value="route" label="RouterLink" />
<el-option value="click" label="Click" />
<el-option value="hover" label="Hover" />
</el-select>
</div>
</div>
<!-- 切换动画 --> <!-- 切换动画 -->
<div class="setting-item"> <div class="setting-item">
<div class="setting-item-title"> <div class="setting-item-title">
@ -439,7 +472,8 @@
Layout, Layout,
HeaderStyle, HeaderStyle,
SidebarStyle, SidebarStyle,
TabStyle TabStyle,
MenuItemTrigger
} from 'ele-admin-plus/es/ele-pro-layout/types'; } from 'ele-admin-plus/es/ele-pro-layout/types';
import { CheckOutlined } from '@/components/icons'; import { CheckOutlined } from '@/components/icons';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
@ -481,6 +515,8 @@
colorfulIcon, colorfulIcon,
uniqueOpened, uniqueOpened,
fixedHome, fixedHome,
tabInHeader,
menuItemTrigger,
weakMode, weakMode,
darkMode, darkMode,
color, color,
@ -490,34 +526,13 @@
/** 预设主题颜色 */ /** 预设主题颜色 */
const themes = ref<ThemeItem[]>([ const themes = ref<ThemeItem[]>([
{ { name: 'default', color: '#1677ff' },
name: 'default', { name: 'dust', value: '#5f80c7' },
color: '#1677ff' { name: 'sunset', value: '#faad14' },
}, { name: 'volcano', value: '#f5686f' },
{ { name: 'purple', value: '#9266f9' },
name: 'dust', { name: 'green', value: '#33cc99' },
value: '#5f80c7' { name: 'geekblue', value: '#32a2d4' }
},
{
name: 'sunset',
value: '#faad14'
},
{
name: 'volcano',
value: '#f5686f'
},
{
name: 'purple',
value: '#9266f9'
},
{
name: 'green',
value: '#33cc99'
},
{
name: 'geekblue',
value: '#32a2d4'
}
]); ]);
/** 颜色选择器预设颜色 */ /** 颜色选择器预设颜色 */
@ -599,7 +614,10 @@
const updateFixedHome = (value: boolean) => { const updateFixedHome = (value: boolean) => {
themeStore.setFixedHome(value); themeStore.setFixedHome(value);
location.reload(); };
const updateTabInHeader = (value: boolean) => {
themeStore.setTabInHeader(value);
}; };
const updateResponsive = (value: boolean) => { const updateResponsive = (value: boolean) => {
@ -619,6 +637,10 @@
themeStore.setTransitionName(value); themeStore.setTransitionName(value);
}; };
const updateMenuItemTrigger = (value: MenuItemTrigger) => {
themeStore.setMenuItemTrigger(value);
};
const updateDarkMode = (isDark: boolean) => { const updateDarkMode = (isDark: boolean) => {
if ( if (
!darkSwitchRef.value || !darkSwitchRef.value ||

24
src/layout/index.vue

@ -5,8 +5,8 @@
:collapse="collapse" :collapse="collapse"
:compact="compact" :compact="compact"
:maximized="maximized" :maximized="maximized"
:tab-bar="tabBar" :tab-bar="tabBar ? (tabInHeader ? 'header' : true) : false"
:breadcrumb="layout === 'side'" :breadcrumb="layout === 'side' && (!tabBar || !tabInHeader)"
:layout="layout" :layout="layout"
:sidebar-layout="sidebarLayout" :sidebar-layout="sidebarLayout"
:header-style="headerStyle" :header-style="headerStyle"
@ -27,9 +27,8 @@
:tab-sortable="!mobileDevice" :tab-sortable="!mobileDevice"
:tab-context-menu="mobileDevice ? false : { iconProps: { size: 15 } }" :tab-context-menu="mobileDevice ? false : { iconProps: { size: 15 } }"
:tab-context-menus="tabContext" :tab-context-menus="tabContext"
:nav-trigger="navTrigger" :nav-trigger="layout === 'top' ? void 0 : menuItemTrigger"
:box-trigger="boxTrigger" :box-trigger="menuItemTrigger"
:item-trigger="itemTrigger"
:keep-alive="TAB_KEEP_ALIVE" :keep-alive="TAB_KEEP_ALIVE"
:transition-name="transitionName" :transition-name="transitionName"
:ellipsis-props="{ hideTimeout: 800 }" :ellipsis-props="{ hideTimeout: 800 }"
@ -46,7 +45,7 @@
<router-layout /> <router-layout />
<!-- logo --> <!-- logo -->
<template #logo> <template #logo>
<img src="/src/assets/logo.svg" style="width: 30px; height: 30px" /> <img src="@/assets/logo.svg" style="width: 30px; height: 30px" />
<h1 style="font-size: 17px; letter-spacing: 1px">{{ PROJECT_NAME }}</h1> <h1 style="font-size: 17px; letter-spacing: 1px">{{ PROJECT_NAME }}</h1>
</template> </template>
<!-- 顶栏左侧按钮 --> <!-- 顶栏左侧按钮 -->
@ -60,7 +59,7 @@
</header-tool> </header-tool>
<!-- 刷新 --> <!-- 刷新 -->
<header-tool <header-tool
v-if="layout === 'side'" v-if="layout === 'side' && (!tabBar || !tabInHeader)"
class="hidden-sm-and-down" class="hidden-sm-and-down"
@click="reloadPageTab()" @click="reloadPageTab()"
> >
@ -79,11 +78,11 @@
</el-icon> </el-icon>
</header-tool> </header-tool>
<!-- 语言切换 --> <!-- 语言切换 -->
<header-tool> <header-tool :class="{ 'hidden-sm-and-down': tabBar && tabInHeader }">
<i18n-icon :icon-style="{ transform: 'scale(1.15)' }" /> <i18n-icon :icon-style="{ transform: 'scale(1.15)' }" />
</header-tool> </header-tool>
<!-- 消息通知 --> <!-- 消息通知 -->
<header-tool> <header-tool :class="{ 'hidden-sm-and-down': tabBar && tabInHeader }">
<header-notice /> <header-notice />
</header-tool> </header-tool>
<!-- 用户信息 --> <!-- 用户信息 -->
@ -98,7 +97,7 @@
</header-tool> </header-tool>
</template> </template>
<!-- 页签栏右侧下拉菜单 --> <!-- 页签栏右侧下拉菜单 -->
<template #tabExtra="{ active }"> <template v-if="tabBar && !tabInHeader" #tabExtra="{ active }">
<tab-dropdown <tab-dropdown
:items="tabExtra" :items="tabExtra"
:dropdown-props="{ iconProps: { size: 15 } }" :dropdown-props="{ iconProps: { size: 15 } }"
@ -232,10 +231,9 @@
transitionName, transitionName,
uniqueOpened, uniqueOpened,
fixedHome, fixedHome,
tabInHeader,
roundedTheme, roundedTheme,
navTrigger, menuItemTrigger,
boxTrigger,
itemTrigger,
responsive responsive
} = storeToRefs(themeStore); } = storeToRefs(themeStore);

6
src/router/index.ts

@ -37,10 +37,8 @@ router.beforeEach(async (to) => {
if (!getToken()) { if (!getToken()) {
// 未登录跳转登录界面 // 未登录跳转登录界面
if (!WHITE_LIST.includes(to.path)) { if (!WHITE_LIST.includes(to.path)) {
return { const query = { from: encodeURIComponent(to.fullPath) };
path: '/login', return { path: '/login', query: to.path === LAYOUT_PATH ? {} : query };
query: to.path === LAYOUT_PATH ? {} : { from: to.path }
};
} }
return; return;
} }

38
src/store/modules/theme.ts

@ -55,6 +55,8 @@ const DEFAULT_STATE = Object.freeze<ThemeState>({
uniqueOpened: true, uniqueOpened: true,
/** 固定主页页签 */ /** 固定主页页签 */
fixedHome: true, fixedHome: true,
/** 页签是否置于顶栏 */
tabInHeader: false,
/** 路由切换动画 */ /** 路由切换动画 */
transitionName: 'slide-right', transitionName: 'slide-right',
/** 是否色弱模式 */ /** 是否色弱模式 */
@ -67,12 +69,8 @@ const DEFAULT_STATE = Object.freeze<ThemeState>({
contentWidth: null, contentWidth: null,
/** 是否开启圆角主题 */ /** 是否开启圆角主题 */
roundedTheme: true, roundedTheme: true,
/** 顶栏菜单触发模式 */ /** 菜单触发模式 */
navTrigger: 'route', menuItemTrigger: 'route',
/** 双侧栏一级菜单触发模式 */
boxTrigger: 'route',
/** 侧栏菜单触发模式 */
itemTrigger: 'route',
/** 是否开启响应式 */ /** 是否开启响应式 */
responsive: true responsive: true
}); });
@ -157,8 +155,7 @@ function changeTheme(value?: string | null, dark?: boolean) {
}); });
} }
export const useThemeStore = defineStore({ export const useThemeStore = defineStore('theme', {
id: 'theme',
state: (): ThemeState => { state: (): ThemeState => {
const state = { ...DEFAULT_STATE }; const state = { ...DEFAULT_STATE };
// 读取本地缓存 // 读取本地缓存
@ -259,6 +256,10 @@ export const useThemeStore = defineStore({
this.fixedHome = value; this.fixedHome = value;
cacheSetting('fixedHome', value); cacheSetting('fixedHome', value);
}, },
setTabInHeader(value: boolean) {
this.tabInHeader = value;
cacheSetting('tabInHeader', value);
},
setTransitionName(value: string) { setTransitionName(value: string) {
this.transitionName = value; this.transitionName = value;
cacheSetting('transitionName', value); cacheSetting('transitionName', value);
@ -351,7 +352,10 @@ export const useThemeStore = defineStore({
return {}; return {};
} }
const t = this.tabs[i]; const t = this.tabs[i];
if (!t.closable || (t.home && this.tabs.length === 1)) { if (
!t.closable ||
(t.home && (this.tabs.length === 1 || this.fixedHome))
) {
return Promise.reject(); return Promise.reject();
} }
const path = this.tabs[i + (i === 0 ? 1 : -1)]?.fullPath; const path = this.tabs[i + (i === 0 ? 1 : -1)]?.fullPath;
@ -487,14 +491,9 @@ export const useThemeStore = defineStore({
this.setTabs(temps); this.setTabs(temps);
}, },
/** 修改菜单触发模式 */ /** 修改菜单触发模式 */
setMenuItemTrigger(key: string, trigger: MenuItemTrigger) { setMenuItemTrigger(value: MenuItemTrigger) {
if (key === 'navTrigger') { this.menuItemTrigger = value;
this.navTrigger = trigger; cacheSetting('menuItemTrigger', value);
} else if (key === 'boxTrigger') {
this.boxTrigger = trigger;
} else if (key === 'itemTrigger') {
this.itemTrigger = trigger;
}
}, },
/** 切换圆角主题 */ /** 切换圆角主题 */
setRoundedTheme(value: boolean) { setRoundedTheme(value: boolean) {
@ -533,14 +532,13 @@ export interface ThemeState {
colorfulIcon: boolean; colorfulIcon: boolean;
uniqueOpened: boolean; uniqueOpened: boolean;
fixedHome: boolean; fixedHome: boolean;
tabInHeader: boolean;
transitionName: string; transitionName: string;
weakMode: boolean; weakMode: boolean;
darkMode: boolean; darkMode: boolean;
color: string | null; color: string | null;
contentWidth: number | null; contentWidth: number | null;
navTrigger: MenuItemTrigger; menuItemTrigger: MenuItemTrigger;
boxTrigger: MenuItemTrigger;
itemTrigger: MenuItemTrigger;
roundedTheme: boolean; roundedTheme: boolean;
responsive: boolean; responsive: boolean;
} }

3
src/store/modules/user.ts

@ -20,8 +20,7 @@ export interface UserState {
dicts: Record<string, DictionaryData[]>; dicts: Record<string, DictionaryData[]>;
} }
export const useUserStore = defineStore({ export const useUserStore = defineStore('user', {
id: 'user',
state: (): UserState => ({ state: (): UserState => ({
/** 当前登录用户的信息 */ /** 当前登录用户的信息 */
info: null, info: null,

11
src/styles/index.scss

@ -35,16 +35,21 @@ body.ele-body-limited {
/* 按钮加图标减少间距 */ /* 按钮加图标减少间距 */
.ele-btn-icon.el-button, .ele-btn-icon.el-button,
.ele-btn-icon.el-button.is-round { .ele-btn-icon.el-button.is-round {
padding-left: 10px; padding-left: 12px;
padding-right: 12px; padding-right: 12px;
& > .el-icon {
margin-left: -2px;
margin-right: -2px;
}
&.el-button--small { &.el-button--small {
padding-left: 5px; padding-left: 6px;
padding-right: 6px; padding-right: 6px;
} }
&.el-button--large { &.el-button--large {
padding-left: 14px; padding-left: 16px;
padding-right: 16px; padding-right: 16px;
} }
} }

4
src/utils/common.ts

@ -16,9 +16,9 @@ export function logout(route?: boolean, from?: string, push?: Router['push']) {
}); });
return; return;
} }
// 这样跳转避免再次登录重复注册动态路由 // 这样跳转避免再次登录重复注册动态路由, hash 路由模式使用 location.reload();
const BASE_URL = import.meta.env.BASE_URL; const BASE_URL = import.meta.env.BASE_URL;
const url = BASE_URL + 'login'; // hash 路由模式使用 '#/login' const url = BASE_URL + 'login';
location.replace(from ? `${url}?from=${encodeURIComponent(from)}` : url); location.replace(from ? `${url}?from=${encodeURIComponent(from)}` : url);
} }

8
src/views/dashboard/analysis/components/sale-card.vue

@ -18,10 +18,10 @@
<template #extra> <template #extra>
<div class="hidden-xs-only" style="display: flex; align-items: center"> <div class="hidden-xs-only" style="display: flex; align-items: center">
<el-radio-group v-model="saleSearch.dateType"> <el-radio-group v-model="saleSearch.dateType">
<el-radio-button label="1">今天</el-radio-button> <el-radio-button value="1" label="今天" />
<el-radio-button label="2">本周</el-radio-button> <el-radio-button value="2" label="本周" />
<el-radio-button label="3">本月</el-radio-button> <el-radio-button value="3" label="本月" />
<el-radio-button label="4">本年</el-radio-button> <el-radio-button value="4" label="本年" />
</el-radio-group> </el-radio-group>
<div class="hidden-md-and-down" style="width: 320px; margin-left: 12px"> <div class="hidden-md-and-down" style="width: 320px; margin-left: 12px">
<el-date-picker <el-date-picker

59
src/views/example/components/demo-component.vue

@ -44,6 +44,14 @@
class="ele-fluid" class="ele-fluid"
/> />
</option-item> </option-item>
<option-item label="年多选" label-width="90px">
<el-date-picker
type="years"
v-model="value16"
placeholder="Pick one or more years"
class="ele-fluid"
/>
</option-item>
<option-item label="日期范围选择" label-width="90px"> <option-item label="日期范围选择" label-width="90px">
<el-date-picker <el-date-picker
type="daterange" type="daterange"
@ -154,26 +162,26 @@
> >
<div> <div>
<el-radio-group v-model="radio" size="large"> <el-radio-group v-model="radio" size="large">
<el-radio :label="1">Apple</el-radio> <el-radio :value="1" label="Apple" />
<el-radio :label="2" disabled>Pear</el-radio> <el-radio :value="2" label="Pear" disabled />
<el-radio :label="3">Orange</el-radio> <el-radio :value="3" label="Orange" />
<el-radio :label="4">Banana</el-radio> <el-radio :value="4" label="Banana" />
</el-radio-group> </el-radio-group>
</div> </div>
<div> <div>
<el-radio-group v-model="radio"> <el-radio-group v-model="radio">
<el-radio :label="1">Apple</el-radio> <el-radio :value="1" label="Apple" />
<el-radio :label="2">Pear</el-radio> <el-radio :value="2" label="Pear" />
<el-radio :label="3" disabled>Orange</el-radio> <el-radio :value="3" label="Orange" disabled />
<el-radio :label="4">Banana</el-radio> <el-radio :value="4" label="Banana" />
</el-radio-group> </el-radio-group>
</div> </div>
<div style="margin-top: 4px"> <div style="margin-top: 4px">
<el-radio-group v-model="radio" size="small"> <el-radio-group v-model="radio" size="small">
<el-radio :label="1">Apple</el-radio> <el-radio :value="1" label="Apple" />
<el-radio :label="2">Pear</el-radio> <el-radio :value="2" label="Pear" />
<el-radio :label="3">Orange</el-radio> <el-radio :value="3" label="Orange" />
<el-radio :label="4" disabled>Banana</el-radio> <el-radio :value="4" label="Banana" disabled />
</el-radio-group> </el-radio-group>
</div> </div>
</option-item> </option-item>
@ -185,26 +193,26 @@
> >
<div> <div>
<el-checkbox-group v-model="checkbox" size="large"> <el-checkbox-group v-model="checkbox" size="large">
<el-checkbox :label="1">Apple</el-checkbox> <el-checkbox :value="1" label="Apple" />
<el-checkbox :label="2" disabled>Pear</el-checkbox> <el-checkbox :value="2" label="Pear" disabled />
<el-checkbox :label="3">Orange</el-checkbox> <el-checkbox :value="3" label="Orange" />
<el-checkbox :label="4">Banana</el-checkbox> <el-checkbox :value="4" label="Banana" />
</el-checkbox-group> </el-checkbox-group>
</div> </div>
<div> <div>
<el-checkbox-group v-model="checkbox"> <el-checkbox-group v-model="checkbox">
<el-checkbox :label="1">Apple</el-checkbox> <el-checkbox :value="1" label="Apple" />
<el-checkbox :label="2">Pear</el-checkbox> <el-checkbox :value="2" label="Pear" />
<el-checkbox :label="3" disabled>Orange</el-checkbox> <el-checkbox :value="3" label="Orange" disabled />
<el-checkbox :label="4">Banana</el-checkbox> <el-checkbox :value="4" label="Banana" />
</el-checkbox-group> </el-checkbox-group>
</div> </div>
<div style="margin-top: 4px"> <div style="margin-top: 4px">
<el-checkbox-group v-model="checkbox" size="small"> <el-checkbox-group v-model="checkbox" size="small">
<el-checkbox :label="1">Apple</el-checkbox> <el-checkbox :value="1" label="Apple" />
<el-checkbox :label="2">Pear</el-checkbox> <el-checkbox :value="2" label="Pear" />
<el-checkbox :label="3">Orange</el-checkbox> <el-checkbox :value="3" label="Orange" />
<el-checkbox :label="4" disabled>Banana</el-checkbox> <el-checkbox :value="4" label="Banana" disabled />
</el-checkbox-group> </el-checkbox-group>
</div> </div>
</option-item> </option-item>
@ -410,4 +418,7 @@
/** 多选框 */ /** 多选框 */
const checkbox = ref([]); const checkbox = ref([]);
/** 年多选 */
const value16 = ref('');
</script> </script>

72
src/views/example/components/menu-group.vue

@ -1,6 +1,5 @@
<template> <template>
<ele-card header="菜单更多功能"> <ele-card header="使用分组菜单">
<div style="margin-bottom: 10px; font-size: 16px">分组菜单</div>
<div style="display: flex; flex-wrap: wrap"> <div style="display: flex; flex-wrap: wrap">
<el-button type="primary" @click="toGroup1" style="margin: 0 12px 12px 0"> <el-button type="primary" @click="toGroup1" style="margin: 0 12px 12px 0">
一级菜单变为分组形式 一级菜单变为分组形式
@ -15,70 +14,12 @@
重置菜单数据 重置菜单数据
</el-link> </el-link>
</ele-text> </ele-text>
<div style="margin: 22px 0 10px 0; font-size: 16px">菜单项触发模式</div>
<el-space style="display: flex">
<el-button
type="primary"
style="margin: 0 12px 12px 0"
@click="setMenuTrigger('navTrigger', 'click')"
>
顶栏菜单使用事件触发
</el-button>
<el-link
type="primary"
:underline="false"
@click="setMenuTrigger('navTrigger', 'route')"
>
重置
</el-link>
</el-space>
<el-space style="display: flex">
<el-button
type="primary"
style="margin: 0 12px 12px 0"
@click="setMenuTrigger('boxTrigger', 'click')"
>
双侧栏一级菜单使用事件触发
</el-button>
<el-link
type="primary"
:underline="false"
@click="setMenuTrigger('boxTrigger', 'route')"
>
重置
</el-link>
</el-space>
<el-space style="display: flex">
<el-button
type="primary"
style="margin: 0 12px 12px 0"
@click="setMenuTrigger('itemTrigger', 'click')"
>
侧栏菜单使用事件触发
</el-button>
<el-link
type="primary"
:underline="false"
@click="setMenuTrigger('itemTrigger', 'route')"
>
重置
</el-link>
</el-space>
<ele-text type="placeholder">
菜单默认使用 RouterLink 渲染
修改为事件触发后还提供了事件触发前的钩子可阻止默认的操作
</ele-text>
<ele-text type="placeholder">
混合导航或双侧栏时使用事件触发点击切换子菜单时就不会再默认打开第一个子菜单了
</ele-text>
</ele-card> </ele-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { EleMessage } from 'ele-admin-plus/es';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { useThemeStore } from '@/store/modules/theme';
const userStore = useUserStore(); const userStore = useUserStore();
const { menus } = storeToRefs(userStore); const { menus } = storeToRefs(userStore);
@ -94,7 +35,7 @@
...m, ...m,
meta: { meta: {
...meta, ...meta,
icon: hasChild ? null : meta.icon, //icon: hasChild ? null : meta.icon,
props: { props: {
...meta.props, ...meta.props,
group: hasChild group: hasChild
@ -121,7 +62,7 @@
...c, ...c,
meta: { meta: {
...meta, ...meta,
icon: hasChild ? null : meta.icon, //icon: hasChild ? null : meta.icon,
props: { props: {
...meta.props, ...meta.props,
group: hasChild group: hasChild
@ -139,11 +80,4 @@
const resetMenu = () => { const resetMenu = () => {
userStore.setMenus(JSON.parse(JSON.stringify(tempMenus))); userStore.setMenus(JSON.parse(JSON.stringify(tempMenus)));
}; };
/** 修改菜单触发模式 */
const themeStore = useThemeStore();
const setMenuTrigger = (key: string, trigger: 'route' | 'click') => {
themeStore.setMenuItemTrigger(key, trigger);
EleMessage.success('修改成功');
};
</script> </script>

14
src/views/extension/bar-code/index.vue

@ -10,15 +10,15 @@
:model-value="options.format" :model-value="options.format"
@update:modelValue="updateFormat" @update:modelValue="updateFormat"
> >
<el-radio label="CODE128">CODE128</el-radio> <el-radio value="CODE128" label="CODE128" />
<el-radio label="EAN13">EAN13</el-radio> <el-radio value="EAN13" label="EAN13" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="渲染方式"> <el-form-item label="渲染方式">
<el-radio-group v-model="tag"> <el-radio-group v-model="tag">
<el-radio label="svg">svg</el-radio> <el-radio value="svg" label="svg" />
<el-radio label="img">img</el-radio> <el-radio value="img" label="img" />
<el-radio label="canvas">canvas</el-radio> <el-radio value="canvas" label="canvas" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="条码文本"> <el-form-item label="条码文本">
@ -54,8 +54,8 @@
label="文本位置" label="文本位置"
> >
<el-radio-group v-model="options.textPosition"> <el-radio-group v-model="options.textPosition">
<el-radio label="bottom">bottom</el-radio> <el-radio value="bottom" label="bottom" />
<el-radio label="top">top</el-radio> <el-radio value="top" label="top" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>

5
src/views/extension/editor/index.vue

@ -69,7 +69,10 @@
reader.onload = (e) => { reader.onload = (e) => {
if (e.target?.result != null) { if (e.target?.result != null) {
const blob = new Blob([e.target.result], { type: file.type }); const blob = new Blob([e.target.result], { type: file.type });
callback(URL.createObjectURL(blob)); callback(URL.createObjectURL(blob), {
text: file.name,
alt: file.name
});
} }
}; };
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);

2
src/views/extension/excel/components/excel-export.vue

@ -7,7 +7,7 @@
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
:pagination="false" :pagination="false"
v-model:selections="selections" v-model:selections="selections"
:tools="['size', 'columns', 'maximized']" :tools="['export', 'print', 'size', 'columns', 'maximized']"
:toolbar="{ theme: 'default' }" :toolbar="{ theme: 'default' }"
:border="true" :border="true"
> >

1
src/views/extension/file/components/file-list.vue

@ -121,6 +121,7 @@
/** item 点击事件 */ /** item 点击事件 */
const onItemClick = (item: FileItem) => { const onItemClick = (item: FileItem) => {
console.log('onItemClick:', JSON.parse(JSON.stringify(item)));
if (item.isDirectory) { if (item.isDirectory) {
// //
emit('go-directory', item as unknown as UserFile); emit('go-directory', item as unknown as UserFile);

22
src/views/extension/map/components/demo-picker.vue

@ -4,35 +4,36 @@
<ele-dropdown <ele-dropdown
v-model="mode" v-model="mode"
:items="[ :items="[
{ title: 'POI检索模式', command: 'poi' }, { title: '点击地图模式', command: 'lnglat' },
{ title: '关键字检索模式', command: 'keyword' } { title: '关键字检索模式', command: 'keyword' },
{ title: 'POI检索模式', command: 'poi' }
]" ]"
trigger="click" trigger="click"
style="margin-right: 12px" style="margin-right: 12px"
> >
<template #default="{ selected }"> <template #default="{ selected }">
<el-button class="ele-btn-icon"> <el-button class="ele-btn-icon" style="width: 134px">
<span>{{ selected.title }}</span> <span>{{ selected.title }}</span>
<el-icon :size="12" style="margin: -1px -4px 0 2px"> <el-icon :size="12" style="margin: 0 -4px 0 2px">
<ArrowDown /> <ArrowDown />
</el-icon> </el-icon>
</el-button> </el-button>
</template> </template>
</ele-dropdown> </ele-dropdown>
<el-button class="ele-btn-icon" @click="openMapPicker"> <el-button type="primary" class="ele-btn-icon" @click="openMapPicker">
打开位置选择 打开位置选择
</el-button> </el-button>
</div> </div>
<div style="margin-top: 12px"> : {{ result.lngAndLat }}</div>
<div style="margin-top: 12px">选中位置: {{ result.location }}</div> <div style="margin-top: 12px">选中位置: {{ result.location }}</div>
<div style="margin-top: 12px">详细地址: {{ result.address }}</div> <div style="margin-top: 12px">详细地址: {{ result.address }}</div>
<div style="margin-top: 12px"> : {{ result.lngAndLat }}</div>
</ele-card> </ele-card>
<!-- 地图位置选择弹窗 --> <!-- 地图位置选择弹窗 -->
<ele-map-picker <ele-map-picker
v-model="visible" v-model="visible"
:dark-mode="darkMode" :dark-mode="darkMode"
:return-regions="true" :return-regions="true"
:keyword-mode="mode === 'keyword'" :mode="mode"
@done="onChoose" @done="onChoose"
/> />
</template> </template>
@ -40,7 +41,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { ArrowDown } from '@/components/icons'; import { ArrowDown } from '@/components/icons';
import type { CenterPoint } from 'ele-admin-plus/es/ele-map-picker/types'; import type {
CenterPoint,
SelectMode
} from 'ele-admin-plus/es/ele-map-picker/types';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@ -51,7 +55,7 @@
const visible = ref(false); const visible = ref(false);
/** 地点检索类型 */ /** 地点检索类型 */
const mode = ref('poi'); const mode = ref<SelectMode>('lnglat');
/** 选择结果 */ /** 选择结果 */
const result = reactive({ const result = reactive({

21
src/views/extension/menu/index.vue

@ -45,6 +45,7 @@
:default-active="menuActive" :default-active="menuActive"
:theme="dark ? 'dark' : 'light'" :theme="dark ? 'dark' : 'light'"
:colorful="colorful" :colorful="colorful"
:text-ellipsis-tooltip="true"
/> />
</div> </div>
</ele-card> </ele-card>
@ -57,6 +58,7 @@
:theme="dark ? 'dark' : 'light'" :theme="dark ? 'dark' : 'light'"
:colorful="colorful" :colorful="colorful"
:ellipsis-props="{ hideTimeout: 800 }" :ellipsis-props="{ hideTimeout: 800 }"
:text-ellipsis-tooltip="true"
/> />
</div> </div>
</ele-card> </ele-card>
@ -132,7 +134,7 @@
}, },
{ {
index: '2-2', index: '2-2',
title: '操作日志', title: '操作日志如果标题很长很长很长',
icon: LinkOutlined, icon: LinkOutlined,
badge: { isDot: true, type: 'success' } badge: { isDot: true, type: 'success' }
} }
@ -142,7 +144,8 @@
index: '3', index: '3',
title: '订单管理', title: '订单管理',
icon: LogOutlined, icon: LogOutlined,
hideTimeout: 450, //hideTimeout: 450,
popperClass: 'demo-menu-popper',
children: [ children: [
{ {
index: '3-1', index: '3-1',
@ -158,7 +161,7 @@
index: '3-3', index: '3-3',
title: '订单报表', title: '订单报表',
icon: LinkOutlined, icon: LinkOutlined,
hideTimeout: 100, //hideTimeout: 100,
children: [ children: [
{ {
index: '3-3-1', index: '3-3-1',
@ -167,7 +170,7 @@
}, },
{ {
index: '3-3-2', index: '3-3-2',
title: '周报表', title: '周报表如果标题很长很长很长很长',
icon: LinkOutlined, icon: LinkOutlined,
disabled: true disabled: true
}, },
@ -280,3 +283,13 @@
name: 'ExtensionMenu' name: 'ExtensionMenu'
}; };
</script> </script>
<style lang="scss">
.demo-menu-popper.ele-menu > .el-menu--popup-container > .el-menu--popup {
overflow: visible;
.el-sub-menu > .el-popper {
position: absolute !important;
}
}
</style>

24
src/views/extension/message/index.vue

@ -3,9 +3,9 @@
<ele-card header="消息提示"> <ele-card header="消息提示">
<option-item label="风格"> <option-item label="风格">
<el-radio-group v-model="theme"> <el-radio-group v-model="theme">
<el-radio :label="0">默认</el-radio> <el-radio :value="0" label="默认" />
<el-radio :label="1">简约</el-radio> <el-radio :value="1" label="简约" />
<el-radio :label="2">原始</el-radio> <el-radio :value="2" label="原始" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="类型"> <option-item label="类型">
@ -331,8 +331,8 @@
</el-row> </el-row>
<option-item label="主题风格"> <option-item label="主题风格">
<el-radio-group v-model="effect"> <el-radio-group v-model="effect">
<el-radio label="light">light</el-radio> <el-radio value="light" label="light" />
<el-radio label="dark">dark</el-radio> <el-radio value="dark" label="dark" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
</ele-card> </ele-card>
@ -342,21 +342,21 @@
</option-item> </option-item>
<option-item label="尺寸" :responsive="false" style="margin: 6px 0 0 0"> <option-item label="尺寸" :responsive="false" style="margin: 6px 0 0 0">
<el-radio-group v-model="loadingSize"> <el-radio-group v-model="loadingSize">
<el-radio label="small">小型</el-radio> <el-radio value="small" label="小型" />
<el-radio label="default">默认</el-radio> <el-radio value="default" label="默认" />
<el-radio label="large">大型</el-radio> <el-radio value="large" label="大型" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="文本" :responsive="false" style="margin: 2px 0 0 0"> <option-item label="文本" :responsive="false" style="margin: 2px 0 0 0">
<el-radio-group v-model="loadingText"> <el-radio-group v-model="loadingText">
<el-radio label="">不显示</el-radio> <el-radio value="" label="不显示" />
<el-radio label="Loading...">显示</el-radio> <el-radio value="Loading..." label="显示" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="类型" :responsive="false" style="margin: 2px 0 0 0"> <option-item label="类型" :responsive="false" style="margin: 2px 0 0 0">
<el-radio-group v-model="loadingType"> <el-radio-group v-model="loadingType">
<el-radio label="dot">圆点</el-radio> <el-radio value="dot" label="圆点" />
<el-radio label="circle">圆圈</el-radio> <el-radio value="circle" label="圆圈" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<ele-loading <ele-loading

12
src/views/extension/modal/components/demo-modal.vue

@ -30,7 +30,7 @@
<el-switch v-model="resetOnClose" size="small" /> <el-switch v-model="resetOnClose" size="small" />
</el-form-item> </el-form-item>
<el-form-item label="限制在主体区域"> <el-form-item label="限制在主体区域">
<el-switch v-model="inner" size="small" /> <el-switch v-model="inner" size="small" @change="onConfigChange" />
</el-form-item> </el-form-item>
<el-form-item label="默认打开位置"> <el-form-item label="默认打开位置">
<el-select <el-select
@ -38,6 +38,7 @@
v-model="position" v-model="position"
placeholder="请选择" placeholder="请选择"
class="ele-fluid" class="ele-fluid"
@change="onConfigChange"
> >
<el-option value="top" label="顶部" /> <el-option value="top" label="顶部" />
<el-option value="bottom" label="底部" /> <el-option value="bottom" label="底部" />
@ -58,6 +59,7 @@
</el-form> </el-form>
</ele-card> </ele-card>
<ele-modal <ele-modal
:key="modalKey"
form form
:width="400" :width="400"
title="高级弹窗" title="高级弹窗"
@ -129,6 +131,9 @@
import { EleMessage } from 'ele-admin-plus/es'; import { EleMessage } from 'ele-admin-plus/es';
import { useFormData } from '@/utils/use-form-data'; import { useFormData } from '@/utils/use-form-data';
/** 用于更新弹窗 */
const modalKey = ref(0);
/** 弹窗是否打开 */ /** 弹窗是否打开 */
const visible = ref(false); const visible = ref(false);
@ -162,6 +167,11 @@
: resizable.value; : resizable.value;
}); });
/** 弹窗属性改变 */
const onConfigChange = () => {
modalKey.value = modalKey.value + 1;
};
/** 表单数据 */ /** 表单数据 */
const { form, resetFields } = useFormData({ const { form, resetFields } = useFormData({
nickname: '', nickname: '',

10
src/views/extension/modal/components/multiple-modal.vue

@ -14,8 +14,12 @@
<ele-text block type="placeholder" style="margin-top: 20px"> <ele-text block type="placeholder" style="margin-top: 20px">
同时打开多个弹窗时点击会自动置顶 同时打开多个弹窗时点击会自动置顶
</ele-text> </ele-text>
<option-item label="限制在主体区域" :responsive="false">
<el-switch v-model="inner" size="small" />
</option-item>
</ele-card> </ele-card>
<ele-modal <ele-modal
:inner="inner"
:width="460" :width="460"
title="弹窗1" title="弹窗1"
v-model="visible1" v-model="visible1"
@ -33,6 +37,7 @@
</template> </template>
</ele-modal> </ele-modal>
<ele-modal <ele-modal
:inner="inner"
:width="460" :width="460"
title="弹窗2" title="弹窗2"
v-model="visible2" v-model="visible2"
@ -49,6 +54,7 @@
</template> </template>
</ele-modal> </ele-modal>
<ele-modal <ele-modal
:inner="inner"
:width="360" :width="360"
title="弹窗3" title="弹窗3"
v-model="visible3" v-model="visible3"
@ -79,6 +85,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import OptionItem from '@/views/extension/avatar/components/option-item.vue';
/** 弹窗是否打开 */ /** 弹窗是否打开 */
const visible1 = ref(false); const visible1 = ref(false);
@ -99,4 +106,7 @@
const openDialog3 = () => { const openDialog3 = () => {
visible3.value = true; visible3.value = true;
}; };
/** 限制在主体区域 */
const inner = ref(false);
</script> </script>

1
src/views/extension/player/components/demo-music.vue

@ -241,6 +241,7 @@
font-size: 16px; font-size: 16px;
line-height: 2; line-height: 2;
overflow: auto; overflow: auto;
scrollbar-width: none;
.xgplayer-lyric-item-active { .xgplayer-lyric-item-active {
color: #e31106; color: #e31106;

18
src/views/extension/printer/index.vue

@ -38,8 +38,8 @@
</el-form-item> </el-form-item>
<el-form-item label="始终显示"> <el-form-item label="始终显示">
<el-radio-group v-model="option.static"> <el-radio-group v-model="option.static">
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -130,17 +130,17 @@
<div style="margin: 12px 0">更多表单组件演示</div> <div style="margin: 12px 0">更多表单组件演示</div>
<div> <div>
<el-radio-group v-model="status"> <el-radio-group v-model="status">
<el-radio label="未完成">未完成</el-radio> <el-radio value="未完成" label="未完成" />
<el-radio label="已完成">已完成</el-radio> <el-radio value="已完成" label="已完成" />
</el-radio-group> </el-radio-group>
</div> </div>
<div> <div>
<el-checkbox-group v-model="citys"> <el-checkbox-group v-model="citys">
<el-checkbox label="武汉">武汉</el-checkbox> <el-checkbox value="武汉" label="武汉" />
<el-checkbox label="北京">北京</el-checkbox> <el-checkbox value="北京" label="北京" />
<el-checkbox label="上海">上海</el-checkbox> <el-checkbox value="上海" label="上海" />
<el-checkbox label="广州">广州</el-checkbox> <el-checkbox value="广州" label="广州" />
<el-checkbox label="深圳">深圳</el-checkbox> <el-checkbox value="深圳" label="深圳" />
</el-checkbox-group> </el-checkbox-group>
</div> </div>
<div style="margin: 12px 0">自定义更加适合打印风格的复选框组件</div> <div style="margin: 12px 0">自定义更加适合打印风格的复选框组件</div>

16
src/views/extension/regions/index.vue

@ -33,6 +33,22 @@
/> />
</div> </div>
</ele-card> </ele-card>
<ele-card header="回显">
<div style="margin: 0 0 12px 0">单选回显</div>
<regions-select
component="text"
:model-value="['420000', '420100', '420103']"
/>
<div style="margin: 22px 0 12px 0">多选回显</div>
<regions-select
component="text"
:model-value="[
['420000', '420100', '420103'],
['420000', '420100', '420106'],
['420000', '420100', '420111']
]"
/>
</ele-card>
</ele-page> </ele-page>
</template> </template>

16
src/views/extension/split/components/demo-basic.vue

@ -5,26 +5,26 @@
> >
<option-item label="显示折叠按钮" :responsive="false" style="margin: 0"> <option-item label="显示折叠按钮" :responsive="false" style="margin: 0">
<el-radio-group v-model="allowCollapse"> <el-radio-group v-model="allowCollapse">
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="支持自由拉伸" :responsive="false" style="margin: 0"> <option-item label="支持自由拉伸" :responsive="false" style="margin: 0">
<el-radio-group v-model="resizable"> <el-radio-group v-model="resizable">
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="上下布局模式" :responsive="false" style="margin: 0"> <option-item label="上下布局模式" :responsive="false" style="margin: 0">
<el-radio-group v-model="vertical"> <el-radio-group v-model="vertical">
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="反转布局方向" :responsive="false" style="margin: 0"> <option-item label="反转布局方向" :responsive="false" style="margin: 0">
<el-radio-group v-model="reverse"> <el-radio-group v-model="reverse">
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<ele-split-panel <ele-split-panel

16
src/views/extension/split/components/demo-table.vue

@ -155,7 +155,9 @@
width: 128, width: 128,
align: 'center', align: 'center',
resizable: false, resizable: false,
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -166,8 +168,8 @@
const current = ref<Organization | null>(null); const current = ref<Organization | null>(null);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageOrganizations({ ...where, ...orders, page, limit }); return pageOrganizations({ ...where, ...orders, ...pages });
}; };
/** 表格数据请求完成事件 */ /** 表格数据请求完成事件 */
@ -235,7 +237,9 @@
width: 128, width: 128,
align: 'center', align: 'center',
resizable: false, resizable: false,
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -243,8 +247,8 @@
const selections2 = ref<User[]>([]); const selections2 = ref<User[]>([]);
/** 表格2数据源 */ /** 表格2数据源 */
const datasource2: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource2: DatasourceFunction = ({ pages, where, orders }) => {
return pageUsers({ ...where, ...orders, page, limit }); return pageUsers({ ...where, ...orders, ...pages });
}; };
</script> </script>

9
src/views/extension/steps/index.vue

@ -15,9 +15,12 @@
<el-button @click="onPrev">上一步</el-button> <el-button @click="onPrev">上一步</el-button>
<el-button type="primary" @click="onNext">下一步</el-button> <el-button type="primary" @click="onNext">下一步</el-button>
<div style="margin-left: 12px"> <div style="margin-left: 12px">
<el-checkbox v-model="alignCenter" :true-label="1" :false-label="0"> <el-checkbox
居中 v-model="alignCenter"
</el-checkbox> :true-value="1"
:false-value="0"
label="居中"
/>
</div> </div>
</div> </div>
</ele-card> </ele-card>

114
src/views/extension/table-select/components/demo-advanced.vue

@ -12,6 +12,7 @@
:table-props="tableProps" :table-props="tableProps"
:popper-width="580" :popper-width="580"
:cache-data="cacheData" :cache-data="cacheData"
@select="onSelect"
> >
<template #topExtra> <template #topExtra>
<demo-advanced-search @search="onSearch" /> <demo-advanced-search @search="onSearch" />
@ -32,13 +33,40 @@
<div style="margin-top: 14px"> <div style="margin-top: 14px">
<el-button type="primary" @click="setValue">回显数据</el-button> <el-button type="primary" @click="setValue">回显数据</el-button>
</div> </div>
<div style="margin: 22px 0 8px 0">使用默认的搜索框:</div>
<div style="max-width: 260px">
<ele-table-select
filterable
clearable
placeholder="请选择"
value-key="userId"
label-key="nickname"
v-model="selectedValue2"
:table-props="tableProps2"
:popper-width="580"
@filterChange="onFilterChange"
@visibleChange="onVisibleChange"
>
<!-- 角色列 -->
<template #roles="{ row }">
<el-tag
v-for="item in row.roles"
:key="item.roleId"
size="small"
:disable-transitions="true"
>
{{ item.roleName }}
</el-tag>
</template>
</ele-table-select>
</div>
</ele-card> </ele-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import DemoAdvancedSearch from './demo-advanced-search.vue'; import DemoAdvancedSearch from './demo-advanced-search.vue';
import { pageUsers } from '@/api/system/user'; import { pageUsers, listUsers } from '@/api/system/user';
import type { EleTableSelect } from 'ele-admin-plus'; import type { EleTableSelect } from 'ele-admin-plus';
import type { TableSelectProps } from 'ele-admin-plus/es/ele-table-select/props'; import type { TableSelectProps } from 'ele-admin-plus/es/ele-table-select/props';
type SelectTableProps = Exclude<TableSelectProps['tableProps'], undefined>; type SelectTableProps = Exclude<TableSelectProps['tableProps'], undefined>;
@ -52,8 +80,8 @@
/** 表格配置 */ /** 表格配置 */
const tableProps = reactive<SelectTableProps>({ const tableProps = reactive<SelectTableProps>({
datasource: ({ page, limit, where, orders }) => { datasource: ({ pages, where, orders }) => {
return pageUsers({ ...where, ...orders, page, limit }); return pageUsers({ ...where, ...orders, ...pages });
}, },
columns: [ columns: [
{ {
@ -130,4 +158,84 @@
const onSearch = (where: UserParam) => { const onSearch = (where: UserParam) => {
selectRef.value?.tableRef?.reload?.({ where, page: 1 }); selectRef.value?.tableRef?.reload?.({ where, page: 1 });
}; };
/** 选中事件 */
const onSelect = (selection: User[]) => {
console.log(JSON.parse(JSON.stringify(selection)));
};
/** 表格下拉选中值 */
const selectedValue2 = ref<number>();
/** 表格配置 */
const tableProps2 = reactive<SelectTableProps>({
datasource: [],
columns: [
{
type: 'index',
columnKey: 'index',
width: 50,
align: 'center',
fixed: 'left'
},
{
prop: 'username',
label: '用户账号',
sortable: 'custom'
},
{
prop: 'nickname',
label: '用户名',
sortable: 'custom',
slot: 'nickname'
},
{
prop: 'sexName',
label: '性别',
sortable: 'custom',
width: 80
},
{
columnKey: 'roles',
label: '角色',
slot: 'roles'
}
],
showOverflowTooltip: true,
highlightCurrentRow: true,
toolbar: false,
pagination: {
pageSize: 6,
layout: 'total, prev, pager, next, jumper',
style: { padding: '0px' }
},
rowStyle: { cursor: 'pointer' }
});
let allData: User[] = [];
/** 查询表格数据 */
listUsers().then((data) => {
tableProps2.datasource = data;
allData = data;
});
/** 筛选输入框值改变事件 */
const onFilterChange = (keyword: string) => {
tableProps2.datasource = allData.filter((d) => {
return (
d.username?.includes?.(keyword) ||
d.nickname?.includes?.(keyword) ||
d.sexName?.includes?.(keyword) ||
d.roles?.some?.((r) => r.roleName?.includes?.(keyword))
);
});
};
/** 下拉框展开状态改变事件 */
const onVisibleChange = (visible: boolean) => {
if (visible) {
tableProps2.datasource = allData;
}
};
</script> </script>

10
src/views/extension/table-select/components/demo-basic-page.vue

@ -29,8 +29,8 @@
<div style="margin-top: 12px; display: flex; align-items: center"> <div style="margin-top: 12px; display: flex; align-items: center">
<div style="padding-right: 12px">禁用:</div> <div style="padding-right: 12px">禁用:</div>
<el-radio-group v-model="disabled"> <el-radio-group v-model="disabled">
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</div> </div>
<div style="margin-top: 8px"> <div style="margin-top: 8px">
@ -51,8 +51,8 @@
/** 表格配置 */ /** 表格配置 */
const tableProps = reactive<SelectTableProps>({ const tableProps = reactive<SelectTableProps>({
datasource: ({ page, limit, where, orders }) => { datasource: ({ pages, where, orders }) => {
return pageUsers({ ...where, ...orders, page, limit }); return pageUsers({ ...where, ...orders, ...pages });
}, },
columns: [ columns: [
{ {
@ -110,6 +110,6 @@
/** 选中事件 */ /** 选中事件 */
const onSelect = (item: User) => { const onSelect = (item: User) => {
console.log(JSON.stringify(item)); console.log(JSON.parse(JSON.stringify(item)));
}; };
</script> </script>

4
src/views/extension/table-select/components/demo-multiple.vue

@ -29,8 +29,8 @@
<div style="margin-top: 12px; display: flex; align-items: center"> <div style="margin-top: 12px; display: flex; align-items: center">
<div style="padding-right: 12px">禁用:</div> <div style="padding-right: 12px">禁用:</div>
<el-radio-group v-model="disabled"> <el-radio-group v-model="disabled">
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</div> </div>
<div style="margin-top: 8px"> <div style="margin-top: 8px">

19
src/views/extension/table/components/child-table.vue

@ -8,6 +8,8 @@
:toolbar="false" :toolbar="false"
:pagination="false" :pagination="false"
:empty-props="false" :empty-props="false"
:selections="selections || []"
@update:selections="updateSelections"
> >
<template #action="{ row: d }"> <template #action="{ row: d }">
<el-link type="primary" :underline="false">修改</el-link> <el-link type="primary" :underline="false">修改</el-link>
@ -32,11 +34,23 @@
const props = defineProps<{ const props = defineProps<{
dictId: number; dictId: number;
selections?: DictionaryData[];
}>();
const emit = defineEmits<{
(e: 'update:selections', value: DictionaryData[]): void;
}>(); }>();
/** 字典数据列配置 */ /** 字典数据列配置 */
const dictDataColumns = ref<Columns>([ const dictDataColumns = ref<Columns>([
{ {
type: 'selection',
columnKey: 'selection',
width: 50,
align: 'center',
fixed: 'left'
},
{
type: 'index', type: 'index',
columnKey: 'index', columnKey: 'index',
width: 50, width: 50,
@ -85,4 +99,9 @@
const removeDictData = (row: DictionaryData) => { const removeDictData = (row: DictionaryData) => {
EleMessage.error('删除: ' + row.dictDataName); EleMessage.error('删除: ' + row.dictDataName);
}; };
/** 更新多选选中数据 */
const updateSelections = (value: DictionaryData[]) => {
emit('update:selections', value);
};
</script> </script>

4
src/views/extension/table/components/default-sorter.vue

@ -81,8 +81,8 @@
]); ]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, orders, filters }) => { const datasource: DatasourceFunction = ({ pages, orders, filters }) => {
return pageUsers({ ...orders, ...filters, page, limit }); return pageUsers({ ...orders, ...filters, ...pages });
}; };
/** 设置排序 */ /** 设置排序 */

117
src/views/extension/table/components/demo-base.vue

@ -4,16 +4,16 @@
<option-item label="表头背景" :responsive="false"> <option-item label="表头背景" :responsive="false">
<el-switch v-model="toolbarBg" size="small" /> <el-switch v-model="toolbarBg" size="small" />
</option-item> </option-item>
<option-item label="显示边框" :responsive="false"> <option-item label="边框" :responsive="false">
<el-switch v-model="border" size="small" @change="onConfigChange" /> <el-switch v-model="border" size="small" @change="onConfigChange" />
</option-item> </option-item>
<option-item label="显示斑马纹" :responsive="false"> <option-item label="斑马纹" :responsive="false">
<el-switch v-model="stripe" size="small" @change="onConfigChange" /> <el-switch v-model="stripe" size="small" @change="onConfigChange" />
</option-item> </option-item>
<option-item label="显示表头" :responsive="false"> <option-item label="表头" :responsive="false">
<el-switch v-model="showHeader" size="small" /> <el-switch v-model="showHeader" size="small" />
</option-item> </option-item>
<option-item label="显示表尾合计行" :responsive="false"> <option-item label="表尾合计行" :responsive="false">
<el-switch v-model="showSummary" size="small" /> <el-switch v-model="showSummary" size="small" />
</option-item> </option-item>
<option-item label="行点击高亮" :responsive="false"> <option-item label="行点击高亮" :responsive="false">
@ -22,6 +22,9 @@
<option-item label="行点击勾选" :responsive="false"> <option-item label="行点击勾选" :responsive="false">
<el-switch v-model="rowClickChecked" size="small" /> <el-switch v-model="rowClickChecked" size="small" />
</option-item> </option-item>
<option-item label="不返回总数" :responsive="false">
<el-switch v-model="noCount" size="small" @change="reload()" />
</option-item>
</div> </div>
<ele-pro-table <ele-pro-table
ref="tableRef" ref="tableRef"
@ -79,9 +82,32 @@
Column Column
} from 'ele-admin-plus/es/ele-pro-table/types'; } from 'ele-admin-plus/es/ele-pro-table/types';
import OptionItem from '@/views/extension/avatar/components/option-item.vue'; import OptionItem from '@/views/extension/avatar/components/option-item.vue';
const data = Array.from({ length: 39 }).map((_, i) => {
const pieceId = i + 1;
const no = String(pieceId).padStart(3, '0');
const secrets = ['机密', '秘密', '公开', '绝密', '内部'];
const retentions = ['永久', '定期10年', '定期20年'];
const retentionNos = ['Y', 'D10', 'D20'];
const retentionRandom = random(0, retentions.length);
const carriers = ['纸张', '光盘'];
const year = '2020';
const item: Piece = {
pieceId,
title: `教学档案${no}`,
pieceNo: `ELE101-${retentionNos[retentionRandom]}-ZM•JX-${year}-${no}`,
secret: secrets[random(0, secrets.length)],
location: `001-001-${Math.random() < 0.5 ? 'A' : 'B'}0${random(1, 7)}`,
type: '教学组织',
retention: retentions[retentionRandom],
carrier: carriers[random(0, carriers.length)],
year,
amount: random(2, 10)
};
return item;
});
/** 案卷 */ /** 案卷 */
export interface Piece { interface Piece {
/** 案卷id */ /** 案卷id */
pieceId?: number; pieceId?: number;
/** 案卷题名 */ /** 案卷题名 */
@ -137,7 +163,13 @@
prop: 'retention', prop: 'retention',
label: '保管期限', label: '保管期限',
minWidth: 110, minWidth: 110,
align: 'center' align: 'center',
filters: [
{ text: '永久', value: '永久' },
{ text: '定期10年', value: '定期10年' },
{ text: '定期20年', value: '定期20年' }
],
filterMultiple: false
}, },
{ {
prop: 'amount', prop: 'amount',
@ -149,7 +181,15 @@
prop: 'secret', prop: 'secret',
label: '密级', label: '密级',
minWidth: 110, minWidth: 110,
align: 'center' align: 'center',
filters: [
{ text: '机密', value: '机密' },
{ text: '秘密', value: '秘密' },
{ text: '公开', value: '公开' },
{ text: '绝密', value: '绝密' },
{ text: '内部', value: '内部' }
],
filterMultiple: true
}, },
{ {
prop: 'year', prop: 'year',
@ -181,36 +221,35 @@
width: 160, width: 160,
align: 'center', align: 'center',
slot: 'action', slot: 'action',
fixed: 'right' fixed: 'right',
hideInPrint: true,
hideInExport: true
} }
]); ]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = async ({ page, limit }) => { const datasource: DatasourceFunction = async ({ pages, filter }) => {
const list = Array.from({ length: limit || 10 }).map((_, i) => { const secretFilter = filter.secret?.length ? filter.secret : null;
const pieceId = ((page || 1) - 1) * (limit || 10) + i + 1; const retentFilter = filter.retention?.length ? filter.retention : null;
const no = String(pieceId).padStart(3, '0'); const list = data.filter((d) => {
const secrets = ['机密', '秘密', '公开', '绝密', '内部']; if (secretFilter != null) {
const retentions = ['永久', '定期10年', '定期20年']; if (!d.secret || !secretFilter.includes(d.secret)) {
const retentionNos = ['Y', 'D10', 'D20']; return false;
const retentionRandom = random(0, retentions.length); }
const carriers = ['纸张', '光盘']; }
const year = '2020'; if (retentFilter != null) {
const item: Piece = { if (!d.retention || !retentFilter.includes(d.retention)) {
pieceId, return false;
title: `教学档案${no}`, }
pieceNo: `ELE101-${retentionNos[retentionRandom]}-ZM•JX-${year}-${no}`, }
secret: secrets[random(0, secrets.length)], return true;
location: `001-001-${Math.random() < 0.5 ? 'A' : 'B'}0${random(1, 7)}`,
type: '教学组织',
retention: retentions[retentionRandom],
carrier: carriers[random(0, carriers.length)],
year,
amount: random(2, 10)
};
return item;
}); });
return { count: 40, list }; const start = ((pages.page || 1) - 1) * (pages.limit || 10);
const end = start + (pages.limit || 10);
return {
count: noCount.value ? '*' : list.length,
list: list.slice(start, end > list.length ? list.length : end)
};
}; };
/** 表格多选选中数据 */ /** 表格多选选中数据 */
@ -227,6 +266,7 @@
const showSummary = ref(true); const showSummary = ref(true);
const highlightCurrentRow = ref(false); const highlightCurrentRow = ref(false);
const rowClickChecked = ref(false); const rowClickChecked = ref(false);
const noCount = ref(false);
/** 打开编辑弹窗 */ /** 打开编辑弹窗 */
const openEdit = (row: Piece) => { const openEdit = (row: Piece) => {
@ -241,6 +281,7 @@
/** 表格合计行 */ /** 表格合计行 */
const getSummaries = ({ columns, data }) => { const getSummaries = ({ columns, data }) => {
const sums: string[] = []; const sums: string[] = [];
const labelIndex = columns[0]?.type === 'selection' ? 2 : 1;
columns.forEach((column: Column, index: number) => { columns.forEach((column: Column, index: number) => {
if (column.property === 'amount') { if (column.property === 'amount') {
sums[index] = data sums[index] = data
@ -253,7 +294,7 @@
return prev; return prev;
} }
}, 0); }, 0);
} else if (index === 2) { } else if (index === labelIndex) {
sums[index] = '合计'; sums[index] = '合计';
} }
}); });
@ -262,7 +303,7 @@
/** 获取多选行数据 */ /** 获取多选行数据 */
const getSelections = () => { const getSelections = () => {
console.log(JSON.stringify(selections.value)); console.log(JSON.parse(JSON.stringify(selections.value)));
EleMessage.success( EleMessage.success(
'共 ' + selections.value.length + ' 条数据已打印在控制台' '共 ' + selections.value.length + ' 条数据已打印在控制台'
); );
@ -270,14 +311,20 @@
/** 获取选中行数据 */ /** 获取选中行数据 */
const getCurrent = () => { const getCurrent = () => {
console.log(JSON.stringify(current.value));
if (!current.value) { if (!current.value) {
EleMessage.success('未选中任何数据'); EleMessage.success('未选中任何数据');
} else { } else {
console.log(JSON.parse(JSON.stringify(current.value)));
EleMessage.success(current.value.pieceNo + '的数据已打印在控制台'); EleMessage.success(current.value.pieceNo + '的数据已打印在控制台');
} }
}; };
/** 搜索 */
const reload = () => {
selections.value = [];
tableRef.value?.reload?.({ page: 1 });
};
/** 表格属性改变 */ /** 表格属性改变 */
const onConfigChange = () => { const onConfigChange = () => {
nextTick(() => { nextTick(() => {

199
src/views/extension/table/components/demo-selections.vue

@ -0,0 +1,199 @@
<template>
<div>
<ele-pro-table
row-key="pieceId"
:columns="columns"
:datasource="data"
:show-overflow-tooltip="true"
v-model:selections="selections"
v-model:current="current"
highlight-current-row
:pagination="false"
>
<template #toolbar>
<el-space :size="12" wrap>
<el-button type="primary" class="ele-btn-icon" @click="getSelections">
获取多选
</el-button>
<el-button type="warning" class="ele-btn-icon" @click="setSelections">
修改多选
</el-button>
<el-button
type="danger"
class="ele-btn-icon"
@click="clearSelections"
>
清空多选
</el-button>
<el-button
plain
type="primary"
class="ele-btn-icon"
@click="getCurrent"
>
获取单选
</el-button>
<el-button
plain
type="warning"
class="ele-btn-icon"
@click="setCurrent"
>
修改单选
</el-button>
<el-button
plain
type="danger"
class="ele-btn-icon"
@click="removeCurrent"
>
清空单选
</el-button>
</el-space>
</template>
</ele-pro-table>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { EleMessage, random } from 'ele-admin-plus/es';
import type { Column } from 'ele-admin-plus/es/ele-pro-table/types';
/** 案卷 */
interface Piece {
/** 案卷id */
pieceId?: number;
/** 案卷题名 */
title?: string;
/** 案卷档号 */
pieceNo?: string;
/** 密级 */
secret?: string;
/** 保管期限 */
retention?: string;
/** 归档年度 */
year?: string;
/** 件数 */
amount?: number;
}
/** 表格列配置 */
const columns = ref<Column[]>([
{
type: 'selection',
columnKey: 'selection',
width: 50,
align: 'center',
fixed: 'left'
},
{
type: 'index',
columnKey: 'index',
width: 50,
align: 'center',
fixed: 'left'
},
{
prop: 'pieceNo',
label: '案卷档号',
minWidth: 260
},
{
prop: 'title',
label: '案卷题名',
minWidth: 160
},
{
prop: 'retention',
label: '保管期限',
minWidth: 110,
align: 'center'
},
{
prop: 'amount',
label: '件数',
minWidth: 110,
align: 'center'
},
{
prop: 'secret',
label: '密级',
minWidth: 110,
align: 'center'
},
{
prop: 'year',
label: '归档年度',
minWidth: 110,
align: 'center'
}
]);
/** 表格数据源 */
const data = ref(
Array.from({ length: 8 }).map((_, i) => {
const pieceId = i + 1;
const no = String(pieceId).padStart(3, '0');
const secrets = ['机密', '秘密', '公开', '绝密', '内部'];
const retentions = ['永久', '定期10年', '定期20年'];
const retentionNos = ['Y', 'D10', 'D20'];
const retentionRandom = random(0, retentions.length);
const year = '2020';
const item: Piece = {
pieceId,
title: `教学档案${no}`,
pieceNo: `ELE101-${retentionNos[retentionRandom]}-ZM•JX-${year}-${no}`,
secret: secrets[random(0, secrets.length)],
retention: retentions[retentionRandom],
year,
amount: random(2, 10)
};
return item;
})
);
/** 表格多选选中数据 */
const selections = ref<Piece[]>([]);
/** 表格单选选中数据 */
const current = ref<Piece | null>(null);
/** 获取多选 */
const getSelections = () => {
console.log(JSON.parse(JSON.stringify(selections.value)));
EleMessage.success(
'共 ' + selections.value.length + ' 条数据已打印在控制台'
);
};
/** 修改多选 */
const setSelections = () => {
selections.value = [data.value[1], data.value[2], data.value[5]];
};
/** 清空多选 */
const clearSelections = () => {
selections.value = [];
};
/** 获取单选 */
const getCurrent = () => {
if (!current.value) {
EleMessage.success('未选中任何数据');
} else {
console.log(JSON.parse(JSON.stringify(current.value)));
EleMessage.success(current.value.pieceNo + '的数据已打印在控制台');
}
};
/** 修改单选 */
const setCurrent = () => {
current.value = data.value[3];
};
/** 清空单选 */
const removeCurrent = () => {
current.value = null;
};
</script>

4
src/views/extension/table/components/expandable-table.vue

@ -92,7 +92,7 @@
]); ]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, orders }) => { const datasource: DatasourceFunction = ({ pages, orders }) => {
return pageUsers({ ...orders, page, limit }); return pageUsers({ ...orders, ...pages });
}; };
</script> </script>

36
src/views/extension/table/components/nested-table.vue

@ -6,6 +6,15 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
<template #toolbar>
<el-button
type="primary"
@click="getChildSelections"
class="ele-btn-icon"
>
获取所有选中
</el-button>
</template>
<template #action="{ row }"> <template #action="{ row }">
<el-link type="primary" :underline="false">修改</el-link> <el-link type="primary" :underline="false">修改</el-link>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
@ -14,14 +23,17 @@
</el-link> </el-link>
</template> </template>
<template #expand="{ row }"> <template #expand="{ row }">
<child-table :dictId="row.dictId" /> <child-table
:dictId="row.dictId"
v-model:selections="childSelections[row.dictId]"
/>
</template> </template>
</ele-pro-table> </ele-pro-table>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref, reactive } from 'vue';
import { EleMessage } from 'ele-admin-plus/es'; import { EleMessage } from 'ele-admin-plus/es';
import type { import type {
DatasourceFunction, DatasourceFunction,
@ -79,17 +91,31 @@
width: 120, width: 120,
align: 'center', align: 'center',
slot: 'action', slot: 'action',
fixed: 'right' fixed: 'right',
hideInPrint: true,
hideInExport: true
} }
]); ]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, orders }) => { const datasource: DatasourceFunction = ({ pages, orders }) => {
return pageDictionaries({ ...orders, page, limit }); return pageDictionaries({ ...orders, ...pages });
}; };
/** 删除字典 */ /** 删除字典 */
const removeDict = (row: Dictionary) => { const removeDict = (row: Dictionary) => {
EleMessage.error('删除: ' + row.dictName); EleMessage.error('删除: ' + row.dictName);
}; };
/** 子表格多选选中数据 */
const childSelections = reactive({});
/** 获取子表格多选选中数据 */
const getChildSelections = () => {
console.log(JSON.parse(JSON.stringify(childSelections)));
const length = Object.keys(childSelections)
.map((key) => childSelections[key].length)
.reduce((a, b) => a + b, 0);
EleMessage.success(`共选中 ${length} 条数据, 已打印在控制台`);
};
</script> </script>

75
src/views/extension/table/components/sortable-table.vue

@ -4,12 +4,11 @@
ref="tableRef" ref="tableRef"
row-key="userId" row-key="userId"
:columns="columns" :columns="columns"
:datasource="data" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
:loading="loading"
:pagination="false" :pagination="false"
v-model:selections="selections"
class="sortable-table" class="sortable-table"
@refresh="query"
> >
<template #handle> <template #handle>
<ele-text <ele-text
@ -28,15 +27,24 @@
size="8px" size="8px"
/> />
</template> </template>
<template #toolbar>
<el-button type="primary" @click="getSelections" class="ele-btn-icon">
获取选中
</el-button>
</template>
</ele-pro-table> </ele-pro-table>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'; import { ref, onMounted, onBeforeUnmount } from 'vue';
import { EleMessage } from 'ele-admin-plus/es';
import SortableJs from 'sortablejs'; import SortableJs from 'sortablejs';
import type { EleDataTable } from 'ele-admin-plus'; import type { EleProTable } from 'ele-admin-plus';
import type { Columns } from 'ele-admin-plus/es/ele-data-table/types'; import type {
DatasourceFunction,
Columns
} from 'ele-admin-plus/es/ele-pro-table/types';
import { HolderOutlined } from '@/components/icons'; import { HolderOutlined } from '@/components/icons';
import type { User } from '@/api/system/user/model'; import type { User } from '@/api/system/user/model';
import { listUsers } from '@/api/system/user'; import { listUsers } from '@/api/system/user';
@ -45,7 +53,10 @@
let sortableIns: SortableJs | null = null; let sortableIns: SortableJs | null = null;
/** 表格实例 */ /** 表格实例 */
const tableRef = ref<InstanceType<typeof EleDataTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
/** 表格选中数据 */
const selections = ref<User[]>([]);
/** 表格列配置 */ /** 表格列配置 */
const columns = ref<Columns>([ const columns = ref<Columns>([
@ -54,7 +65,16 @@
width: 58, width: 58,
align: 'center', align: 'center',
slot: 'handle', slot: 'handle',
fixed: 'left' fixed: 'left',
hideInSetting: true
},
{
type: 'selection',
columnKey: 'selection',
width: 50,
align: 'center',
fixed: 'left',
reserveSelection: true
}, },
{ {
prop: 'username', prop: 'username',
@ -67,10 +87,16 @@
minWidth: 110 minWidth: 110
}, },
{ {
columnKey: 'sex',
prop: 'sexName', prop: 'sexName',
label: '性别', label: '性别',
width: 110, width: 110,
align: 'center' align: 'center',
filters: [
{ text: '男', value: '1' },
{ text: '女', value: '2' }
],
filterMultiple: false
}, },
{ {
prop: 'createTime', prop: 'createTime',
@ -89,20 +115,9 @@
]); ]);
/** 表格数据 */ /** 表格数据 */
const data = ref<User[]>([]); const datasource: DatasourceFunction = ({ where, orders, filters }) => {
return listUsers({ ...where, ...orders, ...filters });
/** 请求状态 */
const loading = ref(true);
/** 请求数据 */
const query = () => {
loading.value = true;
listUsers().then((list) => {
data.value = list;
loading.value = false;
});
}; };
query();
onMounted(() => { onMounted(() => {
// //
@ -123,13 +138,13 @@
typeof oldDraggableIndex === 'number' && typeof oldDraggableIndex === 'number' &&
typeof newDraggableIndex === 'number' typeof newDraggableIndex === 'number'
) { ) {
const temp = [...data.value]; const temp = [...(tableRef.value?.getData?.() ?? [])];
temp.splice( temp.splice(
newDraggableIndex, newDraggableIndex,
0, 0,
temp.splice(oldDraggableIndex, 1)[0] temp.splice(oldDraggableIndex, 1)[0]
); );
data.value = temp; tableRef.value?.setData?.(temp);
Array.from( Array.from(
tbodyEl.querySelectorAll('.el-table__row.hover-row') tbodyEl.querySelectorAll('.el-table__row.hover-row')
).forEach((el) => { ).forEach((el) => {
@ -147,6 +162,20 @@
sortableIns = null; sortableIns = null;
} }
}); });
/** 获取选中数据 */
const getSelections = () => {
const data = tableRef.value?.getData?.() ?? [];
const result = [...selections.value].sort((a, b) => {
const ai = data.findIndex((d) => d.userId === a.userId);
const bi = data.findIndex((d) => d.userId === b.userId);
return ai - bi;
});
console.log(JSON.parse(JSON.stringify(result)));
EleMessage.success(
`共选中 ${selections.value.length} 条数据, 已打印在控制台`
);
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

14
src/views/extension/table/components/virtual-base.vue

@ -206,7 +206,9 @@
width: 160, width: 160,
align: 'center', align: 'center',
slot: 'action', slot: 'action',
fixed: 'right' fixed: 'right',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -294,12 +296,12 @@
}; };
/** 列筛选改变事件 */ /** 列筛选改变事件 */
const onFilterChange = (filters: Filter) => { const onFilterChange = (filter: Filter) => {
loading.value = true; loading.value = true;
setTimeout(() => { setTimeout(() => {
let result = [...database]; let result = [...database];
Object.keys(filters).forEach((key: string) => { Object.keys(filter).forEach((key: string) => {
const filterValue = filters[key]; const filterValue = filter[key];
if (filterValue && filterValue.length) { if (filterValue && filterValue.length) {
result = result.filter((d) => { result = result.filter((d) => {
if (Array.isArray(d[key])) { if (Array.isArray(d[key])) {
@ -316,7 +318,7 @@
/** 获取多选行数据 */ /** 获取多选行数据 */
const getSelections = () => { const getSelections = () => {
console.log(JSON.stringify(selections.value)); console.log(JSON.parse(JSON.stringify(selections.value)));
EleMessage.success( EleMessage.success(
'共 ' + selections.value.length + ' 条数据已打印在控制台' '共 ' + selections.value.length + ' 条数据已打印在控制台'
); );
@ -324,10 +326,10 @@
/** 获取选中行数据 */ /** 获取选中行数据 */
const getCurrent = () => { const getCurrent = () => {
console.log(JSON.stringify(current.value));
if (!current.value) { if (!current.value) {
EleMessage.success('未选中任何数据'); EleMessage.success('未选中任何数据');
} else { } else {
console.log(JSON.parse(JSON.stringify(current.value)));
EleMessage.success(current.value.nickname + '的数据已打印在控制台'); EleMessage.success(current.value.nickname + '的数据已打印在控制台');
} }
}; };

6
src/views/extension/table/index.vue

@ -27,6 +27,7 @@
<merge-cell v-else-if="active == 'merge'" /> <merge-cell v-else-if="active == 'merge'" />
<editable-table v-else-if="active == 'editable'" /> <editable-table v-else-if="active == 'editable'" />
<sortable-table v-else-if="active == 'sortable'" /> <sortable-table v-else-if="active == 'sortable'" />
<demo-selections v-else-if="active == 'selections'" />
<virtual-base v-else-if="active == 'virtual-base'" /> <virtual-base v-else-if="active == 'virtual-base'" />
<virtual-header v-else-if="active == 'virtual-header'" /> <virtual-header v-else-if="active == 'virtual-header'" />
<virtual-merge v-else-if="active == 'virtual-merge'" /> <virtual-merge v-else-if="active == 'virtual-merge'" />
@ -52,6 +53,7 @@
import MergeCell from './components/merge-cell.vue'; import MergeCell from './components/merge-cell.vue';
import EditableTable from './components/editable-table.vue'; import EditableTable from './components/editable-table.vue';
import SortableTable from './components/sortable-table.vue'; import SortableTable from './components/sortable-table.vue';
import DemoSelections from './components/demo-selections.vue';
import VirtualBase from './components/virtual-base.vue'; import VirtualBase from './components/virtual-base.vue';
import VirtualHeader from './components/virtual-header.vue'; import VirtualHeader from './components/virtual-header.vue';
import VirtualMerge from './components/virtual-merge.vue'; import VirtualMerge from './components/virtual-merge.vue';
@ -102,6 +104,10 @@
title: '拖拽排序' title: '拖拽排序'
}, },
{ {
index: 'selections',
title: '单选多选'
},
{
index: 'virtual', index: 'virtual',
title: '虚拟滚动表格', title: '虚拟滚动表格',
group: true, group: true,

42
src/views/extension/tabs/components/demo-list.vue

@ -0,0 +1,42 @@
<template>
<div style="padding: 10px 0">
<template v-for="(d, i) in data" :key="i">
<el-divider v-if="i > 0" border-style="dashed" style="margin: 0" />
<div style="padding: 8px 0; display: flex">
<ele-ellipsis class="demo-tab-item-title">{{ d.title }}</ele-ellipsis>
<ele-text type="placeholder">{{ d.date }}</ele-text>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const props = defineProps<{
type: number;
}>();
const data = ref(
(() => {
return Array.from({ length: 4 - props.type }).map((_, i) => {
return {
title: `EleAdmin新版本发布, 欢迎体验 ${props.type + 1}-${i + 1}`,
date: '2023-05-20'
};
});
})()
);
</script>
<style lang="scss" scoped>
.demo-tab-item-title {
flex: 1;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: var(--el-color-primary);
}
}
</style>

85
src/views/extension/tabs/index.vue

@ -3,21 +3,22 @@
<ele-card header="基础用法"> <ele-card header="基础用法">
<option-item label="尺寸"> <option-item label="尺寸">
<el-radio-group v-model="size" size="large" @change="onChange"> <el-radio-group v-model="size" size="large" @change="onChange">
<el-radio label="small">小型</el-radio> <el-radio value="small" label="小型" />
<el-radio label="default">默认</el-radio> <el-radio value="default" label="默认" />
<el-radio label="large">大型</el-radio> <el-radio value="large" label="大型" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="风格" style="margin: 0"> <option-item label="风格" style="margin: 0">
<div style="max-width: 220px"> <div style="max-width: 220px">
<el-select v-model="type" class="ele-fluid"> <el-select v-model="type" class="ele-fluid">
<el-option label="默认" value="default" /> <el-option label="默认风格" value="default" />
<el-option label="卡片" value="card" /> <el-option label="卡片风格" value="card" />
<el-option label="边框卡片" value="border-card" /> <el-option label="边框卡片风格" value="border-card" />
<el-option label="朴素" value="plain" /> <el-option label="朴素风格" value="plain" />
<el-option label="简约" value="simple" /> <el-option label="简约风格" value="simple" />
<el-option label="指示器" value="indicator" /> <el-option label="指示器风格" value="indicator" />
<el-option label="按钮" value="button" /> <el-option label="按钮风格" value="button" />
<el-option label="标签风格" value="tag" />
</el-select> </el-select>
</div> </div>
</option-item> </option-item>
@ -110,55 +111,13 @@
class="demo-tabs" class="demo-tabs"
> >
<template #new> <template #new>
<div style="padding: 10px 0"> <demo-list :type="0" />
<template v-for="i in 4" :key="i">
<el-divider
v-if="i > 1"
border-style="dashed"
style="margin: 0"
/>
<div style="padding: 8px 0; display: flex">
<ele-ellipsis style="flex: 1; cursor: pointer">
EleAdmin新版本发布, 欢迎体验 1-{{ i }}
</ele-ellipsis>
<ele-text type="placeholder">2023-05-20</ele-text>
</div>
</template>
</div>
</template> </template>
<template #hot> <template #hot>
<div style="padding: 10px 0"> <demo-list :type="1" />
<template v-for="i in 3" :key="i">
<el-divider
v-if="i > 1"
border-style="dashed"
style="margin: 0"
/>
<div style="padding: 8px 0; display: flex">
<ele-ellipsis style="flex: 1; cursor: pointer">
EleAdmin新版本发布, 欢迎体验 2-{{ i }}
</ele-ellipsis>
<ele-text type="placeholder">2023-05-20</ele-text>
</div>
</template>
</div>
</template> </template>
<template #subject> <template #subject>
<div style="padding: 10px 0"> <demo-list :type="2" />
<template v-for="i in 2" :key="i">
<el-divider
v-if="i > 1"
border-style="dashed"
style="margin: 0"
/>
<div style="padding: 8px 0; display: flex">
<ele-ellipsis style="flex: 1; cursor: pointer">
EleAdmin新版本发布, 欢迎体验 3-{{ i }}
</ele-ellipsis>
<ele-text type="placeholder">2023-05-20</ele-text>
</div>
</template>
</div>
</template> </template>
</ele-tabs> </ele-tabs>
</div> </div>
@ -168,9 +127,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import OptionItem from '@/views/extension/avatar/components/option-item.vue';
import type { EleTabs } from 'ele-admin-plus'; import type { EleTabs } from 'ele-admin-plus';
import type { TabSize, TabType } from 'ele-admin-plus/es/ele-tabs/types'; import type { TabSize, TabType } from 'ele-admin-plus/es/ele-tabs/types';
import OptionItem from '@/views/extension/avatar/components/option-item.vue';
import DemoList from './components/demo-list.vue';
/** 组件实例 */ /** 组件实例 */
const tabRef = ref<InstanceType<typeof EleTabs> | null>(null); const tabRef = ref<InstanceType<typeof EleTabs> | null>(null);
@ -234,22 +194,17 @@
div.demo-tabs.ele-tabs :deep(.el-tabs__header) { div.demo-tabs.ele-tabs :deep(.el-tabs__header) {
--ele-tab-padding: 0; --ele-tab-padding: 0;
--ele-tab-height: 46px; --ele-tab-height: 46px;
--ele-tab-simple-active-bg: none; --ele-tab-font-size: 16px;
--ele-tab-simple-hover-color: var(--el-color-primary);
--ele-tab-simple-hover-bg: none; --ele-tab-simple-hover-bg: none;
--ele-tab-simple-active-bg: none;
--ele-tab-simple-active-weight: normal;
.el-tabs__item { .el-tabs__item {
font-size: 16px;
transition: none;
& + .el-tabs__item { & + .el-tabs__item {
margin-left: 40px; margin-left: 40px;
} }
&:hover,
&.is-active {
font-weight: bold;
}
&.is-active::after { &.is-active::after {
height: 3px; height: 3px;
width: 20px !important; width: 20px !important;

42
src/views/extension/tag/index.vue

@ -8,31 +8,45 @@
:effect="effect" :effect="effect"
:validator="validator" :validator="validator"
:tooltip-props="{ effect: 'danger' }" :tooltip-props="{ effect: 'danger' }"
:readonly="readonly"
:disabled="disabled"
/> />
<ele-text type="secondary" style="padding: 8px 0">{{ <ele-text type="secondary" style="padding: 8px 0">{{
JSON.stringify(tags) JSON.stringify(tags)
}}</ele-text> }}</ele-text>
<option-item label="尺寸选择"> <option-item label="尺寸选择">
<el-radio-group v-model="size"> <el-radio-group v-model="size">
<el-radio label="large">large</el-radio> <el-radio value="large" label="large" />
<el-radio label="default">default</el-radio> <el-radio value="default" label="default" />
<el-radio label="small">small</el-radio> <el-radio value="small" label="small" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="颜色选择" style="margin-top: 8px"> <option-item label="颜色选择" style="margin-top: 8px">
<el-radio-group v-model="type"> <el-radio-group v-model="type">
<el-radio label="primary">primary</el-radio> <el-radio value="primary" label="primary" />
<el-radio label="success">success</el-radio> <el-radio value="success" label="success" />
<el-radio label="info">info</el-radio> <el-radio value="info" label="info" />
<el-radio label="warning">warning</el-radio> <el-radio value="warning" label="warning" />
<el-radio label="danger">danger</el-radio> <el-radio value="danger" label="danger" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="主题选择" style="margin-top: 8px"> <option-item label="主题选择" style="margin-top: 8px">
<el-radio-group v-model="effect"> <el-radio-group v-model="effect">
<el-radio label="light">light</el-radio> <el-radio value="light" label="light" />
<el-radio label="dark">dark</el-radio> <el-radio value="dark" label="dark" />
<el-radio label="plain">plain</el-radio> <el-radio value="plain" label="plain" />
</el-radio-group>
</option-item>
<option-item label="是否禁用" style="margin-top: 8px">
<el-radio-group v-model="disabled">
<el-radio :value="false" label="否" />
<el-radio :value="true" label="是" />
</el-radio-group>
</option-item>
<option-item label="是否只读" style="margin-top: 8px">
<el-radio-group v-model="readonly">
<el-radio :value="false" label="否" />
<el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
</ele-card> </ele-card>
@ -67,6 +81,12 @@
/** 标签输入 */ /** 标签输入 */
const tags = ref(['标签一', '标签二', '标签三']); const tags = ref(['标签一', '标签二', '标签三']);
/** 是否只读 */
const readonly = ref(false);
/** 是否禁用 */
const disabled = ref(false);
/** 限制不能重复添加 */ /** 限制不能重复添加 */
const validator = (value: string) => { const validator = (value: string) => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {

22
src/views/extension/text/index.vue

@ -3,9 +3,9 @@
<ele-card header="单行文本省略"> <ele-card header="单行文本省略">
<option-item label="提示风格"> <option-item label="提示风格">
<el-radio-group v-model="tooltip"> <el-radio-group v-model="tooltip">
<el-radio :label="1">原生</el-radio> <el-radio :value="1" label="原生" />
<el-radio :label="2">Tooltip</el-radio> <el-radio :value="2" label="Tooltip" />
<el-radio :label="3">关闭</el-radio> <el-radio :value="3" label="关闭" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<div style="width: 520px; max-width: 100%; margin-top: 8px"> <div style="width: 520px; max-width: 100%; margin-top: 8px">
@ -18,9 +18,9 @@
<ele-card header="多行文本省略"> <ele-card header="多行文本省略">
<option-item label="最大行数"> <option-item label="最大行数">
<el-radio-group v-model="maxLine"> <el-radio-group v-model="maxLine">
<el-radio :label="2">2</el-radio> <el-radio :value="2" label="2" />
<el-radio :label="3">3</el-radio> <el-radio :value="3" label="3" />
<el-radio :label="4">4</el-radio> <el-radio :value="4" label="4" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<div style="width: 520px; max-width: 100%; margin-top: 8px"> <div style="width: 520px; max-width: 100%; margin-top: 8px">
@ -108,15 +108,15 @@
</option-item> </option-item>
<option-item label="显示动画"> <option-item label="显示动画">
<el-radio-group v-model="ripple"> <el-radio-group v-model="ripple">
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="尺寸选择"> <option-item label="尺寸选择">
<el-radio-group v-model="dotSize"> <el-radio-group v-model="dotSize">
<el-radio label="7px">7px</el-radio> <el-radio value="7px" label="7px" />
<el-radio label="11px">11px</el-radio> <el-radio value="11px" label="11px" />
<el-radio label="15px">15px</el-radio> <el-radio value="15px" label="15px" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
</ele-card> </ele-card>

8
src/views/extension/tree-select/components/demo-basic.vue

@ -14,14 +14,14 @@
</div> </div>
<option-item label="整体禁用" :responsive="false" style="margin-top: 16px"> <option-item label="整体禁用" :responsive="false" style="margin-top: 16px">
<el-radio-group v-model="disabled"> <el-radio-group v-model="disabled">
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="父级点击" :responsive="false" style="margin-top: 0"> <option-item label="父级点击" :responsive="false" style="margin-top: 0">
<el-radio-group v-model="treeProps.expandOnClickNode"> <el-radio-group v-model="treeProps.expandOnClickNode">
<el-radio :label="true">展开</el-radio> <el-radio :value="true" label="展开" />
<el-radio :label="false">选中</el-radio> <el-radio :value="false" label="选中" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
</ele-card> </ele-card>

12
src/views/extension/tree-select/components/demo-multiple.vue

@ -15,14 +15,14 @@
</div> </div>
<option-item label="整体禁用" :responsive="false" style="margin-top: 16px"> <option-item label="整体禁用" :responsive="false" style="margin-top: 16px">
<el-radio-group v-model="disabled"> <el-radio-group v-model="disabled">
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="父级点击" :responsive="false" style="margin-top: 0"> <option-item label="父级点击" :responsive="false" style="margin-top: 0">
<el-radio-group v-model="treeProps.expandOnClickNode"> <el-radio-group v-model="treeProps.expandOnClickNode">
<el-radio :label="true">展开</el-radio> <el-radio :value="true" label="展开" />
<el-radio :label="false">勾选</el-radio> <el-radio :value="false" label="勾选" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="父子联动" :responsive="false" style="margin-top: 0"> <option-item label="父子联动" :responsive="false" style="margin-top: 0">
@ -30,8 +30,8 @@
:model-value="treeProps.checkStrictly" :model-value="treeProps.checkStrictly"
@update:modelValue="updateCheckStrictly" @update:modelValue="updateCheckStrictly"
> >
<el-radio :label="true"></el-radio> <el-radio :value="true" label="否" />
<el-radio :label="false"></el-radio> <el-radio :value="false" label="是" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
</ele-card> </ele-card>

28
src/views/extension/tree-select/components/demo-strategy.vue

@ -22,21 +22,27 @@
v-model="showCheckedStrategy" v-model="showCheckedStrategy"
style="flex-direction: column; align-items: flex-start" style="flex-direction: column; align-items: flex-start"
> >
<el-radio label="parent" style="margin: 0; display: flex; width: 100%"> <el-radio
parent(当子节点全部选中时只显示父级) value="parent"
</el-radio> label="parent(当子节点全部选中时只显示父级)"
<el-radio label="child" style="margin: 0; display: flex; width: 100%"> style="margin: 0; display: flex; width: 100%"
child(只显示选中的子节点) />
</el-radio> <el-radio
<el-radio label="all" style="margin: 0; display: flex; width: 100%"> value="child"
all(显示所有选中的节点) label="child(只显示选中的子节点)"
</el-radio> style="margin: 0; display: flex; width: 100%"
/>
<el-radio
value="all"
label="all(显示所有选中的节点)"
style="margin: 0; display: flex; width: 100%"
/>
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="值策略" :responsive="false" style="margin-top: 10px"> <option-item label="值策略" :responsive="false" style="margin-top: 10px">
<el-radio-group v-model="checkedValueStrategy"> <el-radio-group v-model="checkedValueStrategy">
<el-radio :label="false">全部选中值</el-radio> <el-radio :value="false" label="全部选中值" />
<el-radio :label="true">同标签策略</el-radio> <el-radio :value="true" label="同标签策略" />
</el-radio-group> </el-radio-group>
</option-item> </option-item>
<option-item label="选中值" style="margin-top: 10px"> <option-item label="选中值" style="margin-top: 10px">

6
src/views/extension/upload/components/demo-advanced.vue

@ -16,8 +16,8 @@
<div style="line-height: 22px; margin-left: 22px">禁用:</div> <div style="line-height: 22px; margin-left: 22px">禁用:</div>
<div style="flex: 1; padding-left: 12px"> <div style="flex: 1; padding-left: 12px">
<el-radio-group v-model="disabled"> <el-radio-group v-model="disabled">
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</div> </div>
</div> </div>
@ -137,7 +137,7 @@
/** 全部上传完毕后与其它表单数据一起提交 */ /** 全部上传完毕后与其它表单数据一起提交 */
const submitForm = () => { const submitForm = () => {
EleMessage.success('已全部上传完毕'); EleMessage.success('已全部上传完毕');
console.log(JSON.stringify(images.value)); console.log(JSON.parse(JSON.stringify(images.value)));
loading.value = false; loading.value = false;
}; };
</script> </script>

8
src/views/extension/upload/components/demo-basic.vue

@ -14,8 +14,8 @@
<div style="line-height: 22px; margin-left: 22px">只读:</div> <div style="line-height: 22px; margin-left: 22px">只读:</div>
<div style="flex: 1; padding-left: 12px"> <div style="flex: 1; padding-left: 12px">
<el-radio-group v-model="readonly"> <el-radio-group v-model="readonly">
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
</el-radio-group> </el-radio-group>
</div> </div>
</div> </div>
@ -54,7 +54,7 @@
/** 获取当前数据 */ /** 获取当前数据 */
const getData = () => { const getData = () => {
console.log(JSON.stringify(images.value)); console.log(JSON.parse(JSON.stringify(images.value)));
EleMessage.success('数据已打印在控制台'); EleMessage.success('数据已打印在控制台');
}; };
@ -75,7 +75,7 @@
images.value.push({ ...d }); images.value.push({ ...d });
} }
const item = images.value.find((t) => t.key === d.key); const item = images.value.find((t) => t.key === d.key);
console.log(JSON.stringify(item)); console.log(JSON.parse(JSON.stringify(item)));
if (!item) { if (!item) {
return; return;
} }

2
src/views/extension/upload/components/demo-customer.vue

@ -169,7 +169,7 @@
if (!valid) { if (!valid) {
return; return;
} }
console.log(JSON.stringify(form)); console.log(JSON.parse(JSON.stringify(form)));
EleMessage.success('数据已打印在控制台'); EleMessage.success('数据已打印在控制台');
}); });
}; };

2
src/views/extension/upload/components/demo-file.vue

@ -95,7 +95,7 @@
if (!valid) { if (!valid) {
return; return;
} }
console.log(JSON.stringify(form)); console.log(JSON.parse(JSON.stringify(form)));
EleMessage.success('数据已打印在控制台'); EleMessage.success('数据已打印在控制台');
}); });
}; };

2
src/views/extension/upload/components/demo-form1.vue

@ -144,7 +144,7 @@
idCardImg1: form.idCardImg1.map((d) => d.url).join(), idCardImg1: form.idCardImg1.map((d) => d.url).join(),
idCardImg2: form.idCardImg2.map((d) => d.url).join() idCardImg2: form.idCardImg2.map((d) => d.url).join()
}; };
console.log(JSON.stringify(data)); // dataform console.log(JSON.parse(JSON.stringify(data))); // dataform
EleMessage.success('数据已打印在控制台'); EleMessage.success('数据已打印在控制台');
}); });
}; };

2
src/views/extension/upload/components/demo-form2.vue

@ -94,7 +94,7 @@
// //
images: JSON.stringify(form.images.map((d) => d.url)) images: JSON.stringify(form.images.map((d) => d.url))
}; };
console.log(JSON.stringify(data)); // dataform console.log(JSON.parse(JSON.stringify(data))); // dataform
EleMessage.success('数据已打印在控制台'); EleMessage.success('数据已打印在控制台');
}); });
}; };

4
src/views/extension/upload/components/demo-multiple.vue

@ -41,7 +41,7 @@
/** 获取当前数据 */ /** 获取当前数据 */
const getData = () => { const getData = () => {
console.log(JSON.stringify(images.value)); console.log(JSON.parse(JSON.stringify(images.value)));
EleMessage.success('数据已打印在控制台'); EleMessage.success('数据已打印在控制台');
}; };
@ -60,7 +60,7 @@
} }
images.value.push({ ...d }); images.value.push({ ...d });
const item = images.value.find((t) => t.key === d.key); const item = images.value.find((t) => t.key === d.key);
console.log(JSON.stringify(item)); console.log(JSON.parse(JSON.stringify(item)));
if (!item) { if (!item) {
return; return;
} }

6
src/views/form/basic/index.vue

@ -95,9 +95,9 @@
</el-form-item> </el-form-item>
<el-form-item label="目标公开" style="margin-bottom: 0"> <el-form-item label="目标公开" style="margin-bottom: 0">
<el-radio-group v-model="form.publicType"> <el-radio-group v-model="form.publicType">
<el-radio :label="1">公开</el-radio> <el-radio :value="1" label="公开" />
<el-radio :label="2">部分公开</el-radio> <el-radio :value="2" label="部分公开" />
<el-radio :label="3">不公开</el-radio> <el-radio :value="3" label="不公开" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item

4
src/views/form/build/components/code-preview.vue

@ -217,6 +217,10 @@
user-select: none; user-select: none;
position: sticky; position: sticky;
left: 0; left: 0;
& > div {
max-height: 100%;
}
} }
.code-header { .code-header {

2
src/views/form/build/components/form-preview.vue

@ -67,7 +67,7 @@
/** 提交 */ /** 提交 */
const onSubmit = () => { const onSubmit = () => {
console.log('form:', JSON.stringify(form)); console.log('form:', JSON.parse(JSON.stringify(form)));
}; };
/** 回显缓存数据 */ /** 回显缓存数据 */

4
src/views/form/build/components/item-edit.vue

@ -33,8 +33,8 @@
</el-form-item> </el-form-item>
<el-form-item label="是否必填" prop="required"> <el-form-item label="是否必填" prop="required">
<el-radio-group v-model="form.required"> <el-radio-group v-model="form.required">
<el-radio :label="true"></el-radio> <el-radio :value="true" label="是" />
<el-radio :label="false"></el-radio> <el-radio :value="false" label="否" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item prop="itemProps"> <el-form-item prop="itemProps">

11
src/views/form/build/components/util.ts

@ -15,6 +15,7 @@ export const proTemplate = `<template>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue';
import { useFormData } from '@/utils/use-form-data'; import { useFormData } from '@/utils/use-form-data';
import ProForm from '@/components/ProForm/index.vue'; import ProForm from '@/components/ProForm/index.vue';
@ -207,19 +208,19 @@ export const itemTemplate = `
</el-select><%# }else if(d.type=='radio'){ %><el-radio-group </el-select><%# }else if(d.type=='radio'){ %><el-radio-group
v-model="form.<% d.prop %>" v-model="form.<% d.prop %>"
><%# (d.options||[]).forEach(function(opt){ %> ><%# (d.options||[]).forEach(function(opt){ %>
<el-radio label="<% opt.value %>"><% opt.label %></el-radio><%# }); %> <el-radio value="<% opt.value %>" label="<% opt.label %>" /><%# }); %>
</el-radio-group><%# }else if(d.type=='radioButton'){ %><el-radio-group </el-radio-group><%# }else if(d.type=='radioButton'){ %><el-radio-group
v-model="form.<% d.prop %>" v-model="form.<% d.prop %>"
><%# (d.options||[]).forEach(function(opt){ %> ><%# (d.options||[]).forEach(function(opt){ %>
<el-radio-button label="<% opt.value %>"><% opt.label %></el-radio-button><%# }); %> <el-radio-button value="<% opt.value %>" label="<% opt.label %>" /><%# }); %>
</el-radio-group><%# }else if(d.type=='checkbox'){ %><el-checkbox-group </el-radio-group><%# }else if(d.type=='checkbox'){ %><el-checkbox-group
v-model="form.<% d.prop %>" v-model="form.<% d.prop %>"
><%# (d.options||[]).forEach(function(opt){ %> ><%# (d.options||[]).forEach(function(opt){ %>
<el-checkbox label="<% opt.value %>"><% opt.label %></el-checkbox><%# }); %> <el-checkbox value="<% opt.value %>" label="<% opt.label %>" /><%# }); %>
</el-checkbox-group><%# }else if(d.type=='checkboxButton'){ %><el-checkbox-group </el-checkbox-group><%# }else if(d.type=='checkboxButton'){ %><el-checkbox-group
v-model="form.<% d.prop %>" v-model="form.<% d.prop %>"
><%# (d.options||[]).forEach(function(opt){ %> ><%# (d.options||[]).forEach(function(opt){ %>
<el-checkbox-button label="<% opt.value %>"><% opt.label %></el-checkbox-button><%# }); %> <el-checkbox-button value="<% opt.value %>" label="<% opt.label %>" /><%# }); %>
</el-checkbox-group><%# }else if(d.type=='date'){ %><el-date-picker </el-checkbox-group><%# }else if(d.type=='date'){ %><el-date-picker
v-model="form.<% d.prop %>" v-model="form.<% d.prop %>"
placeholder="请选择<% d.label %>" placeholder="请选择<% d.label %>"
@ -505,7 +506,7 @@ export function templateEngine(template: string, data = {}) {
return new Function( return new Function(
'd, _escape_', 'd, _escape_',
'"use strict";var view = "' + result + '";return view;' '"use strict";var view = "' + result + '";return view;'
)(data, (str) => { )(data, (str: string) => {
return String(str || '') return String(str || '')
.replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;') .replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;')
.replace(/</g, '&lt;') .replace(/</g, '&lt;')

2
src/views/form/build/index.vue

@ -71,8 +71,8 @@
<thead style="position: sticky; top: 0; z-index: 2"> <thead style="position: sticky; top: 0; z-index: 2">
<tr> <tr>
<th style="width: 38px"></th> <th style="width: 38px"></th>
<th style="text-align: center">表单项</th>
<th style="text-align: center">字段名</th> <th style="text-align: center">字段名</th>
<th style="text-align: center">标题</th>
<th style="width: 98px"></th> <th style="width: 98px"></th>
</tr> </tr>
</thead> </thead>

13
src/views/form/step/components/step-edit.vue

@ -34,7 +34,7 @@
<el-form-item label="转账金额" prop="amount"> <el-form-item label="转账金额" prop="amount">
<el-input <el-input
clearable clearable
v-model.number="form.amount" v-model="form.amount"
placeholder="请输入转账金额" placeholder="请输入转账金额"
:prefix-icon="prefixIcon" :prefix-icon="prefixIcon"
/> />
@ -50,6 +50,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, h } from 'vue'; import { ref, reactive, h } from 'vue';
import type { FormInstance, FormRules } from 'element-plus'; import type { FormInstance, FormRules } from 'element-plus';
import { numberReg } from 'ele-admin-plus/es';
import { useFormData } from '@/utils/use-form-data'; import { useFormData } from '@/utils/use-form-data';
import type { StepForm } from '../model'; import type { StepForm } from '../model';
@ -71,7 +72,7 @@
receiver: 'test@example.com', receiver: 'test@example.com',
pay: 'alipay', pay: 'alipay',
name: 'Alex', name: 'Alex',
amount: 500 amount: '500'
}); });
/** 表单验证规则 */ /** 表单验证规则 */
@ -103,8 +104,14 @@
amount: [ amount: [
{ {
required: true, required: true,
message: '请输入转账金额',
type: 'string',
trigger: 'blur'
},
{
pattern: numberReg,
message: '请输入合法金额数字', message: '请输入合法金额数字',
type: 'number', type: 'string',
trigger: 'blur' trigger: 'blur'
} }
] ]

2
src/views/form/step/model/index.ts

@ -3,5 +3,5 @@ export interface StepForm {
receiver?: string; receiver?: string;
pay?: string; pay?: string;
name?: string; name?: string;
amount?: number; amount?: string;
} }

11
src/views/list/advanced/index.vue

@ -28,9 +28,9 @@
<template #tools> <template #tools>
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<el-radio-group v-model="where.status" @change="query"> <el-radio-group v-model="where.status" @change="query">
<el-radio-button label="0">全部</el-radio-button> <el-radio-button value="0" label="全部" />
<el-radio-button label="1">进行中</el-radio-button> <el-radio-button value="1" label="进行中" />
<el-radio-button label="2">已完成</el-radio-button> <el-radio-button value="2" label="已完成" />
</el-radio-group> </el-radio-group>
<div style="width: 200px; margin-left: 12px" class="hidden-xs-only"> <div style="width: 200px; margin-left: 12px" class="hidden-xs-only">
<el-input v-model="where.keyword" placeholder="请输入"> <el-input v-model="where.keyword" placeholder="请输入">
@ -107,7 +107,8 @@
:total="count" :total="count"
v-model:page-size="limit" v-model:page-size="limit"
v-model:current-page="page" v-model:current-page="page"
layout="prev, pager, next, jumper" layout="prev, pager, next, sizes, jumper"
:pageSizes="[5, 10, 20]"
style="margin-top: 18px; justify-content: center" style="margin-top: 18px; justify-content: center"
/> />
</ele-card> </ele-card>
@ -267,7 +268,7 @@
loading.value = true; loading.value = true;
setTimeout(() => { setTimeout(() => {
loading.value = false; loading.value = false;
count.value = 25; count.value = 20;
data.value = [ data.value = [
{ {
id: 1, id: 1,

6
src/views/list/basic/components/nickname-filter.vue

@ -86,4 +86,10 @@
background: var(--ele-table-icon-hover-bg); background: var(--ele-table-icon-hover-bg);
} }
} }
@media print {
.demo-filter-icon {
display: none;
}
}
</style> </style>

73
src/views/list/basic/index.vue

@ -1,8 +1,15 @@
<template> <template>
<ele-page hide-footer :flex-table="fixedHeight" style="min-height: 600px"> <ele-page :flex-table="fixedHeight">
<!-- 搜索表单 --> <!-- 搜索表单 -->
<search-form @search="onSearch" /> <search-form @search="onSearch" />
<ele-card :flex-table="fixedHeight" :body-style="{ paddingBottom: 0 }"> <ele-card
:flex-table="fixedHeight"
:body-style="{ paddingBottom: '4px' }"
:style="{
minHeight: fixedHeight ? '380px' : void 0,
marginBottom: fixedHeight ? '0px' : void 0
}"
>
<!-- 提示信息 --> <!-- 提示信息 -->
<ele-alert <ele-alert
show-icon show-icon
@ -36,12 +43,14 @@
v-model:current="current" v-model:current="current"
v-model:selections="selections" v-model:selections="selections"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
highlight-current-row :export-config="{ fileName: '基础列表数据', datasource: exportSource }"
row-click-checked :print-config="{ datasource: exportSource }"
:border="bordered" :border="bordered"
:sticky="!fixedHeight"
:toolbar="{ theme: toolDefault ? 'default' : 'plain' }" :toolbar="{ theme: toolDefault ? 'default' : 'plain' }"
:default-sort="{ prop: 'createTime', order: 'ascending' }" :default-sort="{ prop: 'createTime', order: 'ascending' }"
:footer-style="{ paddingBottom: '20px' }" :footer-style="{ paddingBottom: '12px' }"
style="padding-bottom: 0"
@done="onDone" @done="onDone"
> >
<template #toolbar> <template #toolbar>
@ -169,6 +178,16 @@
<nickname-filter @search="onNicknameFilter" /> <nickname-filter @search="onNicknameFilter" />
</div> </div>
</template> </template>
<!-- 打印增加额外内容 -->
<template #printTop>
<h2 style="text-align: center">还可以自定义打印的顶部区域</h2>
</template>
<template #printBottom="{ data }">
<h2 style="text-align: center">还可以自定义打印的底部区域</h2>
<div style="text-align: center">
共打印了 <b style="color: red">{{ data.length }}</b> 条数据
</div>
</template>
</ele-pro-table> </ele-pro-table>
</ele-card> </ele-card>
</ele-page> </ele-page>
@ -198,7 +217,7 @@
import SearchForm from './components/search-form.vue'; import SearchForm from './components/search-form.vue';
import NicknameFilter from './components/nickname-filter.vue'; import NicknameFilter from './components/nickname-filter.vue';
import type { User, UserParam } from '@/api/system/user/model'; import type { User, UserParam } from '@/api/system/user/model';
import { pageUsers } from '@/api/system/user'; import { pageUsers, listUsers } from '@/api/system/user';
const { t } = useI18n(); const { t } = useI18n();
const { push } = useRouter(); const { push } = useRouter();
@ -255,6 +274,14 @@
headerSlot: 'nicknameHeader' headerSlot: 'nicknameHeader'
}, },
{ {
columnKey: 'roles',
label: t('list.basic.table.roles'),
minWidth: 110,
slot: 'roles',
hideInTable: true,
formatter: (row) => row.roles.map((d: any) => d.roleName).join(',')
},
{
prop: 'organizationName', prop: 'organizationName',
label: t('list.basic.table.organizationName'), label: t('list.basic.table.organizationName'),
minWidth: 110, minWidth: 110,
@ -264,19 +291,14 @@
prop: 'phone', prop: 'phone',
label: t('list.basic.table.phone'), label: t('list.basic.table.phone'),
minWidth: 110, minWidth: 110,
sortable: 'custom' sortable: 'custom',
hideInTable: true
}, },
{ {
prop: 'email', prop: 'email',
label: t('list.basic.table.email'), label: t('list.basic.table.email'),
minWidth: 110, minWidth: 110,
sortable: 'custom' sortable: 'custom',
},
{
columnKey: 'roles',
label: t('list.basic.table.roles'),
minWidth: 110,
slot: 'roles',
hideInTable: true hideInTable: true
}, },
{ {
@ -308,7 +330,8 @@
{ text: '正常', value: '0' }, { text: '正常', value: '0' },
{ text: '冻结', value: '1' } { text: '冻结', value: '1' }
], ],
filterMultiple: false filterMultiple: false,
formatter: (row) => (row.status == 0 ? '正常' : '冻结')
}, },
{ {
columnKey: 'action', columnKey: 'action',
@ -318,7 +341,9 @@
showOverflowTooltip: false, showOverflowTooltip: false,
resizable: false, resizable: false,
slot: 'action', slot: 'action',
fixed: 'right' fixed: 'right',
hideInPrint: true,
hideInExport: true
} }
]; ];
}); });
@ -330,23 +355,22 @@
const current = ref<User | null>(null); const current = ref<User | null>(null);
/** 表格是否显示边框 */ /** 表格是否显示边框 */
const bordered = ref(false); const bordered = ref(true);
/** 表头工具栏风格 */ /** 表头工具栏风格 */
const toolDefault = ref(false); const toolDefault = ref(true);
/** 表格固定高度 */ /** 表格固定高度 */
const fixedHeight = ref(true); const fixedHeight = ref(false);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ const datasource: DatasourceFunction = ({
page, pages,
limit,
where, where,
orders, orders,
filters filters
}) => { }) => {
return pageUsers({ ...where, ...orders, ...filters, page, limit }); return pageUsers({ ...where, ...orders, ...filters, ...pages });
}; };
/** 表格数据请求完成事件 */ /** 表格数据请求完成事件 */
@ -423,6 +447,11 @@
nicknameFilterValue.value = nickname; nicknameFilterValue.value = nickname;
doReload(); doReload();
}; };
/** 导出和打印全部数据的数据源 */
const exportSource: DatasourceFunction = ({ where, orders, filters }) => {
return listUsers({ ...where, ...orders, ...filters });
};
</script> </script>
<script lang="ts"> <script lang="ts">

54
src/views/list/card/article/components/img-grid.vue

@ -0,0 +1,54 @@
<template>
<div class="demo-img-grid">
<el-image
v-for="(item, index) in images"
:key="item"
fit="cover"
:src="item"
style="width: 100%; height: 100%; border-radius: 4px; cursor: zoom-in"
@click="openPreview(index)"
/>
<div style="display: none">
<el-image
ref="previewRef"
:src="images[0]"
:preview-src-list="images"
:initial-index="previewIndex"
:hide-on-click-modal="true"
:preview-teleported="true"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import type { ElImageInstance } from 'ele-admin-plus/es/ele-app/el';
defineProps<{
images: string[];
}>();
const previewRef = ref<ElImageInstance>(null);
const previewIndex = ref(0);
const openPreview = (index: number) => {
previewIndex.value = index;
previewRef.value?.$el?.querySelector?.('img')?.click?.();
};
</script>
<style lang="scss" scoped>
.demo-img-grid {
display: grid;
grid-gap: 6px 6px;
grid-template-columns: repeat(3, 1fr);
height: 100%;
padding: 8px;
box-sizing: border-box;
border-radius: 6px;
background: #eee;
background: var(--el-fill-color-light);
}
</style>

93
src/views/list/card/article/index.vue

@ -3,7 +3,7 @@
<top-search /> <top-search />
<ele-card :body-style="{ padding: '16px 8px' }"> <ele-card :body-style="{ padding: '16px 8px' }">
<div class="article-list"> <div class="article-list">
<div v-for="(item, index) in data" :key="item.id" class="list-item"> <div v-for="item in data" :key="item.id" class="list-item">
<div class="list-item-body"> <div class="list-item-body">
<div style="flex: 1"> <div style="flex: 1">
<ele-text size="md">{{ item.title }}</ele-text> <ele-text size="md">{{ item.title }}</ele-text>
@ -53,15 +53,7 @@
</div> </div>
</div> </div>
<div class="list-item-extra"> <div class="list-item-extra">
<el-image <img-grid :images="item.covers" />
fit="cover"
:src="item.cover"
:preview-src-list="previewList"
:initial-index="index"
:hide-on-click-modal="true"
:preview-teleported="true"
style="width: 100%; height: 100%"
/>
</div> </div>
</div> </div>
<el-divider style="margin: 0; opacity: 0.6" /> <el-divider style="margin: 0; opacity: 0.6" />
@ -77,16 +69,17 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from 'vue'; import { ref } from 'vue';
import { Star, CollectionTag, ChatLineSquare } from '@element-plus/icons-vue'; import { Star, CollectionTag, ChatLineSquare } from '@element-plus/icons-vue';
import TopSearch from '../project/components/top-search.vue'; import TopSearch from '../project/components/top-search.vue';
import ImgGrid from './components/img-grid.vue';
interface Item { interface Item {
id: number; id: number;
title: string; title: string;
content: string; content: string;
time: string; time: string;
cover: string; covers: string[];
tags: string[]; tags: string[];
user: string; user: string;
avatar: string; avatar: string;
@ -101,16 +94,16 @@
const page = ref(2); const page = ref(2);
const previewList = computed(() => {
return data.value.map((item) => item.cover);
});
const query = () => { const query = () => {
loading.value = true; loading.value = true;
setTimeout(() => { setTimeout(() => {
loading.value = false; loading.value = false;
page.value++; page.value++;
data.value = data.value.concat(data.value.slice(0, 3)); data.value = data.value.concat(
data.value.slice(0, 3).map((d, i) => {
return { ...d, id: data.value.length + i + 1 };
})
);
}, 1000); }, 1000);
}; };
@ -121,8 +114,14 @@
content: content:
'Element, 一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库, 提供了配套设计资源, 帮助你的网站快速成型。', 'Element, 一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库, 提供了配套设计资源, 帮助你的网站快速成型。',
time: '2 小时前', time: '2 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/RZ8FQmZfHkcffMlTBCJllBFjEhEsObVo.jpg', 'https://cdn.eleadmin.com/20200610/RZ8FQmZfHkcffMlTBCJllBFjEhEsObVo.jpg',
'https://cdn.eleadmin.com/20200610/WLXm7gp1EbLDtvVQgkeQeyq5OtDm00Jd.jpg',
'https://cdn.eleadmin.com/20200610/4Z0QR2L0J1XStxBh99jVJ8qLfsGsOgjU.jpg',
'https://cdn.eleadmin.com/20200610/ttkIjNPlVDuv4lUTvRX8GIlM2QqSe0jg.jpg',
'https://cdn.eleadmin.com/20200610/fAenQ8nvRjL7x0i0jEfuDBZHvJfHf3v6.jpg',
'https://cdn.eleadmin.com/20200610/LrCTN2j94lo9N7wEql7cBr1Ux4rHMvmZ.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: 'SunSmile', user: 'SunSmile',
avatar: avatar:
@ -137,8 +136,14 @@
content: content:
'Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是, Vue 被设计为可以自底向上逐层应用。', 'Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是, Vue 被设计为可以自底向上逐层应用。',
time: '4 小时前', time: '4 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/WLXm7gp1EbLDtvVQgkeQeyq5OtDm00Jd.jpg', 'https://cdn.eleadmin.com/20200610/WLXm7gp1EbLDtvVQgkeQeyq5OtDm00Jd.jpg',
'https://cdn.eleadmin.com/20200610/4Z0QR2L0J1XStxBh99jVJ8qLfsGsOgjU.jpg',
'https://cdn.eleadmin.com/20200610/ttkIjNPlVDuv4lUTvRX8GIlM2QqSe0jg.jpg',
'https://cdn.eleadmin.com/20200610/fAenQ8nvRjL7x0i0jEfuDBZHvJfHf3v6.jpg',
'https://cdn.eleadmin.com/20200610/LrCTN2j94lo9N7wEql7cBr1Ux4rHMvmZ.jpg',
'https://cdn.eleadmin.com/20200610/yeKvhT20lMU0f1T3Y743UlGEOLLnZSnp.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: '你的名字很好听', user: '你的名字很好听',
avatar: avatar:
@ -153,8 +158,14 @@
content: content:
'Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化。', 'Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化。',
time: '12 小时前', time: '12 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/4Z0QR2L0J1XStxBh99jVJ8qLfsGsOgjU.jpg', 'https://cdn.eleadmin.com/20200610/4Z0QR2L0J1XStxBh99jVJ8qLfsGsOgjU.jpg',
'https://cdn.eleadmin.com/20200610/ttkIjNPlVDuv4lUTvRX8GIlM2QqSe0jg.jpg',
'https://cdn.eleadmin.com/20200610/fAenQ8nvRjL7x0i0jEfuDBZHvJfHf3v6.jpg',
'https://cdn.eleadmin.com/20200610/LrCTN2j94lo9N7wEql7cBr1Ux4rHMvmZ.jpg',
'https://cdn.eleadmin.com/20200610/yeKvhT20lMU0f1T3Y743UlGEOLLnZSnp.jpg',
'https://cdn.eleadmin.com/20200610/CyrCNmTJfv7D6GFAg39bjT3eRkkRm5dI.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: '全村人的希望', user: '全村人的希望',
avatar: avatar:
@ -169,8 +180,14 @@
content: content:
'Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成, 让构建单页面应用变得易如反掌。', 'Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成, 让构建单页面应用变得易如反掌。',
time: '14 小时前', time: '14 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/ttkIjNPlVDuv4lUTvRX8GIlM2QqSe0jg.jpg', 'https://cdn.eleadmin.com/20200610/ttkIjNPlVDuv4lUTvRX8GIlM2QqSe0jg.jpg',
'https://cdn.eleadmin.com/20200610/fAenQ8nvRjL7x0i0jEfuDBZHvJfHf3v6.jpg',
'https://cdn.eleadmin.com/20200610/LrCTN2j94lo9N7wEql7cBr1Ux4rHMvmZ.jpg',
'https://cdn.eleadmin.com/20200610/yeKvhT20lMU0f1T3Y743UlGEOLLnZSnp.jpg',
'https://cdn.eleadmin.com/20200610/CyrCNmTJfv7D6GFAg39bjT3eRkkRm5dI.jpg',
'https://cdn.eleadmin.com/20200610/RZ8FQmZfHkcffMlTBCJllBFjEhEsObVo.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: 'Jasmine', user: 'Jasmine',
avatar: avatar:
@ -184,8 +201,14 @@
title: 'Sass', title: 'Sass',
content: 'Sass 是世界上最成熟、稳定、强大的专业级 CSS 扩展语言。', content: 'Sass 是世界上最成熟、稳定、强大的专业级 CSS 扩展语言。',
time: '10 小时前', time: '10 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/fAenQ8nvRjL7x0i0jEfuDBZHvJfHf3v6.jpg', 'https://cdn.eleadmin.com/20200610/fAenQ8nvRjL7x0i0jEfuDBZHvJfHf3v6.jpg',
'https://cdn.eleadmin.com/20200610/LrCTN2j94lo9N7wEql7cBr1Ux4rHMvmZ.jpg',
'https://cdn.eleadmin.com/20200610/yeKvhT20lMU0f1T3Y743UlGEOLLnZSnp.jpg',
'https://cdn.eleadmin.com/20200610/CyrCNmTJfv7D6GFAg39bjT3eRkkRm5dI.jpg',
'https://cdn.eleadmin.com/20200610/RZ8FQmZfHkcffMlTBCJllBFjEhEsObVo.jpg',
'https://cdn.eleadmin.com/20200610/WLXm7gp1EbLDtvVQgkeQeyq5OtDm00Jd.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: '酷酷的大叔', user: '酷酷的大叔',
avatar: avatar:
@ -200,8 +223,14 @@
content: content:
'Axios 是一个基于 promise 的 HTTP 库, 可以用在浏览器和 node.js 中。', 'Axios 是一个基于 promise 的 HTTP 库, 可以用在浏览器和 node.js 中。',
time: '16 小时前', time: '16 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/LrCTN2j94lo9N7wEql7cBr1Ux4rHMvmZ.jpg', 'https://cdn.eleadmin.com/20200610/LrCTN2j94lo9N7wEql7cBr1Ux4rHMvmZ.jpg',
'https://cdn.eleadmin.com/20200610/yeKvhT20lMU0f1T3Y743UlGEOLLnZSnp.jpg',
'https://cdn.eleadmin.com/20200610/CyrCNmTJfv7D6GFAg39bjT3eRkkRm5dI.jpg',
'https://cdn.eleadmin.com/20200610/RZ8FQmZfHkcffMlTBCJllBFjEhEsObVo.jpg',
'https://cdn.eleadmin.com/20200610/WLXm7gp1EbLDtvVQgkeQeyq5OtDm00Jd.jpg',
'https://cdn.eleadmin.com/20200610/4Z0QR2L0J1XStxBh99jVJ8qLfsGsOgjU.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: 'SunSmile', user: 'SunSmile',
avatar: avatar:
@ -216,8 +245,14 @@
content: content:
'webpack 是一个模块打包器。webpack 的主要目标是将 JavaScript 文件打包在一起, 打包后的文件用于在浏览器中使用。', 'webpack 是一个模块打包器。webpack 的主要目标是将 JavaScript 文件打包在一起, 打包后的文件用于在浏览器中使用。',
time: '6 小时前', time: '6 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/yeKvhT20lMU0f1T3Y743UlGEOLLnZSnp.jpg', 'https://cdn.eleadmin.com/20200610/yeKvhT20lMU0f1T3Y743UlGEOLLnZSnp.jpg',
'https://cdn.eleadmin.com/20200610/CyrCNmTJfv7D6GFAg39bjT3eRkkRm5dI.jpg',
'https://cdn.eleadmin.com/20200610/RZ8FQmZfHkcffMlTBCJllBFjEhEsObVo.jpg',
'https://cdn.eleadmin.com/20200610/WLXm7gp1EbLDtvVQgkeQeyq5OtDm00Jd.jpg',
'https://cdn.eleadmin.com/20200610/4Z0QR2L0J1XStxBh99jVJ8qLfsGsOgjU.jpg',
'https://cdn.eleadmin.com/20200610/ttkIjNPlVDuv4lUTvRX8GIlM2QqSe0jg.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: '全村人的希望', user: '全村人的希望',
avatar: avatar:
@ -232,8 +267,14 @@
content: content:
'Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型, 使其轻量又高效。', 'Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型, 使其轻量又高效。',
time: '8 小时前', time: '8 小时前',
cover: covers: [
'https://cdn.eleadmin.com/20200610/CyrCNmTJfv7D6GFAg39bjT3eRkkRm5dI.jpg', 'https://cdn.eleadmin.com/20200610/CyrCNmTJfv7D6GFAg39bjT3eRkkRm5dI.jpg',
'https://cdn.eleadmin.com/20200610/RZ8FQmZfHkcffMlTBCJllBFjEhEsObVo.jpg',
'https://cdn.eleadmin.com/20200610/WLXm7gp1EbLDtvVQgkeQeyq5OtDm00Jd.jpg',
'https://cdn.eleadmin.com/20200610/4Z0QR2L0J1XStxBh99jVJ8qLfsGsOgjU.jpg',
'https://cdn.eleadmin.com/20200610/ttkIjNPlVDuv4lUTvRX8GIlM2QqSe0jg.jpg',
'https://cdn.eleadmin.com/20200610/fAenQ8nvRjL7x0i0jEfuDBZHvJfHf3v6.jpg'
],
tags: ['EleAdminPro', 'UI框架', '设计语言'], tags: ['EleAdminPro', 'UI框架', '设计语言'],
user: 'Jasmine', user: 'Jasmine',
avatar: avatar:
@ -260,9 +301,7 @@
.list-item-extra { .list-item-extra {
width: 280px; width: 280px;
height: 175px; height: 175px;
border-radius: 6px;
margin-left: 12px; margin-left: 12px;
overflow: hidden;
} }
@media screen and (max-width: 880px) { @media screen and (max-width: 880px) {

4
src/views/login/index.vue

@ -245,7 +245,7 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background-image: url('/src/assets/login-bg.png'); background-image: url('@/assets/login-bg.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
@ -267,7 +267,7 @@
padding: 36px 8px; padding: 36px 8px;
box-sizing: border-box; box-sizing: border-box;
background-color: #1681fd; background-color: #1681fd;
background-image: url('/src/assets/login-img.png'); background-image: url('@/assets/login-img.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: bottom; background-position: bottom;
background-size: contain; background-size: contain;

12
src/views/system/dictionary/components/dict-data-list.vue

@ -11,7 +11,8 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
v-model:selections="selections" v-model:selections="selections"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '字典数据' }"
:footer-style="{ paddingBottom: '16px' }" :footer-style="{ paddingBottom: '16px' }"
cache-key="systemDictDataTable" cache-key="systemDictDataTable"
> >
@ -122,7 +123,9 @@
label: '操作', label: '操作',
width: 130, width: 130,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -136,12 +139,11 @@
const showEdit = ref(false); const showEdit = ref(false);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageDictionaryData({ return pageDictionaryData({
...where, ...where,
...orders, ...orders,
page, ...pages,
limit,
dictId: props.dictId dictId: props.dictId
}); });
}; };

11
src/views/system/file/index.vue

@ -9,7 +9,8 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
v-model:selections="selections" v-model:selections="selections"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '文件数据' }"
cache-key="systemFileTable" cache-key="systemFileTable"
> >
<template #toolbar> <template #toolbar>
@ -149,7 +150,9 @@
label: '操作', label: '操作',
width: 120, width: 120,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -157,8 +160,8 @@
const selections = ref<FileRecord[]>([]); const selections = ref<FileRecord[]>([]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageFiles({ ...where, ...orders, page, limit }); return pageFiles({ ...where, ...orders, ...pages });
}; };
/** 搜索 */ /** 搜索 */

14
src/views/system/login-record/index.vue

@ -8,7 +8,8 @@
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '登录日志数据' }"
:where="defaultWhere" :where="defaultWhere"
cache-key="systemLoginRecordTable" cache-key="systemLoginRecordTable"
> >
@ -174,19 +175,12 @@
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ const datasource: DatasourceFunction = ({
page, pages,
limit,
where, where,
orders, orders,
filters filters
}) => { }) => {
return pageLoginRecords({ return pageLoginRecords({ ...where, ...orders, ...filters, ...pages });
...where,
...orders,
...filters,
page,
limit
});
}; };
/** 刷新表格 */ /** 刷新表格 */

2
src/views/system/menu/components/icon-select.vue

@ -2,7 +2,7 @@
<template> <template>
<ele-icon-select <ele-icon-select
clearable clearable
filterable filterable="popper"
:data="iconData" :data="iconData"
:model-value="modelValue" :model-value="modelValue"
:placeholder="placeholder" :placeholder="placeholder"

12
src/views/system/menu/components/menu-edit.vue

@ -31,9 +31,9 @@
<el-col :sm="12" :xs="24"> <el-col :sm="12" :xs="24">
<el-form-item label="菜单类型" prop="menuType"> <el-form-item label="菜单类型" prop="menuType">
<el-radio-group v-model="form.menuType" @change="onMenuTypeChange"> <el-radio-group v-model="form.menuType" @change="onMenuTypeChange">
<el-radio :label="0">目录</el-radio> <el-radio :value="0" label="目录" />
<el-radio :label="1">菜单</el-radio> <el-radio :value="1" label="菜单" />
<el-radio :label="2">按钮</el-radio> <el-radio :value="2" label="按钮" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="打开方式"> <el-form-item label="打开方式">
@ -42,9 +42,9 @@
:disabled="form.menuType === 0 || form.menuType === 2" :disabled="form.menuType === 0 || form.menuType === 2"
@change="onOpenTypeChange" @change="onOpenTypeChange"
> >
<el-radio :label="0">组件</el-radio> <el-radio :value="0" label="组件" />
<el-radio :label="1">内链</el-radio> <el-radio :value="1" label="内链" />
<el-radio :label="2">外链</el-radio> <el-radio :value="2" label="外链" />
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>

7
src/views/system/menu/index.vue

@ -9,7 +9,8 @@
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '菜单数据' }"
:default-expand-all="false" :default-expand-all="false"
:pagination="false" :pagination="false"
cache-key="systemMenuTable" cache-key="systemMenuTable"
@ -184,7 +185,9 @@
label: '操作', label: '操作',
width: 180, width: 180,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);

78
src/views/system/operation-record/components/operation-record-detail.vue

@ -58,65 +58,17 @@
<div style="word-break: break-all">{{ data.method }}</div> <div style="word-break: break-all">{{ data.method }}</div>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="请求参数" :span="2"> <el-descriptions-item label="请求参数" :span="2">
<ele-ellipsis <ele-ellipsis :max-line="4" :tooltip="ellipsisTooltipProps">
:max-line="4"
:tooltip="{
popperStyle: {
width: '580px',
maxWidth: '90%',
wordBreak: 'break-all'
},
bodyStyle: {
maxWidth: 'calc(100vw - 32px)',
maxHeight: '252px',
overflowY: 'auto'
},
offset: 4,
placement: 'top'
}"
>
{{ data.params }} {{ data.params }}
</ele-ellipsis> </ele-ellipsis>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item v-if="data.status === 0" label="返回结果" :span="2"> <el-descriptions-item v-if="data.status === 0" label="返回结果" :span="2">
<ele-ellipsis <ele-ellipsis :max-line="4" :tooltip="ellipsisTooltipProps">
:max-line="4"
:tooltip="{
popperStyle: {
width: '580px',
maxWidth: '90%',
wordBreak: 'break-all'
},
bodyStyle: {
maxWidth: 'calc(100vw - 32px)',
maxHeight: '252px',
overflowY: 'auto'
},
offset: 4,
placement: 'top'
}"
>
{{ data.result }} {{ data.result }}
</ele-ellipsis> </ele-ellipsis>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item v-else label="异常信息" :span="2"> <el-descriptions-item v-else label="异常信息" :span="2">
<ele-ellipsis <ele-ellipsis :max-line="4" :tooltip="ellipsisTooltipProps">
:max-line="4"
:tooltip="{
popperStyle: {
width: '580px',
maxWidth: '90%',
wordBreak: 'break-all'
},
bodyStyle: {
maxWidth: 'calc(100vw - 32px)',
maxHeight: '252px',
overflowY: 'auto'
},
offset: 4,
placement: 'top'
}"
>
{{ data.error }} {{ data.error }}
</ele-ellipsis> </ele-ellipsis>
</el-descriptions-item> </el-descriptions-item>
@ -125,8 +77,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useMobile } from '@/utils/use-mobile'; import { reactive } from 'vue';
import type { EleTooltipProps } from 'ele-admin-plus/es/ele-app/plus';
import type { OperationRecord } from '@/api/system/operation-record/model'; import type { OperationRecord } from '@/api/system/operation-record/model';
import { useMobile } from '@/utils/use-mobile';
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', modelValue: boolean): void; (e: 'update:modelValue', modelValue: boolean): void;
@ -144,6 +98,26 @@
emit('update:modelValue', value); emit('update:modelValue', value);
}; };
/** 文字省略组件的提示组件的属性 */
const ellipsisTooltipProps = reactive<EleTooltipProps>({
popperStyle: {
width: '580px',
maxWidth: '90%',
wordBreak: 'break-all'
},
bodyStyle: {
maxWidth: 'calc(100vw - 32px)',
maxHeight: '252px',
overflowY: 'auto',
'--ele-scrollbar-color': '#5e5e5e',
'--ele-scrollbar-hover-color': '#707070',
'--ele-scrollbar-size': '8px'
},
offset: 4,
placement: 'top'
});
/** 是否是移动端 */
const { mobile } = useMobile(); const { mobile } = useMobile();
</script> </script>

13
src/views/system/operation-record/index.vue

@ -8,7 +8,8 @@
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '操作日志数据' }"
cache-key="systemOperationRecordTable" cache-key="systemOperationRecordTable"
> >
<template #toolbar> <template #toolbar>
@ -160,7 +161,9 @@
width: 90, width: 90,
align: 'center', align: 'center',
slot: 'action', slot: 'action',
fixed: 'right' fixed: 'right',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -190,8 +193,7 @@
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ const datasource: DatasourceFunction = ({
page, pages,
limit,
where, where,
orders, orders,
filters filters
@ -200,8 +202,7 @@
...where, ...where,
...orders, ...orders,
...filters, ...filters,
page, ...pages
limit
}); });
}; };

20
src/views/system/organization/components/org-user-edit.vue

@ -25,7 +25,6 @@
:maxlength="20" :maxlength="20"
v-model="form.username" v-model="form.username"
placeholder="请输入用户账号" placeholder="请输入用户账号"
:disabled="isUpdate"
/> />
</el-form-item> </el-form-item>
<el-form-item label="用户名" prop="nickname"> <el-form-item label="用户名" prop="nickname">
@ -142,12 +141,20 @@
username: [ username: [
{ {
required: true, required: true,
message: '请输入用户账号',
type: 'string',
trigger: 'blur'
},
{
min: 4,
message: '账号长度最少为4位',
type: 'string',
trigger: 'blur'
},
{
type: 'string', type: 'string',
trigger: 'blur', trigger: 'blur',
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string, callback: any) => {
if (!value) {
return callback(new Error('请输入用户账号'));
}
checkExistence('username', value, props.data?.userId) checkExistence('username', value, props.data?.userId)
.then(() => { .then(() => {
callback(new Error('账号已经存在')); callback(new Error('账号已经存在'));
@ -193,6 +200,11 @@
password: [ password: [
{ {
required: true, required: true,
message: '请输入登录密码',
type: 'string',
trigger: 'blur'
},
{
type: 'string', type: 'string',
trigger: 'blur', trigger: 'blur',
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string, callback: any) => {

12
src/views/system/organization/components/org-user-list.vue

@ -11,7 +11,8 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
v-model:selections="selections" v-model:selections="selections"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '用户数据' }"
:footer-style="{ paddingBottom: '16px' }" :footer-style="{ paddingBottom: '16px' }"
cache-key="systemOrgUserTable" cache-key="systemOrgUserTable"
> >
@ -170,7 +171,9 @@
label: '操作', label: '操作',
width: 128, width: 128,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -184,12 +187,11 @@
const showEdit = ref(false); const showEdit = ref(false);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageUsers({ return pageUsers({
...where, ...where,
...orders, ...orders,
page, ...pages,
limit,
organizationId: props.organizationId organizationId: props.organizationId
}); });
}; };

11
src/views/system/role/index.vue

@ -11,7 +11,8 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
v-model:selections="selections" v-model:selections="selections"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '角色数据' }"
cache-key="systemRoleTable" cache-key="systemRoleTable"
> >
<template #toolbar> <template #toolbar>
@ -118,7 +119,9 @@
label: '操作', label: '操作',
width: 200, width: 200,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -135,8 +138,8 @@
const showAuth = ref(false); const showAuth = ref(false);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageRoles({ ...where, ...orders, page, limit }); return pageRoles({ ...where, ...orders, ...pages });
}; };
/** 搜索 */ /** 搜索 */

11
src/views/system/user/components/user-edit.vue

@ -158,7 +158,11 @@
type: 'string', type: 'string',
trigger: 'blur', trigger: 'blur',
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string, callback: any) => {
checkExistence('username', value, props.data?.userId) if (isUpdate.value) {
callback();
return;
}
checkExistence('username', value)
.then(() => { .then(() => {
callback(new Error('账号已经存在')); callback(new Error('账号已经存在'));
}) })
@ -266,10 +270,7 @@
(modelValue) => { (modelValue) => {
if (modelValue) { if (modelValue) {
if (props.data) { if (props.data) {
assignFields({ assignFields({ ...props.data, password: '' });
...props.data,
password: ''
});
isUpdate.value = true; isUpdate.value = true;
} else { } else {
isUpdate.value = false; isUpdate.value = false;

13
src/views/user/message/components/message-letter.vue

@ -7,7 +7,8 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
v-model:selections="selections" v-model:selections="selections"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '用户私信数据' }"
> >
<template #toolbar> <template #toolbar>
<el-button type="primary" class="ele-btn-icon" @click="readBatch"> <el-button type="primary" class="ele-btn-icon" @click="readBatch">
@ -91,7 +92,9 @@
label: '操作', label: '操作',
width: 120, width: 120,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -99,13 +102,13 @@
const selections = ref<Message[]>([]); const selections = ref<Message[]>([]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageLetters({ ...where, ...orders, page, limit }); return pageLetters({ ...where, ...orders, ...pages });
}; };
/** 回复 */ /** 回复 */
const reply = (row: Message) => { const reply = (row: Message) => {
console.log(JSON.stringify(row)); console.log(JSON.parse(JSON.stringify(row)));
EleMessage.success('点击了回复'); EleMessage.success('点击了回复');
}; };

11
src/views/user/message/components/message-notice.vue

@ -7,7 +7,8 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
v-model:selections="selections" v-model:selections="selections"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '系统通知数据' }"
> >
<template #toolbar> <template #toolbar>
<el-button type="primary" class="ele-btn-icon" @click="confirm()"> <el-button type="primary" class="ele-btn-icon" @click="confirm()">
@ -91,7 +92,9 @@
label: '操作', label: '操作',
width: 120, width: 120,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -99,8 +102,8 @@
const selections = ref<Message[]>([]); const selections = ref<Message[]>([]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageNotices({ ...where, ...orders, page, limit }); return pageNotices({ ...where, ...orders, ...pages });
}; };
/** 确认 */ /** 确认 */

11
src/views/user/message/components/message-todo.vue

@ -7,7 +7,8 @@
:datasource="datasource" :datasource="datasource"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
v-model:selections="selections" v-model:selections="selections"
highlight-current-row :highlight-current-row="true"
:export-config="{ fileName: '待办事项数据' }"
> >
<template #toolbar> <template #toolbar>
<el-button type="primary" class="ele-btn-icon" @click="ok()"> <el-button type="primary" class="ele-btn-icon" @click="ok()">
@ -91,7 +92,9 @@
label: '操作', label: '操作',
width: 120, width: 120,
align: 'center', align: 'center',
slot: 'action' slot: 'action',
hideInPrint: true,
hideInExport: true
} }
]); ]);
@ -99,8 +102,8 @@
const selections = ref<Message[]>([]); const selections = ref<Message[]>([]);
/** 表格数据源 */ /** 表格数据源 */
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { const datasource: DatasourceFunction = ({ pages, where, orders }) => {
return pageTodos({ ...where, ...orders, page, limit }); return pageTodos({ ...where, ...orders, ...pages });
}; };
/** 完成 */ /** 完成 */

2
vite.config.ts

@ -65,7 +65,7 @@ export default defineConfig(({ command }) => {
] ]
}, },
build: { build: {
target: 'es2015', target: 'chrome63',
chunkSizeWarningLimit: 2000 chunkSizeWarningLimit: 2000
} }
}; };

Loading…
Cancel
Save