mirror of
https://github.com/jumpserver/lina.git
synced 2025-09-26 06:58:53 +00:00
feat: table columns support drag
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM jumpserver/lina-base:20250408_074136 AS stage-build
|
||||
FROM jumpserver/lina-base:20250508_085854 AS stage-build
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION=$VERSION
|
||||
|
293
package.json
293
package.json
@@ -1,148 +1,149 @@
|
||||
{
|
||||
"name": "lina",
|
||||
"version": "v4.0.0",
|
||||
"description": "JumpServer Web UI",
|
||||
"author": "JumpServer Team <support@lxware.hk>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"dev": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
|
||||
"serve": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
|
||||
"build": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service build",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"preview": "node build/index.js --preview",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"fix": "eslint --ext .js,.vue --fix src",
|
||||
"test:unit": "jest --clearCache && vue-cli-service test:unit",
|
||||
"test:ci": "npm run lint && npm run test:unit",
|
||||
"svgo": "svgo -f src/icons/svg --config=src/icas/svgo.yml",
|
||||
"vue-i18n-extract": "vue-i18n-extract",
|
||||
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'",
|
||||
"vue-i18n-report-json": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -o /tmp/abc.json",
|
||||
"vue-i18n-report-add-miss": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -a",
|
||||
"diff-i18n": "python ./src/i18n/langs/i18n-util.py diff en ja zh_Hant",
|
||||
"apply-i18n": "python ./src/i18n/langs/i18n-util.py apply en ja zh_Hant"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@fontsource/open-sans": "^5.0.24",
|
||||
"@traptitech/markdown-it-katex": "^3.6.0",
|
||||
"@ztree/ztree_v3": "3.5.44",
|
||||
"axios": "0.28.0",
|
||||
"axios-retry": "^3.1.9",
|
||||
"caniuse-lite": "^1.0.30001642",
|
||||
"cron-parser": "^4.0.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"css-color-function": "^1.3.3",
|
||||
"decimal.js": "^10.4.3",
|
||||
"deepmerge": "^4.2.2",
|
||||
"dompurify": "^3.1.6",
|
||||
"echarts": "4.7.0",
|
||||
"element-ui": "2.15.14",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"install": "^0.13.0",
|
||||
"jquery": "^3.6.1",
|
||||
"js-cookie": "2.2.0",
|
||||
"jsencrypt": "^3.2.1",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.frompairs": "^4.0.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.has": "^4.5.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isempty": "^4.4.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.topairs": "^4.3.0",
|
||||
"lodash.values": "^4.3.0",
|
||||
"markdown-it": "^13.0.2",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"moment-parseformat": "^4.0.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"npm": "^7.8.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "3.3.0",
|
||||
"v-sanitize": "^0.0.13",
|
||||
"vue": "2.6.10",
|
||||
"vue-codemirror": "4.0.6",
|
||||
"vue-cookie": "^1.1.4",
|
||||
"vue-echarts": "^5.0.0-beta.0",
|
||||
"vue-i18n": "^8.15.5",
|
||||
"vue-json-editor": "^1.4.3",
|
||||
"vue-markdown": "^2.2.4",
|
||||
"vue-moment": "^4.1.0",
|
||||
"vue-password-strength-meter": "^1.7.2",
|
||||
"vue-router": "3.0.6",
|
||||
"vue-select": "^3.9.5",
|
||||
"vuejs-logger": "^1.5.4",
|
||||
"vuex": "3.1.0",
|
||||
"watermark-js-plus": "^1.5.8",
|
||||
"xss": "^1.0.14",
|
||||
"xterm": "^4.5.0",
|
||||
"xterm-addon-fit": "^0.3.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.18.6",
|
||||
"@babel/register": "7.0.0",
|
||||
"@vue/cli-plugin-babel": "3.6.0",
|
||||
"@vue/cli-plugin-eslint": "^3.9.1",
|
||||
"@vue/cli-plugin-unit-jest": "3.6.3",
|
||||
"@vue/cli-service": "3.6.0",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-jest": "23.6.0",
|
||||
"chalk": "2.4.2",
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
"connect": "3.6.6",
|
||||
"deasync": "^0.1.29",
|
||||
"element-theme-chalk": "^2.13.1",
|
||||
"eslint": "^5.15.3",
|
||||
"eslint-plugin-vue": "5.2.2",
|
||||
"eslint-plugin-vue-i18n": "^0.3.0",
|
||||
"github-markdown-css": "^5.1.0",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"husky": "^4.2.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lint-staged": "^10.1.2",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"runjs": "^4.3.2",
|
||||
"sass": "~1.32.6",
|
||||
"sass-loader": "^7.1.0",
|
||||
"script-ext-html-webpack-plugin": "2.1.3",
|
||||
"script-loader": "0.7.2",
|
||||
"serve-static": "^1.16.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"svg-sprite-loader": "4.1.3",
|
||||
"svgo": "1.2.2",
|
||||
"vue-i18n-extract": "^1.1.1",
|
||||
"vue-template-compiler": "2.6.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 4 versions",
|
||||
"ie 11"
|
||||
],
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,vue}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
"name": "lina",
|
||||
"version": "v4.0.0",
|
||||
"description": "JumpServer Web UI",
|
||||
"author": "JumpServer Team <support@lxware.hk>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"dev": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
|
||||
"serve": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
|
||||
"build": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service build",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"preview": "node build/index.js --preview",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"fix": "eslint --ext .js,.vue --fix src",
|
||||
"test:unit": "jest --clearCache && vue-cli-service test:unit",
|
||||
"test:ci": "npm run lint && npm run test:unit",
|
||||
"svgo": "svgo -f src/icons/svg --config=src/icas/svgo.yml",
|
||||
"vue-i18n-extract": "vue-i18n-extract",
|
||||
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'",
|
||||
"vue-i18n-report-json": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -o /tmp/abc.json",
|
||||
"vue-i18n-report-add-miss": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -a",
|
||||
"diff-i18n": "python ./src/i18n/langs/i18n-util.py diff en ja zh_Hant",
|
||||
"apply-i18n": "python ./src/i18n/langs/i18n-util.py apply en ja zh_Hant"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@fontsource/open-sans": "^5.0.24",
|
||||
"@traptitech/markdown-it-katex": "^3.6.0",
|
||||
"@ztree/ztree_v3": "3.5.44",
|
||||
"axios": "0.28.0",
|
||||
"axios-retry": "^3.1.9",
|
||||
"caniuse-lite": "^1.0.30001642",
|
||||
"cron-parser": "^4.0.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"css-color-function": "^1.3.3",
|
||||
"decimal.js": "^10.4.3",
|
||||
"deepmerge": "^4.2.2",
|
||||
"dompurify": "^3.1.6",
|
||||
"echarts": "4.7.0",
|
||||
"element-ui": "2.15.14",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"install": "^0.13.0",
|
||||
"jquery": "^3.6.1",
|
||||
"js-cookie": "2.2.0",
|
||||
"jsencrypt": "^3.2.1",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.frompairs": "^4.0.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.has": "^4.5.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isempty": "^4.4.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.topairs": "^4.3.0",
|
||||
"lodash.values": "^4.3.0",
|
||||
"markdown-it": "^13.0.2",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"moment-parseformat": "^4.0.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"npm": "^7.8.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "3.3.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"v-sanitize": "^0.0.13",
|
||||
"vue": "2.6.10",
|
||||
"vue-codemirror": "4.0.6",
|
||||
"vue-cookie": "^1.1.4",
|
||||
"vue-echarts": "^5.0.0-beta.0",
|
||||
"vue-i18n": "^8.15.5",
|
||||
"vue-json-editor": "^1.4.3",
|
||||
"vue-markdown": "^2.2.4",
|
||||
"vue-moment": "^4.1.0",
|
||||
"vue-password-strength-meter": "^1.7.2",
|
||||
"vue-router": "3.0.6",
|
||||
"vue-select": "^3.9.5",
|
||||
"vuejs-logger": "^1.5.4",
|
||||
"vuex": "3.1.0",
|
||||
"watermark-js-plus": "^1.5.8",
|
||||
"xss": "^1.0.14",
|
||||
"xterm": "^4.5.0",
|
||||
"xterm-addon-fit": "^0.3.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.18.6",
|
||||
"@babel/register": "7.0.0",
|
||||
"@vue/cli-plugin-babel": "3.6.0",
|
||||
"@vue/cli-plugin-eslint": "^3.9.1",
|
||||
"@vue/cli-plugin-unit-jest": "3.6.3",
|
||||
"@vue/cli-service": "3.6.0",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-jest": "23.6.0",
|
||||
"chalk": "2.4.2",
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
"connect": "3.6.6",
|
||||
"deasync": "^0.1.29",
|
||||
"element-theme-chalk": "^2.13.1",
|
||||
"eslint": "^5.15.3",
|
||||
"eslint-plugin-vue": "5.2.2",
|
||||
"eslint-plugin-vue-i18n": "^0.3.0",
|
||||
"github-markdown-css": "^5.1.0",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"husky": "^4.2.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lint-staged": "^10.1.2",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"runjs": "^4.3.2",
|
||||
"sass": "~1.32.6",
|
||||
"sass-loader": "^7.1.0",
|
||||
"script-ext-html-webpack-plugin": "2.1.3",
|
||||
"script-loader": "0.7.2",
|
||||
"serve-static": "^1.16.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"svg-sprite-loader": "4.1.3",
|
||||
"svgo": "1.2.2",
|
||||
"vue-i18n-extract": "^1.1.1",
|
||||
"vue-template-compiler": "2.6.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 4 versions",
|
||||
"ie 11"
|
||||
],
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,vue}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
@@ -21,21 +21,11 @@
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import Sortable from 'sortablejs'
|
||||
import DataTable from '@/components/Table/DataTable/index.vue'
|
||||
import {
|
||||
ActionsFormatter,
|
||||
ArrayFormatter,
|
||||
ChoicesFormatter,
|
||||
CopyableFormatter,
|
||||
DateFormatter,
|
||||
DetailFormatter,
|
||||
DisplayFormatter,
|
||||
ObjectRelatedFormatter
|
||||
} from '@/components/Table/TableFormatters'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import { newURL, ObjectLocalStorage, replaceAllUUID, toSentenceCase } from '@/utils/common'
|
||||
import { newURL, ObjectLocalStorage, replaceAllUUID } from '@/utils/common'
|
||||
import ColumnSettingPopover from './components/ColumnSettingPopover.vue'
|
||||
import LabelsFormatter from '@/components/Table/TableFormatters/LabelsFormatter.vue'
|
||||
import { TableColumnsGenerator } from './utils'
|
||||
|
||||
export default {
|
||||
name: 'AutoDataTable',
|
||||
@@ -57,9 +47,9 @@ export default {
|
||||
return {
|
||||
loading: true,
|
||||
method: 'get',
|
||||
autoConfig: {},
|
||||
iConfig: {},
|
||||
meta: {},
|
||||
iConfig: {},
|
||||
autoConfig: {},
|
||||
cleanedColumnsShow: {},
|
||||
totalColumns: [],
|
||||
popoverColumns: {
|
||||
@@ -69,18 +59,8 @@ export default {
|
||||
defaultCols: []
|
||||
},
|
||||
isDeactivated: false,
|
||||
objTableColumns: new ObjectLocalStorage('tableColumns')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dynamicActionWidth() {
|
||||
if (this.$i18n.locale === 'en') {
|
||||
return '120px'
|
||||
}
|
||||
if (this.$i18n.locale === 'pt-br') {
|
||||
return '160px'
|
||||
}
|
||||
return '100px'
|
||||
tableColumnsStorage: this.getTableColumnsStorage(),
|
||||
sortable: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -102,8 +82,9 @@ export default {
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.optionUrlMetaAndGenCols()
|
||||
async created() {
|
||||
await this.optionUrlMetaAndGenCols()
|
||||
this.loading = false
|
||||
},
|
||||
deactivated() {
|
||||
this.isDeactivated = true
|
||||
@@ -112,323 +93,101 @@ export default {
|
||||
this.isDeactivated = false
|
||||
},
|
||||
methods: {
|
||||
setColumnDraggable() {
|
||||
const el = this.$el.querySelector('.el-table__header-wrapper thead tr')
|
||||
if (!el) {
|
||||
setTimeout(() => this.setColumnDraggable(), 500)
|
||||
return
|
||||
}
|
||||
if (this.sortable) {
|
||||
this.sortable.destroy()
|
||||
}
|
||||
|
||||
this.sortable = Sortable.create(el, {
|
||||
animation: 150,
|
||||
onEnd: (evt) => {
|
||||
let { oldIndex, newIndex } = evt
|
||||
if (oldIndex === newIndex) {
|
||||
return
|
||||
}
|
||||
// 检测表格是否有选择列
|
||||
const hasSelectionColumn = this.$el.querySelector('.el-table-column--selection') !== null
|
||||
if (hasSelectionColumn) {
|
||||
// 如果有选择列,调整索引
|
||||
if (oldIndex > 0) oldIndex -= 1
|
||||
if (newIndex > 0) newIndex -= 1
|
||||
}
|
||||
|
||||
let columnNames = [...this.cleanedColumnsShow.show]
|
||||
if (columnNames.includes('actions')) {
|
||||
columnNames = columnNames.filter(item => item !== 'actions')
|
||||
columnNames.push('actions')
|
||||
}
|
||||
// 边界
|
||||
if (oldIndex >= 0 && oldIndex < columnNames.length &&
|
||||
newIndex >= 0 && newIndex < columnNames.length) {
|
||||
const movedItem = columnNames.splice(oldIndex, 1)[0]
|
||||
columnNames.splice(newIndex, 0, movedItem)
|
||||
|
||||
this.$log.debug('Column moved: ', movedItem, oldIndex, ' => ', newIndex)
|
||||
// 保存更新的列顺序
|
||||
this.tableColumnsStorage.set(columnNames)
|
||||
|
||||
// 更新内部状态
|
||||
this.cleanedColumnsShow.show = columnNames
|
||||
this.popoverColumns.currentCols = columnNames
|
||||
|
||||
// 重新应用列顺序
|
||||
this.filterShowColumns()
|
||||
|
||||
this.loading = true
|
||||
setTimeout(() => {
|
||||
this.loading = false
|
||||
// 在DOM完全更新后重新初始化拖拽
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => this.setColumnDraggable(), 200)
|
||||
})
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
generateTotalColumns() {
|
||||
const generator = new TableColumnsGenerator(this.config, this.meta, this)
|
||||
this.totalColumns = generator.generateColumns()
|
||||
this.config.columns = this.totalColumns
|
||||
this.iConfig = _.cloneDeep(this.config)
|
||||
},
|
||||
async optionUrlMetaAndGenCols() {
|
||||
if (this.config.url === '') {
|
||||
return
|
||||
}
|
||||
const url = (this.config.url.indexOf('?') === -1)
|
||||
? `${this.config.url}?draw=1&display=1`
|
||||
: `${this.config.url}&draw=1&display=1`
|
||||
this.$store.dispatch('common/getUrlMeta', { url: url }).then(data => {
|
||||
? `${this.config.url}?display=1`
|
||||
: `${this.config.url}&display=1`
|
||||
|
||||
/**
|
||||
* 原有代码无法正确的同步 storage 的原因是 currentOrder 总是在 totalColumns 之前进行的
|
||||
* 这导致在首次加载时,currentOrder总是为空数组,因为此时cleanedColumnsShow.show还未初始化
|
||||
*/
|
||||
try {
|
||||
const data = await this.$store.dispatch('common/getUrlMeta', { url: url })
|
||||
const method = this.method.toUpperCase()
|
||||
this.meta = data.actions && data.actions[method] ? data.actions[method] : {}
|
||||
|
||||
this.generateTotalColumns()
|
||||
}).then(() => {
|
||||
// 根据当前列重新生成最终渲染表格
|
||||
this.cleanColumnsShow()
|
||||
this.filterShowColumns()
|
||||
}).then(() => {
|
||||
// 生成给子组件使用的TotalColList
|
||||
this.generatePopoverColumns()
|
||||
}).catch((error) => {
|
||||
this.setColumnDraggable()
|
||||
} catch (error) {
|
||||
this.$log.error('Error occur: ', error)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
generateColumnByName(name, col) {
|
||||
switch (name) {
|
||||
case 'id':
|
||||
if (!col.width) {
|
||||
col.width = '299px'
|
||||
}
|
||||
if (!col.formatter) {
|
||||
col.formatter = CopyableFormatter
|
||||
col.iconPosition = 'left'
|
||||
}
|
||||
break
|
||||
case 'name':
|
||||
col.formatter = DetailFormatter
|
||||
col.sortable = 'custom'
|
||||
col.showOverflowTooltip = true
|
||||
col.minWidth = '150px'
|
||||
break
|
||||
case 'actions':
|
||||
col = {
|
||||
prop: 'actions',
|
||||
label: i18n.t('Actions'),
|
||||
align: 'center',
|
||||
width: this.dynamicActionWidth,
|
||||
formatter: ActionsFormatter,
|
||||
fixed: 'right',
|
||||
formatterArgs: {}
|
||||
}
|
||||
break
|
||||
case 'is_valid':
|
||||
col.label = i18n.t('Valid')
|
||||
col.formatter = ChoicesFormatter
|
||||
col.formatterArgs = {
|
||||
textChoices: {
|
||||
true: i18n.t('Yes'),
|
||||
false: i18n.t('No')
|
||||
}
|
||||
}
|
||||
col.width = '80px'
|
||||
break
|
||||
case 'is_active':
|
||||
col.formatter = ChoicesFormatter
|
||||
col.formatterArgs = {
|
||||
textChoices: {
|
||||
true: i18n.t('Active'),
|
||||
false: i18n.t('Inactive')
|
||||
}
|
||||
}
|
||||
col.width = '100px'
|
||||
break
|
||||
case 'datetime':
|
||||
case 'date_start':
|
||||
col.formatter = DateFormatter
|
||||
break
|
||||
case 'labels':
|
||||
col.formatter = LabelsFormatter
|
||||
col.width = '200px'
|
||||
break
|
||||
case 'comment':
|
||||
col.showOverflowTooltip = true
|
||||
}
|
||||
return col
|
||||
},
|
||||
generateColumnByType(type, col, meta) {
|
||||
switch (type) {
|
||||
case 'choice':
|
||||
col.sortable = 'custom'
|
||||
col.formatter = DisplayFormatter
|
||||
break
|
||||
case 'labeled_choice':
|
||||
col.sortable = 'custom'
|
||||
col.formatter = ChoicesFormatter
|
||||
break
|
||||
case 'boolean':
|
||||
col.formatter = ChoicesFormatter
|
||||
// col.width = '80px'
|
||||
break
|
||||
case 'datetime':
|
||||
col.formatter = DateFormatter
|
||||
col.width = '155px'
|
||||
break
|
||||
case 'object_related_field':
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
break
|
||||
case 'm2m_related_field':
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
break
|
||||
case 'list':
|
||||
col.formatter = ArrayFormatter
|
||||
break
|
||||
case 'json':
|
||||
case 'field':
|
||||
if (meta.child && meta.child.type === 'nested object') {
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
}
|
||||
break
|
||||
}
|
||||
// this.$log.debug('Field: ', type, col.prop, col)
|
||||
return col
|
||||
},
|
||||
addHelpTipIfNeed(col) {
|
||||
const helpTip = col.helpTip
|
||||
if (!helpTip) {
|
||||
return col
|
||||
}
|
||||
col.renderHeader = (h, { column, $index }) => {
|
||||
const binds = {
|
||||
props: {
|
||||
placement: 'bottom',
|
||||
effect: 'dark',
|
||||
openDelay: 500,
|
||||
popperClass: 'help-tips'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span>{column.label}
|
||||
<el-tooltip {...binds}>
|
||||
<div slot='content' v-sanitize={helpTip}/>
|
||||
<i class='fa fa-question-circle-o help-tip-icon' style='padding-left: 2px'/>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return col
|
||||
},
|
||||
addFilterIfNeed(col) {
|
||||
if (col.prop) {
|
||||
const column = this.meta[col.prop] || {}
|
||||
if (!column.filter) {
|
||||
return col
|
||||
}
|
||||
if (column.type === 'boolean') {
|
||||
col.filters = [
|
||||
{ text: i18n.t('Yes'), value: true },
|
||||
{ text: i18n.t('No'), value: false }
|
||||
]
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
if (column.type === 'choice' && column.choices) {
|
||||
col.filters = column.choices.map(item => {
|
||||
if (typeof (item.value) === 'boolean') {
|
||||
if (item.value) {
|
||||
return { text: item['label'], value: 'True' }
|
||||
} else {
|
||||
return { text: item['label'], value: 'False' }
|
||||
}
|
||||
}
|
||||
return { text: item['label'], value: item.value }
|
||||
})
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
}
|
||||
return col
|
||||
},
|
||||
addOrderingIfNeed(col) {
|
||||
if (col.prop) {
|
||||
const column = this.meta[col.prop] || {}
|
||||
if (column.order) {
|
||||
col.sortable = 'custom'
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
}
|
||||
return col
|
||||
},
|
||||
setDefaultFormatterIfNeed(col) {
|
||||
if (!col.formatter) {
|
||||
col.formatter = (row, column, cellValue) => {
|
||||
let value = cellValue
|
||||
let padding = '0'
|
||||
const excludes = [undefined, null, '']
|
||||
if (excludes.indexOf(value) !== -1) {
|
||||
padding = '6px'
|
||||
value = '-'
|
||||
}
|
||||
return <span style={{ marginLeft: padding }}>{value}</span>
|
||||
}
|
||||
}
|
||||
return col
|
||||
},
|
||||
setDefaultWidthIfNeed(col) {
|
||||
const lang = this.$i18n.locale
|
||||
let factor = 10
|
||||
if (lang === 'zh') {
|
||||
factor = 20
|
||||
}
|
||||
let [sortable, filters] = [0, 0]
|
||||
if (col && col?.sortable === 'custom') {
|
||||
sortable = 10
|
||||
}
|
||||
if (col && col?.filters?.length > 0) {
|
||||
filters = 12
|
||||
}
|
||||
if (col && !col.width && col.label && !col.minWidth) {
|
||||
col.minWidth = `${col.label.length * factor + sortable + filters + 30}px`
|
||||
}
|
||||
return col
|
||||
},
|
||||
generateColumn(name) {
|
||||
const colMeta = this.meta[name] || {}
|
||||
const customMeta = this.config.columnsMeta ? this.config.columnsMeta[name] : {}
|
||||
let col = { prop: name, label: colMeta.label, showOverflowTooltip: true }
|
||||
|
||||
col = this.generateColumnByType(colMeta.type, col, colMeta)
|
||||
col = this.generateColumnByName(name, col)
|
||||
col = this.setDefaultFormatterIfNeed(col)
|
||||
col = Object.assign(col, customMeta)
|
||||
col = this.addHelpTipIfNeed(col)
|
||||
col = this.addFilterIfNeed(col)
|
||||
col = this.addOrderingIfNeed(col)
|
||||
col = this.updateLabelIfNeed(col)
|
||||
col = this.setDefaultWidthIfNeed(col)
|
||||
return col
|
||||
},
|
||||
updateLabelIfNeed(col) {
|
||||
if (!col.label) {
|
||||
return col
|
||||
}
|
||||
col.label = col.label
|
||||
.replace(' Amount', '')
|
||||
.replace(' amount', '')
|
||||
.replace('数量', '')
|
||||
if (col.label.startsWith('Is ')) {
|
||||
col.label = col.label.replace('Is ', '')
|
||||
}
|
||||
col.label = toSentenceCase(col.label)
|
||||
return col
|
||||
},
|
||||
generateTotalColumns() {
|
||||
const config = _.cloneDeep(this.config)
|
||||
let columns = []
|
||||
const allColumnNames = Object.entries(this.meta)
|
||||
.filter(([name, meta]) => !meta['write_only'])
|
||||
.map(([name, meta]) => name)
|
||||
.concat(config.columnsExtra || [])
|
||||
|
||||
let configColumns = config.columns || allColumnNames
|
||||
const columnsExclude = config.columnsExclude || []
|
||||
const columnsAdd = config.columnsAdd || []
|
||||
configColumns = configColumns.concat(columnsAdd)
|
||||
configColumns = configColumns.filter(item => !columnsExclude.includes(item))
|
||||
|
||||
// 解决后端 API 返回字段中包含 actions 的问题;
|
||||
const hasColumnActions = configColumns.findIndex(item => item?.prop === 'actions') !== -1
|
||||
if (!hasColumnActions) {
|
||||
configColumns = [...configColumns.filter(i => i !== 'actions'), 'actions']
|
||||
}
|
||||
|
||||
for (let col of configColumns) {
|
||||
if (typeof col === 'object') {
|
||||
columns.push(col)
|
||||
} else if (typeof col === 'string') {
|
||||
col = this.generateColumn(col)
|
||||
columns.push(col)
|
||||
}
|
||||
}
|
||||
|
||||
columns = columns.filter(item => {
|
||||
if (item?.showFullContent) {
|
||||
item.className = 'show-full-content'
|
||||
}
|
||||
let has = item.has
|
||||
if (has === undefined) {
|
||||
has = true
|
||||
} else if (typeof has === 'function') {
|
||||
has = has()
|
||||
}
|
||||
return has
|
||||
})
|
||||
|
||||
columns = this.orderingColumns(columns)
|
||||
// 第一次初始化时记录 totalColumns
|
||||
this.totalColumns = columns
|
||||
config.columns = columns
|
||||
this.iConfig = config
|
||||
},
|
||||
orderingColumns(columns) {
|
||||
const cols = _.cloneDeep(this.config.columns)
|
||||
const defaults = _.get(this.config, 'columnsShow.default')
|
||||
const ordering = (cols || defaults || []).map(item => {
|
||||
let prop = item
|
||||
if (typeof item === 'object') {
|
||||
prop = item.prop
|
||||
}
|
||||
return prop
|
||||
})
|
||||
return _.sortBy(columns, (item) => {
|
||||
if (item.prop === 'actions') {
|
||||
return 1000
|
||||
}
|
||||
const i = ordering.indexOf(item.prop)
|
||||
return i === -1 ? 999 : i
|
||||
})
|
||||
getTableColumnsStorage() {
|
||||
let tableName = this.config.name || this.$route.name + '_' + newURL(this.config.url).pathname
|
||||
tableName = replaceAllUUID(tableName)
|
||||
return new ObjectLocalStorage('tableColumns', tableName)
|
||||
},
|
||||
// 生成给子组件使用的TotalColList
|
||||
cleanColumnsShow() {
|
||||
@@ -438,16 +197,12 @@ export default {
|
||||
if (defaultColumnsNames.length === 0) {
|
||||
defaultColumnsNames = totalColumnsNames
|
||||
}
|
||||
// Clean it
|
||||
defaultColumnsNames = totalColumnsNames.filter(n => defaultColumnsNames.indexOf(n) > -1)
|
||||
|
||||
// 最小列
|
||||
const minColumnsNames = _.get(this.iConfig, 'columnsShow.min', ['actions', 'id'])
|
||||
.filter(n => totalColumnsNames.includes(n))
|
||||
|
||||
let tableName = this.config.name || this.$route.name + '_' + newURL(this.config.url).pathname
|
||||
tableName = replaceAllUUID(tableName)
|
||||
const configShowColumnsNames = this.objTableColumns.get(tableName)
|
||||
const configShowColumnsNames = this.tableColumnsStorage.get()
|
||||
let showColumnsNames = configShowColumnsNames || defaultColumnsNames
|
||||
if (showColumnsNames.length === 0) {
|
||||
showColumnsNames = totalColumnsNames
|
||||
@@ -458,8 +213,6 @@ export default {
|
||||
showColumnsNames.push(v)
|
||||
}
|
||||
})
|
||||
// Clean it
|
||||
showColumnsNames = totalColumnsNames.filter(n => showColumnsNames.indexOf(n) > -1)
|
||||
|
||||
this.cleanedColumnsShow = {
|
||||
default: defaultColumnsNames,
|
||||
@@ -471,9 +224,38 @@ export default {
|
||||
},
|
||||
filterShowColumns() {
|
||||
this.cleanColumnsShow()
|
||||
this.iConfig.columns = this.totalColumns.filter(obj => {
|
||||
return this.cleanedColumnsShow.show.indexOf(obj.prop) > -1
|
||||
const showFieldNames = this.cleanedColumnsShow.show
|
||||
let showFields = this.totalColumns.filter(obj => {
|
||||
return showFieldNames.indexOf(obj.prop) > -1
|
||||
})
|
||||
showFields = this.orderingColumns(showFields)
|
||||
this.iConfig.columns = showFields
|
||||
|
||||
// 确保最新的列配置也应用到config对象上,保持同步
|
||||
this.config.columns = this.iConfig.columns
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.dataTable) {
|
||||
this.$refs.dataTable.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
orderingColumns(columns) {
|
||||
const cols = _.cloneDeep(this.config.columns)
|
||||
const show = this.cleanedColumnsShow.show
|
||||
const ordering = (show || cols || []).map(item => {
|
||||
let prop = item
|
||||
if (typeof item === 'object') {
|
||||
prop = item.prop
|
||||
}
|
||||
return prop
|
||||
})
|
||||
const sorted = _.sortBy(columns, (item) => {
|
||||
const i = ordering.indexOf(item.prop)
|
||||
item.order = i
|
||||
return i === -1 ? 999 : i
|
||||
})
|
||||
return sorted
|
||||
},
|
||||
generatePopoverColumns() {
|
||||
this.popoverColumns.totalColumnsList = this.totalColumns.filter(obj => {
|
||||
@@ -493,13 +275,7 @@ export default {
|
||||
columns = this.cleanedColumnsShow.default
|
||||
}
|
||||
this.popoverColumns.currentCols = columns
|
||||
|
||||
let tableName = this.config.name || this.$route.name + '_' + newURL(url).pathname
|
||||
// 替换url中的uuid,避免同一个类型接口生成多个key,localStorage中的数据无法共用.
|
||||
tableName = replaceAllUUID(tableName)
|
||||
|
||||
this.objTableColumns.set(tableName, columns)
|
||||
|
||||
this.tableColumnsStorage.set(columns)
|
||||
this.filterShowColumns()
|
||||
},
|
||||
filterChange(filters) {
|
||||
|
323
src/components/Table/AutoDataTable/utils.js
Normal file
323
src/components/Table/AutoDataTable/utils.js
Normal file
@@ -0,0 +1,323 @@
|
||||
import { toSentenceCase } from '@/utils/common'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
import {
|
||||
ActionsFormatter,
|
||||
ArrayFormatter,
|
||||
ChoicesFormatter,
|
||||
CopyableFormatter,
|
||||
DateFormatter,
|
||||
DetailFormatter,
|
||||
DisplayFormatter,
|
||||
ObjectRelatedFormatter
|
||||
} from '@/components/Table/TableFormatters'
|
||||
import LabelsFormatter from '@/components/Table/TableFormatters/LabelsFormatter.vue'
|
||||
|
||||
export class TableColumnsGenerator {
|
||||
constructor(config, meta, vm) {
|
||||
this.config = config
|
||||
this.meta = meta
|
||||
this.vm = vm
|
||||
}
|
||||
|
||||
dynamicActionWidth() {
|
||||
if (i18n.locale === 'en') {
|
||||
return '120px'
|
||||
}
|
||||
if (i18n.locale === 'pt-br') {
|
||||
return '160px'
|
||||
}
|
||||
return '100px'
|
||||
}
|
||||
|
||||
generateColumns() {
|
||||
const config = _.cloneDeep(this.config)
|
||||
let columns = []
|
||||
const allColumnNames = Object.entries(this.meta)
|
||||
.filter(([name, meta]) => !meta['write_only'])
|
||||
.map(([name, meta]) => name)
|
||||
.concat(config.columnsExtra || [])
|
||||
|
||||
let configColumns = config.columns || allColumnNames
|
||||
const columnsExclude = config.columnsExclude || []
|
||||
const columnsAdd = config.columnsAdd || []
|
||||
configColumns = configColumns.concat(columnsAdd)
|
||||
configColumns = configColumns.filter(item => !columnsExclude.includes(item))
|
||||
|
||||
// 解决后端 API 返回字段中包含 actions 的问题;
|
||||
const hasColumnActions = configColumns.findIndex(item => item?.prop === 'actions') !== -1
|
||||
if (!hasColumnActions) {
|
||||
configColumns = [...configColumns.filter(i => i !== 'actions'), 'actions']
|
||||
}
|
||||
|
||||
for (let col of configColumns) {
|
||||
if (typeof col === 'object') {
|
||||
columns.push(col)
|
||||
} else if (typeof col === 'string') {
|
||||
col = this.generateColumn(col)
|
||||
columns.push(col)
|
||||
}
|
||||
}
|
||||
|
||||
columns = columns.filter(item => {
|
||||
if (item?.showFullContent) {
|
||||
item.className = 'show-full-content'
|
||||
}
|
||||
let has = item.has
|
||||
if (has === undefined) {
|
||||
has = true
|
||||
} else if (typeof has === 'function') {
|
||||
has = has()
|
||||
}
|
||||
return has
|
||||
})
|
||||
|
||||
// columns = this.orderingColumns(columns)
|
||||
// 第一次初始化时记录 totalColumns
|
||||
config.columns = columns
|
||||
return columns
|
||||
}
|
||||
|
||||
updateLabelIfNeed(col) {
|
||||
if (!col.label) {
|
||||
return col
|
||||
}
|
||||
col.label = col.label
|
||||
.replace(' Amount', '')
|
||||
.replace(' amount', '')
|
||||
.replace('数量', '')
|
||||
if (col.label.startsWith('Is ')) {
|
||||
col.label = col.label.replace('Is ', '')
|
||||
}
|
||||
col.label = toSentenceCase(col.label)
|
||||
return col
|
||||
}
|
||||
|
||||
generateColumn(name) {
|
||||
const colMeta = this.meta[name] || {}
|
||||
const customMeta = this.config.columnsMeta ? this.config.columnsMeta[name] : {}
|
||||
let col = { prop: name, label: colMeta.label, showOverflowTooltip: true }
|
||||
|
||||
col = this.generateColumnByType(colMeta.type, col, colMeta)
|
||||
col = this.generateColumnByName(name, col)
|
||||
col = this.setDefaultFormatterIfNeed(col)
|
||||
col = Object.assign(col, customMeta)
|
||||
col = this.addHelpTipIfNeed(col)
|
||||
col = this.addFilterIfNeed(col)
|
||||
col = this.addOrderingIfNeed(col)
|
||||
col = this.updateLabelIfNeed(col)
|
||||
col = this.setDefaultWidthIfNeed(col)
|
||||
return col
|
||||
}
|
||||
|
||||
generateColumnByName(name, col) {
|
||||
switch (name) {
|
||||
case 'id':
|
||||
if (!col.width) {
|
||||
col.width = '299px'
|
||||
}
|
||||
if (!col.formatter) {
|
||||
col.formatter = CopyableFormatter
|
||||
col.iconPosition = 'left'
|
||||
}
|
||||
break
|
||||
case 'name':
|
||||
col.formatter = DetailFormatter
|
||||
col.sortable = 'custom'
|
||||
col.showOverflowTooltip = true
|
||||
col.minWidth = '150px'
|
||||
break
|
||||
case 'actions':
|
||||
col = {
|
||||
prop: 'actions',
|
||||
label: i18n.t('Actions'),
|
||||
align: 'center',
|
||||
width: this.dynamicActionWidth(),
|
||||
formatter: ActionsFormatter,
|
||||
fixed: 'right',
|
||||
formatterArgs: {}
|
||||
}
|
||||
break
|
||||
case 'is_valid':
|
||||
col.label = i18n.t('Valid')
|
||||
col.formatter = ChoicesFormatter
|
||||
col.formatterArgs = {
|
||||
textChoices: {
|
||||
true: i18n.t('Yes'),
|
||||
false: i18n.t('No')
|
||||
}
|
||||
}
|
||||
col.width = '80px'
|
||||
break
|
||||
case 'is_active':
|
||||
col.formatter = ChoicesFormatter
|
||||
col.formatterArgs = {
|
||||
textChoices: {
|
||||
true: i18n.t('Active'),
|
||||
false: i18n.t('Inactive')
|
||||
}
|
||||
}
|
||||
col.width = '100px'
|
||||
break
|
||||
case 'datetime':
|
||||
case 'date_start':
|
||||
col.formatter = DateFormatter
|
||||
break
|
||||
case 'labels':
|
||||
col.formatter = LabelsFormatter
|
||||
col.width = '200px'
|
||||
break
|
||||
case 'comment':
|
||||
col.showOverflowTooltip = true
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
generateColumnByType(type, col, meta) {
|
||||
switch (type) {
|
||||
case 'choice':
|
||||
col.sortable = 'custom'
|
||||
col.formatter = DisplayFormatter
|
||||
break
|
||||
case 'labeled_choice':
|
||||
col.sortable = 'custom'
|
||||
col.formatter = ChoicesFormatter
|
||||
break
|
||||
case 'boolean':
|
||||
col.formatter = ChoicesFormatter
|
||||
// col.width = '80px'
|
||||
break
|
||||
case 'datetime':
|
||||
col.formatter = DateFormatter
|
||||
col.width = '155px'
|
||||
break
|
||||
case 'object_related_field':
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
break
|
||||
case 'm2m_related_field':
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
break
|
||||
case 'list':
|
||||
col.formatter = ArrayFormatter
|
||||
break
|
||||
case 'json':
|
||||
case 'field':
|
||||
if (meta.child && meta.child.type === 'nested object') {
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
}
|
||||
break
|
||||
}
|
||||
// this.$log.debug('Field: ', type, col.prop, col)
|
||||
return col
|
||||
}
|
||||
|
||||
setDefaultFormatterIfNeed(col) {
|
||||
const h = this.vm.$createElement
|
||||
if (!col.formatter) {
|
||||
col.formatter = (row, column, cellValue) => {
|
||||
let value = cellValue
|
||||
let padding = '0'
|
||||
const excludes = [undefined, null, '']
|
||||
if (excludes.indexOf(value) !== -1) {
|
||||
padding = '6px'
|
||||
value = '-'
|
||||
}
|
||||
return h('span', {
|
||||
'style': {
|
||||
marginLeft: padding
|
||||
}
|
||||
}, [value])
|
||||
}
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
setDefaultWidthIfNeed(col) {
|
||||
const lang = i18n.locale
|
||||
let factor = 10
|
||||
if (lang === 'zh') {
|
||||
factor = 20
|
||||
}
|
||||
let [sortable, filters] = [0, 0]
|
||||
if (col && col?.sortable === 'custom') {
|
||||
sortable = 10
|
||||
}
|
||||
if (col && col?.filters?.length > 0) {
|
||||
filters = 12
|
||||
}
|
||||
if (col && !col.width && col.label && !col.minWidth) {
|
||||
col.minWidth = `${col.label.length * factor + sortable + filters + 30}px`
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
addOrderingIfNeed(col) {
|
||||
if (col.prop) {
|
||||
const column = this.meta[col.prop] || {}
|
||||
if (column.order) {
|
||||
col.sortable = 'custom'
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
addHelpTipIfNeed(col) {
|
||||
const helpTip = col.helpTip
|
||||
if (!helpTip) {
|
||||
return col
|
||||
}
|
||||
col.renderHeader = (h, { column, $index }) => {
|
||||
const binds = {
|
||||
props: {
|
||||
placement: 'bottom',
|
||||
effect: 'dark',
|
||||
openDelay: 500,
|
||||
popperClass: 'help-tips'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span>{column.label}
|
||||
<el-tooltip {...binds}>
|
||||
<div slot='content' v-sanitize={helpTip}/>
|
||||
<i class='fa fa-question-circle-o help-tip-icon' style='padding-left: 2px'/>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
addFilterIfNeed(col) {
|
||||
if (col.prop) {
|
||||
const column = this.meta[col.prop] || {}
|
||||
if (!column.filter) {
|
||||
return col
|
||||
}
|
||||
if (column.type === 'boolean') {
|
||||
col.filters = [
|
||||
{ text: i18n.t('Yes'), value: true },
|
||||
{ text: i18n.t('No'), value: false }
|
||||
]
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
if (column.type === 'choice' && column.choices) {
|
||||
col.filters = column.choices.map(item => {
|
||||
if (typeof (item.value) === 'boolean') {
|
||||
if (item.value) {
|
||||
return { text: item['label'], value: 'True' }
|
||||
} else {
|
||||
return { text: item['label'], value: 'False' }
|
||||
}
|
||||
}
|
||||
return { text: item['label'], value: item.value }
|
||||
})
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
}
|
||||
return col
|
||||
}
|
||||
}
|
@@ -397,8 +397,9 @@ export function getDrawerWidth() {
|
||||
}
|
||||
|
||||
export class ObjectLocalStorage {
|
||||
constructor(key) {
|
||||
constructor(key, attr) {
|
||||
this.key = key
|
||||
this.attr = attr
|
||||
}
|
||||
|
||||
b64(val) {
|
||||
@@ -421,6 +422,9 @@ export class ObjectLocalStorage {
|
||||
|
||||
get(attr, defaults) {
|
||||
const obj = this.getObject(this.key)
|
||||
if (!attr && this.attr) {
|
||||
attr = this.attr
|
||||
}
|
||||
const attrSafe = this.b64(attr)
|
||||
const val = obj[attrSafe]
|
||||
if (val === undefined) {
|
||||
@@ -431,6 +435,10 @@ export class ObjectLocalStorage {
|
||||
|
||||
set(attr, value) {
|
||||
const obj = this.getObject(this.key)
|
||||
if (value === undefined && this.attr) {
|
||||
value = attr
|
||||
attr = this.attr
|
||||
}
|
||||
const attrSafe = this.b64(attr)
|
||||
obj[attrSafe] = value
|
||||
window.localStorage.setItem(this.key, JSON.stringify(obj))
|
||||
|
@@ -67,6 +67,7 @@ export const AssetPermissionTableMeta = {
|
||||
}
|
||||
},
|
||||
users_amount: {
|
||||
width: 80,
|
||||
formatter: AmountFormatter,
|
||||
formatterArgs: {
|
||||
async: true,
|
||||
@@ -78,7 +79,7 @@ export const AssetPermissionTableMeta = {
|
||||
}
|
||||
},
|
||||
user_groups_amount: {
|
||||
width: 100,
|
||||
width: 80,
|
||||
formatter: AmountFormatter,
|
||||
formatterArgs: {
|
||||
async: true,
|
||||
@@ -90,6 +91,7 @@ export const AssetPermissionTableMeta = {
|
||||
}
|
||||
},
|
||||
assets_amount: {
|
||||
width: 80,
|
||||
formatter: AmountFormatter,
|
||||
formatterArgs: {
|
||||
async: true,
|
||||
@@ -113,6 +115,7 @@ export const AssetPermissionTableMeta = {
|
||||
}
|
||||
},
|
||||
accounts: {
|
||||
width: 80,
|
||||
formatter: AmountFormatter,
|
||||
formatterArgs: {
|
||||
cellValueToRemove: ['@SPEC'],
|
||||
|
@@ -30,6 +30,7 @@ export default {
|
||||
columnsMeta: {
|
||||
users_amount: {
|
||||
formatter: AmountFormatter,
|
||||
width: 100,
|
||||
formatterArgs: {
|
||||
async: true,
|
||||
getItem(item) {
|
||||
|
@@ -11817,6 +11817,11 @@ sort-keys@^2.0.0:
|
||||
dependencies:
|
||||
is-plain-obj "^1.0.0"
|
||||
|
||||
sortablejs@^1.15.6:
|
||||
version "1.15.6"
|
||||
resolved "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.6.tgz#ff93699493f5b8ab8d828f933227b4988df1d393"
|
||||
integrity sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==
|
||||
|
||||
source-list-map@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/source-list-map/-/source-list-map-2.0.1.tgz"
|
||||
|
Reference in New Issue
Block a user