update 去掉无用组件,修改控制台报错
This commit is contained in:
+1
-4
@@ -47,10 +47,7 @@
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-splitpane": "^1.0.4",
|
||||
"vuedraggable": "^2.20.0",
|
||||
"vuex": "^3.1.0",
|
||||
"vxe-table": "^3.6.10",
|
||||
"vxe-table-plugin-antd": "^1.11.2",
|
||||
"xe-utils": "^3.5.7"
|
||||
"vuex": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasFile">
|
||||
|
||||
<template v-for="(file, fileKey) of [innerFile || {}]">
|
||||
<div :key="fileKey" style="position: relative;">
|
||||
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading"/>
|
||||
<span style="margin-left:5px">上传中…</span>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip v-else-if="file.status==='done'" :title="file.name">
|
||||
<a-icon type="paper-clip"/>
|
||||
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip v-else :title="file.message||'上传失败'">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<template style="width: 30px">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
|
||||
<a-tooltip title="操作">
|
||||
<a-icon
|
||||
v-if="file.status!=='uploading'"
|
||||
type="setting"
|
||||
style="cursor: pointer;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
|
||||
<span><a-icon type="download"/> 下载</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
|
||||
<span><a-icon type="delete"/> 删除</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleMoreOperation(originColumn)">
|
||||
<span><a-icon type="bars"/> 更多</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<a-upload
|
||||
v-show="!hasFile"
|
||||
name="file"
|
||||
:data="{'isup': 1}"
|
||||
:multiple="false"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:showUploadList="false"
|
||||
v-bind="cellProps"
|
||||
@change="handleChangeUpload"
|
||||
>
|
||||
<a-button icon="upload">{{originColumn.btnText || '上传文件'}}</a-button>
|
||||
</a-upload>
|
||||
<j-file-pop ref="filePop" @ok="handleFileSuccess" :number="number"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFileAccessHttpUrl } from '@api/manage'
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import JFilePop from '@/components/jero/minipop/JFilePop'
|
||||
|
||||
import JVxeUploadCell from '@/components/jero/JVxeTable/components/cells/JVxeUploadCell'
|
||||
|
||||
export default {
|
||||
name: 'JVxeFileCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: { JFilePop },
|
||||
props: {},
|
||||
data () {
|
||||
return {
|
||||
innerFile: null,
|
||||
number: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** upload headers */
|
||||
uploadHeaders () {
|
||||
const { originColumn: col } = this
|
||||
const headers = {}
|
||||
if (col.token === true) {
|
||||
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
|
||||
/** 上传请求地址 */
|
||||
uploadAction () {
|
||||
if (!this.originColumn.action) {
|
||||
return window._CONFIG.domianURL + '/sys/common/upload'
|
||||
} else {
|
||||
return this.originColumn.action
|
||||
}
|
||||
},
|
||||
|
||||
hasFile () {
|
||||
return this.innerFile != null
|
||||
},
|
||||
|
||||
ellipsisFileName () {
|
||||
const length = 5
|
||||
const file = this.innerFile
|
||||
if (!file || !file.name) {
|
||||
return ''
|
||||
}
|
||||
if (file.name.length > length) {
|
||||
return file.name.substr(0, length) + '…'
|
||||
}
|
||||
return file.name
|
||||
},
|
||||
|
||||
responseName () {
|
||||
if (this.originColumn.responseName) {
|
||||
return this.originColumn.responseName
|
||||
} else {
|
||||
return 'message'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler () {
|
||||
if (this.innerValue) {
|
||||
this.innerFile = this.innerValue
|
||||
} else {
|
||||
this.innerFile = null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 点击更多按钮
|
||||
handleMoreOperation (originColumn) {
|
||||
// update-begin-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
|
||||
if (originColumn.number) {
|
||||
this.number = originColumn.number
|
||||
} else {
|
||||
this.number = 0
|
||||
}
|
||||
// update-end-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
|
||||
if (originColumn && originColumn.fieldExtendJson) {
|
||||
const json = JSON.parse(originColumn.fieldExtendJson)
|
||||
this.number = json.uploadnum ? json.uploadnum : 0
|
||||
}
|
||||
let path = ''
|
||||
if (this.innerFile) {
|
||||
path = this.innerFile.path
|
||||
}
|
||||
this.$refs.filePop.show('', path)
|
||||
},
|
||||
|
||||
// 更多上传回调
|
||||
handleFileSuccess (file) {
|
||||
if (file) {
|
||||
this.innerFile.path = file.path
|
||||
this.handleChangeCommon(this.innerFile)
|
||||
}
|
||||
},
|
||||
|
||||
handleChangeUpload (info) {
|
||||
// const { originColumn: col } = this
|
||||
const { file } = info
|
||||
const value = {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
status: file.status,
|
||||
percent: file.percent
|
||||
}
|
||||
if (file.response) {
|
||||
value.responseName = file.response[this.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
if (typeof file.response.success === 'boolean') {
|
||||
if (file.response.success) {
|
||||
value.path = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else {
|
||||
value.status = 'error'
|
||||
value.message = file.response.message || '未知错误'
|
||||
}
|
||||
} else {
|
||||
// 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
|
||||
value.path = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
}
|
||||
} else if (file.status === 'error') {
|
||||
value.message = file.response.message || '未知错误'
|
||||
}
|
||||
this.innerFile = value
|
||||
},
|
||||
|
||||
handleClickDownloadFile () {
|
||||
let { url, path } = this.innerFile || {}
|
||||
if (!url || url.length === 0) {
|
||||
if (path && path.length > 0) {
|
||||
url = getFileAccessHttpUrl(path.split(',')[0])
|
||||
}
|
||||
}
|
||||
if (url) {
|
||||
window.open(url)
|
||||
}
|
||||
},
|
||||
|
||||
handleClickDeleteFile () {
|
||||
this.handleChangeCommon(null)
|
||||
}
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: { visible: true },
|
||||
getValue: value => JVxeUploadCell.enhanced.getValue(value),
|
||||
setValue: value => JVxeUploadCell.enhanced.setValue(value)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
</style>
|
||||
@@ -1,245 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasFile">
|
||||
<template v-for="(file, fileKey) of [innerFile || {}]">
|
||||
<div :key="fileKey" style="position: relative;">
|
||||
<template v-if="!file || !(file['url'] || file['path'] || file['message'])">
|
||||
<a-tooltip :title="'请稍后: ' + JSON.stringify (file) + ((file['url'] || file['path'] || file['message']))">
|
||||
<a-icon type="loading"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="file['path']">
|
||||
<img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/>
|
||||
</template>
|
||||
<a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<template style="width: 30px">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
|
||||
<a-tooltip title="操作">
|
||||
<a-icon
|
||||
v-if="file.status!=='uploading'"
|
||||
type="setting"
|
||||
style="cursor: pointer;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
|
||||
<span><a-icon type="download"/> 下载</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
|
||||
<span><a-icon type="delete"/> 删除</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleMoreOperation(originColumn)">
|
||||
<span><a-icon type="bars"/> 更多</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<a-upload
|
||||
v-show="!hasFile"
|
||||
name="file"
|
||||
:data="{'isup': 1}"
|
||||
:multiple="false"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:showUploadList="false"
|
||||
v-bind="cellProps"
|
||||
@change="handleChangeUpload"
|
||||
>
|
||||
<a-button icon="upload">{{originColumn.btnText || '上传图片'}}</a-button>
|
||||
</a-upload>
|
||||
<j-file-pop ref="filePop" @ok="handleFileSuccess" :number="number"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFileAccessHttpUrl } from '@api/manage'
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import JFilePop from '@/components/jero/minipop/JFilePop'
|
||||
|
||||
import JVxeUploadCell from '@/components/jero/JVxeTable/components/cells/JVxeUploadCell'
|
||||
|
||||
export default {
|
||||
name: 'JVxeImageCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: { JFilePop },
|
||||
props: {},
|
||||
data () {
|
||||
return {
|
||||
innerFile: null,
|
||||
number: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** upload headers */
|
||||
uploadHeaders () {
|
||||
const { originColumn: col } = this
|
||||
const headers = {}
|
||||
if (col.token === true) {
|
||||
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
|
||||
/** 上传请求地址 */
|
||||
uploadAction () {
|
||||
if (!this.originColumn.action) {
|
||||
return window._CONFIG.domianURL + '/sys/common/upload'
|
||||
} else {
|
||||
return this.originColumn.action
|
||||
}
|
||||
},
|
||||
|
||||
hasFile () {
|
||||
return this.innerFile != null
|
||||
},
|
||||
|
||||
/** 预览图片地址 */
|
||||
imgSrc () {
|
||||
if (this.innerFile) {
|
||||
if (this.innerFile.url) {
|
||||
return this.innerFile.url
|
||||
} else if (this.innerFile.path) {
|
||||
const path = this.innerFile.path.split(',')[0]
|
||||
return getFileAccessHttpUrl(path)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
},
|
||||
|
||||
responseName () {
|
||||
if (this.originColumn.responseName) {
|
||||
return this.originColumn.responseName
|
||||
} else {
|
||||
return 'message'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler () {
|
||||
if (this.innerValue) {
|
||||
this.innerFile = this.innerValue
|
||||
} else {
|
||||
this.innerFile = null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 点击更多按钮
|
||||
handleMoreOperation (originColumn) {
|
||||
// update-begin-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
|
||||
if (originColumn.number) {
|
||||
this.number = originColumn.number
|
||||
} else {
|
||||
this.number = 0
|
||||
}
|
||||
// update-end-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
|
||||
if (originColumn && originColumn.fieldExtendJson) {
|
||||
const json = JSON.parse(originColumn.fieldExtendJson)
|
||||
this.number = json.uploadnum ? json.uploadnum : 0
|
||||
}
|
||||
let path = ''
|
||||
if (this.innerFile) {
|
||||
path = this.innerFile.path
|
||||
}
|
||||
this.$refs.filePop.show('', path, 'img')
|
||||
},
|
||||
|
||||
// 更多上传回调
|
||||
handleFileSuccess (file) {
|
||||
if (file) {
|
||||
this.innerFile.path = file.path
|
||||
this.handleChangeCommon(this.innerFile)
|
||||
}
|
||||
},
|
||||
|
||||
// 弹出上传出错详细信息
|
||||
handleClickShowImageError () {
|
||||
const file = this.innerFile || null
|
||||
if (file && file.message) {
|
||||
this.$error({ title: '上传出错', content: '错误信息:' + file.message, maskClosable: true })
|
||||
}
|
||||
},
|
||||
|
||||
handleChangeUpload (info) {
|
||||
// const { originColumn: col } = this
|
||||
const { file } = info
|
||||
const value = {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
status: file.status,
|
||||
percent: file.percent
|
||||
}
|
||||
if (file.response) {
|
||||
value.responseName = file.response[this.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
if (typeof file.response.success === 'boolean') {
|
||||
if (file.response.success) {
|
||||
value.path = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else {
|
||||
value.status = 'error'
|
||||
value.message = file.response.message || '未知错误'
|
||||
}
|
||||
} else {
|
||||
// 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
|
||||
value.path = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
}
|
||||
} else if (file.status === 'error') {
|
||||
value.message = file.response.message || '未知错误'
|
||||
}
|
||||
this.innerFile = value
|
||||
},
|
||||
|
||||
handleClickDownloadFile () {
|
||||
if (this.imgSrc) {
|
||||
window.open(this.imgSrc)
|
||||
}
|
||||
},
|
||||
|
||||
handleClickDeleteFile () {
|
||||
this.handleChangeCommon(null)
|
||||
}
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: { visible: true },
|
||||
getValue: value => JVxeUploadCell.enhanced.getValue(value),
|
||||
setValue: value => JVxeUploadCell.enhanced.setValue(value)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.j-editable-image {
|
||||
height: 32px;
|
||||
max-width: 100px !important;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<j-popup
|
||||
v-bind="popupProps"
|
||||
@input="handlePopupInput"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins, { dispatchEvent, vModel } from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxePopupCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
popupProps () {
|
||||
const { innerValue, originColumn: col, caseId, cellProps } = this
|
||||
return {
|
||||
...cellProps,
|
||||
value: innerValue,
|
||||
field: col.field || col.key,
|
||||
code: col.popupCode,
|
||||
orgFields: col.orgFields,
|
||||
destFields: col.destFields,
|
||||
groupId: caseId,
|
||||
param: col.param,
|
||||
sorter: col.sorter
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** popup回调 */
|
||||
handlePopupInput (value, others) {
|
||||
const { row, originColumn: col } = this
|
||||
// 存储输入的值
|
||||
let popupValue = value
|
||||
if (others && Object.keys(others).length > 0) {
|
||||
Object.keys(others).forEach(key => {
|
||||
const currentValue = others[key]
|
||||
// 当前列直接赋值,其他列通过vModel赋值
|
||||
if (key === col.key) {
|
||||
popupValue = currentValue
|
||||
} else {
|
||||
vModel.call(this, currentValue, row, key)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.handleChangeCommon(popupValue)
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived (event) {
|
||||
dispatchEvent.call(this, event, 'ant-input')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<a-radio-group
|
||||
:class="clazz"
|
||||
:value="innerValue"
|
||||
v-bind="cellProps"
|
||||
@change="(e)=>handleChangeCommon(e.target.value)"
|
||||
>
|
||||
<a-radio
|
||||
v-for="item of originColumn.options"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
@click="$event=>handleRadioClick(item,$event)"
|
||||
>{{ item.text }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeRadioCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
scrolling () {
|
||||
return !!this.renderOptions.scrolling
|
||||
},
|
||||
clazz () {
|
||||
return {
|
||||
'j-vxe-radio': true,
|
||||
'no-animation': this.scrolling
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRadioClick (item) {
|
||||
if (this.originColumn.allowClear === true) {
|
||||
// 取消选择
|
||||
if (item.value === this.innerValue) {
|
||||
this.handleChangeCommon(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: { visible: true }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// 关闭动画,防止滚动时动态赋值出现问题
|
||||
.j-vxe-radio.no-animation {
|
||||
.ant-radio-inner,
|
||||
.ant-radio-inner::after {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,262 +0,0 @@
|
||||
import debounce from 'lodash/debounce'
|
||||
import { getAction } from '@/api/manage'
|
||||
import { cloneObject } from '@/utils/util'
|
||||
import { filterDictText } from '@/components/dict/JDictSelectUtil'
|
||||
import { ajaxGetDictItems, getDictItemsFromCache } from '@/api/api'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
/** 公共资源 */
|
||||
const common = {
|
||||
/** value - label map,防止重复查询(刷新清空缓存) */
|
||||
labelMap: new Map(),
|
||||
|
||||
/** 公共data */
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
innerSelectValue: null,
|
||||
innerOptions: []
|
||||
}
|
||||
},
|
||||
/** 公共计算属性 */
|
||||
computed: {
|
||||
dict () {
|
||||
return this.originColumn.dict
|
||||
},
|
||||
options () {
|
||||
if (this.isAsync) {
|
||||
return this.innerOptions
|
||||
} else {
|
||||
return this.originColumn.options || []
|
||||
}
|
||||
},
|
||||
// 是否是异步模式
|
||||
isAsync () {
|
||||
const isAsync = this.originColumn.async
|
||||
return (isAsync != null && isAsync !== '') ? !!isAsync : true
|
||||
}
|
||||
},
|
||||
/** 公共属性监听 */
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler (value) {
|
||||
if (value == null || value === '') {
|
||||
this.innerSelectValue = null
|
||||
} else {
|
||||
this.loadDataByValue(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
dict () {
|
||||
this.loadDataByDict()
|
||||
}
|
||||
},
|
||||
/** 公共方法 */
|
||||
methods: {
|
||||
|
||||
// 根据 value 查询数据,用于回显
|
||||
async loadDataByValue (value) {
|
||||
if (this.isAsync) {
|
||||
if (this.innerSelectValue !== value) {
|
||||
if (common.labelMap.has(value)) {
|
||||
this.innerOptions = cloneObject(common.labelMap.get(value))
|
||||
} else {
|
||||
const { success, result } = await getAction(`/sys/dict/loadDictItem/${this.dict}`, { key: value })
|
||||
if (success && result && result.length > 0) {
|
||||
this.innerOptions = [{ value: value, text: result[0] }]
|
||||
common.labelMap.set(value, cloneObject(this.innerOptions))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.innerSelectValue = (value || '').toString()
|
||||
},
|
||||
|
||||
// 初始化字典
|
||||
async loadDataByDict () {
|
||||
if (!this.isAsync) {
|
||||
// 如果字典项集合有数据
|
||||
if (!this.originColumn.options || this.originColumn.options.length === 0) {
|
||||
// 根据字典Code, 初始化字典数组
|
||||
let dictStr = ''
|
||||
if (this.dict) {
|
||||
const arr = this.dict.split(',')
|
||||
if (arr[0].indexOf('where') > 0) {
|
||||
const tbInfo = arr[0].split('where')
|
||||
dictStr = tbInfo[0].trim() + ',' + arr[1] + ',' + arr[2] + ',' + encodeURIComponent(tbInfo[1])
|
||||
} else {
|
||||
dictStr = this.dict
|
||||
}
|
||||
if (this.dict.indexOf(',') === -1) {
|
||||
// 优先从缓存中读取字典配置
|
||||
const cache = getDictItemsFromCache(this.dict)
|
||||
if (cache) {
|
||||
this.innerOptions = cache
|
||||
return
|
||||
}
|
||||
}
|
||||
const { success, result } = await ajaxGetDictItems(dictStr, null)
|
||||
if (success) {
|
||||
this.innerOptions = result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 显示组件,自带翻译
|
||||
export const DictSearchSpanCell = {
|
||||
name: 'JVxeSelectSearchSpanCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data () {
|
||||
return {
|
||||
...common.data.apply(this)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...common.computed
|
||||
},
|
||||
watch: {
|
||||
...common.watch
|
||||
},
|
||||
methods: {
|
||||
...common.methods
|
||||
},
|
||||
render (h) {
|
||||
return h('span', {}, [
|
||||
filterDictText(this.innerOptions, this.innerSelectValue || this.innerValue)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// 请求id
|
||||
let requestId = 0
|
||||
|
||||
// 输入选择组件
|
||||
export const DictSearchInputCell = {
|
||||
name: 'JVxeSelectSearchInputCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data () {
|
||||
return {
|
||||
...common.data.apply(this),
|
||||
|
||||
hasRequest: false,
|
||||
scopedSlots: {
|
||||
notFoundContent: () => {
|
||||
if (this.loading) {
|
||||
return <a-spin size="small"/>
|
||||
} else if (this.hasRequest) {
|
||||
return <div>没有查询到任何数据</div>
|
||||
} else {
|
||||
return <div>{this.tipsContent}</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...common.computed,
|
||||
tipsContent () {
|
||||
return this.originColumn.tipsContent || '请输入搜索内容'
|
||||
},
|
||||
filterOption () {
|
||||
if (this.isAsync) {
|
||||
return null
|
||||
}
|
||||
return (input, option) => option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
...common.watch
|
||||
},
|
||||
created () {
|
||||
this.loadData = debounce(this.loadData, 300)// 消抖
|
||||
},
|
||||
methods: {
|
||||
...common.methods,
|
||||
|
||||
loadData (value) {
|
||||
const currentRequestId = ++requestId
|
||||
this.loading = true
|
||||
this.innerOptions = []
|
||||
if (value == null || value.trim() === '') {
|
||||
this.loading = false
|
||||
this.hasRequest = false
|
||||
return
|
||||
}
|
||||
// 字典code格式:table,text,code
|
||||
this.hasRequest = true
|
||||
getAction(`/sys/dict/loadDict/${this.dict}`, { keyword: value }).then(res => {
|
||||
if (currentRequestId !== requestId) {
|
||||
return
|
||||
}
|
||||
const { success, result, message } = res
|
||||
if (success) {
|
||||
this.innerOptions = result
|
||||
result.forEach((item) => {
|
||||
common.labelMap.set(item.value, [item])
|
||||
})
|
||||
} else {
|
||||
this.$message.warning(message)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleChange (selectedValue) {
|
||||
this.innerSelectValue = selectedValue
|
||||
this.handleChangeCommon(this.innerSelectValue)
|
||||
},
|
||||
handleSearch (value) {
|
||||
if (this.isAsync) {
|
||||
// 在输入时也应该开启加载,因为loadData加了消抖,所以会有800ms的用户主观上认为的卡顿时间
|
||||
this.loading = true
|
||||
if (this.innerOptions.length > 0) {
|
||||
this.innerOptions = []
|
||||
}
|
||||
this.loadData(value)
|
||||
}
|
||||
},
|
||||
|
||||
renderOptionItem () {
|
||||
const options = []
|
||||
this.options.forEach(({ value, text, label, title, disabled }) => {
|
||||
options.push(
|
||||
<a-select-option key={value} value={value} disabled={disabled}>{text || label || title}</a-select-option>
|
||||
)
|
||||
})
|
||||
return options
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<a-select
|
||||
showSearch
|
||||
allowClear
|
||||
value={this.innerSelectValue}
|
||||
filterOption={this.filterOption}
|
||||
style="width: 100%"
|
||||
{...this.cellProps}
|
||||
onSearch={this.handleSearch}
|
||||
onChange={this.handleChange}
|
||||
scopedSlots={this.scopedSlots}
|
||||
>
|
||||
{this.renderOptionItem()}
|
||||
</a-select>
|
||||
)
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived (event) {
|
||||
dispatchEvent.call(this, event, 'ant-select')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { installCell, JVXETypes } from '@/components/jero/JVxeTable'
|
||||
import JVxePopupCell from './JVxePopupCell'
|
||||
import { DictSearchInputCell, DictSearchSpanCell } from './JVxeSelectDictSearchCell'
|
||||
import JVxeFileCell from './JVxeFileCell'
|
||||
import JVxeImageCell from './JVxeImageCell'
|
||||
import JVxeRadioCell from './JVxeRadioCell'
|
||||
import JVxeSelectCell from '@comp/jero/JVxeTable/components/cells/JVxeSelectCell'
|
||||
import JVxeTextareaCell from '@comp/jero/JVxeTable/components/cells/JVxeTextareaCell'
|
||||
|
||||
// 注册online组件
|
||||
JVXETypes.input_pop = 'input_pop'
|
||||
JVXETypes.list_multi = 'list_multi'
|
||||
JVXETypes.sel_search = 'sel_search'
|
||||
installCell(JVXETypes.input_pop, JVxeTextareaCell)
|
||||
installCell(JVXETypes.list_multi, JVxeSelectCell)
|
||||
installCell(JVXETypes.sel_search, JVxeSelectCell)
|
||||
|
||||
// 注册【popup】组件(普通封装方式)
|
||||
JVXETypes.popup = 'popup'
|
||||
installCell(JVXETypes.popup, JVxePopupCell)
|
||||
|
||||
// 注册【字典搜索下拉】组件(高级封装方式)
|
||||
JVXETypes.selectDictSearch = 'select-dict-search'
|
||||
installCell(JVXETypes.selectDictSearch, DictSearchInputCell, DictSearchSpanCell)
|
||||
|
||||
// 注册【文件上传】组件
|
||||
JVXETypes.file = 'file'
|
||||
installCell(JVXETypes.file, JVxeFileCell)
|
||||
|
||||
// 注册【图片上传】组件
|
||||
JVXETypes.image = 'image'
|
||||
installCell(JVXETypes.image, JVxeImageCell)
|
||||
|
||||
// 注册【单选框】组件
|
||||
JVXETypes.radio = 'radio'
|
||||
installCell(JVXETypes.radio, JVxeRadioCell)
|
||||
@@ -1,88 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
|
||||
<v-chart ref="chart" :forceFit="true" :height="height" :data="dataSource" :scale="scale">
|
||||
<v-tooltip :shared="false"/>
|
||||
<v-axis/>
|
||||
<v-line position="x*y" :size="lineSize" :color="lineColor"/>
|
||||
<v-area position="x*y" :color="color"/>
|
||||
</v-chart>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'AreaChartTy',
|
||||
props: {
|
||||
// 图表数据
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 图表标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// x 轴别名
|
||||
x: {
|
||||
type: String,
|
||||
default: 'x'
|
||||
},
|
||||
// y 轴别名
|
||||
y: {
|
||||
type: String,
|
||||
default: 'y'
|
||||
},
|
||||
// Y轴最小值
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// Y轴最大值
|
||||
max: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
// 图表高度
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
},
|
||||
// 线的粗细
|
||||
lineSize: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
// 面积的颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 线的颜色
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scale () {
|
||||
return [
|
||||
{ dataKey: 'x', title: this.x, alias: this.x },
|
||||
{ dataKey: 'y', title: this.y, alias: this.y, min: this.min, max: this.max }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
triggerWindowResizeEvent()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
||||
@@ -1,50 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart :forceFit="true" :height="height" :data="dataSource" :scale="scale" :padding="padding">
|
||||
<v-tooltip/>
|
||||
<v-axis/>
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'Bar',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
yaxisText: {
|
||||
type: String,
|
||||
default: 'y'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return { padding: ['auto', 'auto', '40', '50'] }
|
||||
},
|
||||
computed: {
|
||||
scale () {
|
||||
return [{
|
||||
dataKey: 'y',
|
||||
alias: this.yaxisText
|
||||
}]
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
triggerWindowResizeEvent()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 50px 32px 0' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :padding=" padding" :onClick="handleClick">
|
||||
<v-tooltip/>
|
||||
<v-legend/>
|
||||
<v-axis/>
|
||||
<v-bar position="type*bar"/>
|
||||
<v-line position="type*line" color="#2fc25b" :size="3"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ChartEventMixins } from './mixins/ChartMixins'
|
||||
|
||||
export default {
|
||||
name: 'BarAndLine',
|
||||
mixins: [ChartEventMixins],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ type: '10:10', bar: 200, line: 1000 },
|
||||
{ type: '10:15', bar: 600, line: 1000 },
|
||||
{ type: '10:20', bar: 200, line: 1000 },
|
||||
{ type: '10:25', bar: 900, line: 1000 },
|
||||
{ type: '10:30', bar: 200, line: 1000 },
|
||||
{ type: '10:35', bar: 200, line: 1000 },
|
||||
{ type: '10:40', bar: 100, line: 1000 }
|
||||
]
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 400
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
padding: { top: 50, right: 50, bottom: 100, left: 50 },
|
||||
scale: [{
|
||||
dataKey: 'bar',
|
||||
min: 0
|
||||
}, {
|
||||
dataKey: 'line',
|
||||
min: 0
|
||||
}]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
data () {
|
||||
return this.dataSource
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart :data="data" :height="height" :force-fit="true" :scale="scale" :onClick="handleClick">
|
||||
<v-tooltip/>
|
||||
<v-axis/>
|
||||
<v-legend/>
|
||||
<v-bar position="x*y" color="type" :adjust="adjust"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DataSet } from '@antv/data-set'
|
||||
import { ChartEventMixins } from './mixins/ChartMixins'
|
||||
|
||||
export default {
|
||||
name: 'BarMultid',
|
||||
mixins: [ChartEventMixins],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ type: 'JeroOne', 'Jan.': 18.9, 'Feb.': 28.8, 'Mar.': 39.3, 'Apr.': 81.4, May: 47, 'Jun.': 20.3, 'Jul.': 24, 'Aug.': 35.6 },
|
||||
{ type: 'JeroTwo', 'Jan.': 12.4, 'Feb.': 23.2, 'Mar.': 34.5, 'Apr.': 99.7, May: 52.6, 'Jun.': 35.5, 'Jul.': 37.4, 'Aug.': 42.4 }
|
||||
]
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: () => ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.']
|
||||
},
|
||||
// 别名,需要的格式:[{field:'name',alias:'姓名'}, {field:'sex',alias:'性别'}]
|
||||
aliases: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
adjust: [{
|
||||
type: 'dodge',
|
||||
marginRatio: 1 / 32
|
||||
}]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
data () {
|
||||
const dv = new DataSet.View().source(this.dataSource)
|
||||
dv.transform({
|
||||
type: 'fold',
|
||||
fields: this.fields,
|
||||
key: 'x',
|
||||
value: 'y'
|
||||
})
|
||||
|
||||
// bar 使用不了 - 和 / 所以替换下
|
||||
const rows = dv.rows.map(row => {
|
||||
if (typeof row.x === 'string') {
|
||||
row.x = row.x.replace(/[-/]/g, '_')
|
||||
}
|
||||
return row
|
||||
})
|
||||
// 替换别名
|
||||
rows.forEach(row => {
|
||||
for (const item of this.aliases) {
|
||||
if (item.field === row.type) {
|
||||
row.type = item.alias
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
return rows
|
||||
},
|
||||
scale () {
|
||||
return [
|
||||
{
|
||||
type: 'cat',
|
||||
dataKey: 'x'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,187 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<v-chart :forceFit="true" :height="300" :data="chartData" :scale="scale">
|
||||
<v-coord type="polar" :startAngle="-202.5" :endAngle="22.5" :radius="0.75"></v-coord>
|
||||
<v-axis
|
||||
dataKey="value"
|
||||
:zIndex="2"
|
||||
:line="null"
|
||||
:label="axisLabel"
|
||||
:subTickCount="4"
|
||||
:subTickLine="axisSubTickLine"
|
||||
:tickLine="axisTickLine"
|
||||
:grid="null"
|
||||
></v-axis>
|
||||
<v-axis dataKey="1" :show="false"></v-axis>
|
||||
<v-series
|
||||
gemo="point"
|
||||
position="value*1"
|
||||
shape="pointer"
|
||||
color="#1890FF"
|
||||
:active="false"
|
||||
></v-series>
|
||||
<v-guide
|
||||
type="arc"
|
||||
:zIndex="0"
|
||||
:top="false"
|
||||
:start="arcGuide1Start"
|
||||
:end="arcGuide1End"
|
||||
:vStyle="arcGuide1Style"
|
||||
></v-guide>
|
||||
<v-guide
|
||||
type="arc"
|
||||
:zIndex="1"
|
||||
:start="arcGuide2Start"
|
||||
:end="getArcGuide2End"
|
||||
:vStyle="arcGuide2Style"
|
||||
></v-guide>
|
||||
<v-guide
|
||||
type="html"
|
||||
:position="htmlGuidePosition"
|
||||
:html="getHtmlGuideHtml()"
|
||||
></v-guide>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { registerShape } from 'viser-vue'
|
||||
|
||||
registerShape('point', 'pointer', {
|
||||
draw (cfg, container) {
|
||||
let point = cfg.points[0]
|
||||
point = this.parsePoint(point)
|
||||
const center = this.parsePoint({
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
container.addShape('line', {
|
||||
attrs: {
|
||||
x1: center.x,
|
||||
y1: center.y,
|
||||
x2: point.x,
|
||||
y2: point.y + 15,
|
||||
stroke: cfg.color,
|
||||
lineWidth: 5,
|
||||
lineCap: 'round'
|
||||
}
|
||||
})
|
||||
return container.addShape('circle', {
|
||||
attrs: {
|
||||
x: center.x,
|
||||
y: center.y,
|
||||
r: 9.75,
|
||||
stroke: cfg.color,
|
||||
lineWidth: 4.5,
|
||||
fill: '#fff'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const scale = [{
|
||||
dataKey: 'value',
|
||||
min: 0,
|
||||
max: 9,
|
||||
tickInterval: 1,
|
||||
nice: false
|
||||
}]
|
||||
|
||||
const data = [
|
||||
{ value: 7.0 }
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'DashChartDemo',
|
||||
props: {
|
||||
datasource: {
|
||||
type: Number,
|
||||
default: 7
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.datasource) {
|
||||
this.chartData = data
|
||||
} else {
|
||||
this.chartData = [
|
||||
{ value: this.datasource }
|
||||
]
|
||||
}
|
||||
this.getChartData()
|
||||
},
|
||||
watch: {
|
||||
datasource: function (val) {
|
||||
this.chartData = [
|
||||
{ value: val }
|
||||
]
|
||||
this.getChartData()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getChartData () {
|
||||
if (this.chartData && this.chartData.length > 0) {
|
||||
this.abcd = this.chartData[0].value * 10
|
||||
} else {
|
||||
this.abcd = 70
|
||||
}
|
||||
},
|
||||
getHtmlGuideHtml () {
|
||||
return '<div style="width: 300px;text-align: center;">\n' +
|
||||
'<p style="font-size: 14px;color: #545454;margin: 0;">' + this.title + '</p>\n' +
|
||||
'<p style="font-size: 36px;color: #545454;margin: 0;">' + this.abcd + '%</p>\n' +
|
||||
'</div>'
|
||||
},
|
||||
getArcGuide2End () {
|
||||
return [this.chartData[0].value, 0.945]
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
chartData: [],
|
||||
height: 400,
|
||||
scale: scale,
|
||||
abcd: 70,
|
||||
axisLabel: {
|
||||
offset: -16,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle'
|
||||
}
|
||||
},
|
||||
axisSubTickLine: {
|
||||
length: -8,
|
||||
stroke: '#fff',
|
||||
strokeOpacity: 1
|
||||
},
|
||||
axisTickLine: {
|
||||
length: -17,
|
||||
stroke: '#fff',
|
||||
strokeOpacity: 1
|
||||
},
|
||||
arcGuide1Start: [0, 0.945],
|
||||
arcGuide1End: [9, 0.945],
|
||||
arcGuide1Style: {
|
||||
stroke: '#CBCBCB',
|
||||
lineWidth: 18
|
||||
},
|
||||
arcGuide2Start: [0, 0.945],
|
||||
arcGuide2Style: {
|
||||
stroke: '#1890FF',
|
||||
lineWidth: 18
|
||||
},
|
||||
htmlGuidePosition: ['50%', '100%'],
|
||||
htmlGuideHtml: `
|
||||
<div style="width: 300px;text-align: center;">
|
||||
<p style="font-size: 14px;color: #545454;margin: 0;">${this.title}</p>
|
||||
<p style="font-size: 36px;color: #545454;margin: 0;">${this.abcd}%</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart
|
||||
height="254"
|
||||
:data="datasource"
|
||||
:forceFit="true"
|
||||
:padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip />
|
||||
<v-axis />
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const data = []
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
data.push({
|
||||
x: `${i + 1}月`,
|
||||
y: Math.floor(Math.random() * 1000) + 200
|
||||
})
|
||||
}
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '时间',
|
||||
min: 1,
|
||||
max: 22
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: 'Bar',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.datasource = data
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
datasource: [],
|
||||
scale,
|
||||
tooltip
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,95 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
|
||||
<v-tooltip/>
|
||||
<v-axis/>
|
||||
<v-legend/>
|
||||
<v-line position="type*y" color="x"/>
|
||||
<v-point position="type*y" color="x" :size="4" :v-style="style" :shape="'circle'"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DataSet } from '@antv/data-set'
|
||||
import { ChartEventMixins } from './mixins/ChartMixins'
|
||||
|
||||
export default {
|
||||
name: 'LineChartMultid',
|
||||
mixins: [ChartEventMixins],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ type: 'Jan', jeroOne: 7.0, jeroTwo: 3.9 },
|
||||
{ type: 'Feb', jeroOne: 6.9, jeroTwo: 4.2 },
|
||||
{ type: 'Mar', jeroOne: 9.5, jeroTwo: 5.7 },
|
||||
{ type: 'Apr', jeroOne: 14.5, jeroTwo: 8.5 },
|
||||
{ type: 'May', jeroOne: 18.4, jeroTwo: 11.9 },
|
||||
{ type: 'Jun', jeroOne: 21.5, jeroTwo: 15.2 },
|
||||
{ type: 'Jul', jeroOne: 25.2, jeroTwo: 17.0 },
|
||||
{ type: 'Aug', jeroOne: 26.5, jeroTwo: 16.6 },
|
||||
{ type: 'Sep', jeroOne: 23.3, jeroTwo: 14.2 },
|
||||
{ type: 'Oct', jeroOne: 18.3, jeroTwo: 10.3 },
|
||||
{ type: 'Nov', jeroOne: 13.9, jeroTwo: 6.6 },
|
||||
{ type: 'Dec', jeroOne: 9.6, jeroTwo: 4.8 }
|
||||
]
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: () => ['jeroOne', 'jeroTwo']
|
||||
},
|
||||
// 别名,需要的格式:[{field:'name',alias:'姓名'}, {field:'sex',alias:'性别'}]
|
||||
aliases: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
scale: [{
|
||||
type: 'cat',
|
||||
dataKey: 'x',
|
||||
min: 0,
|
||||
max: 1
|
||||
}],
|
||||
style: { stroke: '#fff', lineWidth: 1 }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
data () {
|
||||
const dv = new DataSet.View().source(this.dataSource)
|
||||
dv.transform({
|
||||
type: 'fold',
|
||||
fields: this.fields,
|
||||
key: 'x',
|
||||
value: 'y'
|
||||
})
|
||||
const rows = dv.rows
|
||||
// 替换别名
|
||||
rows.forEach(row => {
|
||||
for (const item of this.aliases) {
|
||||
if (item.field === row.x) {
|
||||
row.x = item.alias
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
return rows
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-chart
|
||||
:forceFit="true"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:data="data"
|
||||
:scale="scale"
|
||||
:padding="0">
|
||||
<v-tooltip/>
|
||||
<v-interval
|
||||
:shape="['liquid-fill-gauge']"
|
||||
position="transfer*value"
|
||||
color=""
|
||||
:v-style="{
|
||||
lineWidth: 8,
|
||||
opacity: 0.75
|
||||
}"
|
||||
:tooltip="[
|
||||
'transfer*value',
|
||||
(transfer, value) => {
|
||||
return {
|
||||
name: transfer,
|
||||
value
|
||||
};
|
||||
},
|
||||
]"
|
||||
></v-interval>
|
||||
<v-guide
|
||||
v-for="(row, index) in data"
|
||||
:key="index"
|
||||
type="text"
|
||||
:top="true"
|
||||
:position="{
|
||||
gender: row.transfer,
|
||||
value: 45
|
||||
}"
|
||||
:content="row.value + '%'"
|
||||
:v-style="{
|
||||
fontSize: 100,
|
||||
textAlign: 'center',
|
||||
opacity: 0.75,
|
||||
}"
|
||||
/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const sourceDataConst = [
|
||||
{ transfer: '一月', value: 813 },
|
||||
{ transfer: '二月', value: 233 },
|
||||
{ transfer: '三月', value: 561 }
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'Liquid',
|
||||
props: {
|
||||
height: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data: sourceDataConst,
|
||||
scale: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div class="antv-chart-mini">
|
||||
<div class="chart-wrapper" :style="{ height: 46 }">
|
||||
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :padding="[36, 0, 18, 0]">
|
||||
<v-tooltip/>
|
||||
<v-smooth-area position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'dayjs'
|
||||
|
||||
const sourceData = []
|
||||
const beginDay = new Date().getTime()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
sourceData.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: Math.round(Math.random() * 10)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'MiniArea',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// x 轴别名
|
||||
x: {
|
||||
type: String,
|
||||
default: 'x'
|
||||
},
|
||||
// y 轴别名
|
||||
y: {
|
||||
type: String,
|
||||
default: 'y'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data: [],
|
||||
height: 100
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scale () {
|
||||
return [
|
||||
{ dataKey: 'x', title: this.x, alias: this.x },
|
||||
{ dataKey: 'y', title: this.y, alias: this.y }
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.dataSource.length === 0) {
|
||||
this.data = sourceData
|
||||
} else {
|
||||
this.data = this.dataSource
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
||||
@@ -1,76 +0,0 @@
|
||||
<template>
|
||||
<div :style="{'width':width===null?'auto':width+'px'}">
|
||||
<v-chart :forceFit="typeof width !== 'number'" :height="height" :data="data" padding="0">
|
||||
<v-tooltip/>
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'dayjs'
|
||||
|
||||
const sourceData = []
|
||||
const beginDay = new Date().getTime()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
sourceData.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: Math.round(Math.random() * 10)
|
||||
})
|
||||
}
|
||||
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '时间',
|
||||
min: 1,
|
||||
max: 30
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: 'MiniBar',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 200
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.dataSource.length === 0) {
|
||||
this.data = sourceData
|
||||
} else {
|
||||
this.data = this.dataSource
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tooltip,
|
||||
data: [],
|
||||
scale
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
||||
@@ -1,75 +0,0 @@
|
||||
<template>
|
||||
<div class="chart-mini-progress">
|
||||
<div class="target" :style="{ left: target + '%'}">
|
||||
<span :style="{ backgroundColor: color }"/>
|
||||
<span :style="{ backgroundColor: color }"/>
|
||||
</div>
|
||||
<div class="progress-wrapper">
|
||||
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height+'px' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MiniProgress',
|
||||
props: {
|
||||
target: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#13C2C2'
|
||||
},
|
||||
percentage: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-mini-progress {
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.target {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
span {
|
||||
border-radius: 100px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
width: 2px;
|
||||
|
||||
&:last-child {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.progress-wrapper {
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
|
||||
.progress {
|
||||
transition: all .4s cubic-bezier(.08, .82, .17, 1) 0s;
|
||||
border-radius: 1px 0 0 1px;
|
||||
background-color: #1890ff;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,70 +0,0 @@
|
||||
<template>
|
||||
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
|
||||
<v-tooltip :showTitle="false" dataKey="item*percent"/>
|
||||
<v-axis/>
|
||||
<v-legend dataKey="item"/>
|
||||
<v-pie position="percent" color="item" :v-style="pieStyle" :label="labelConfig"/>
|
||||
<v-coord type="theta"/>
|
||||
</v-chart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ChartEventMixins } from './mixins/ChartMixins'
|
||||
const DataSet = require('@antv/data-set')
|
||||
|
||||
export default {
|
||||
name: 'Pie',
|
||||
mixins: [ChartEventMixins],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ item: '示例一', count: 40 },
|
||||
{ item: '示例二', count: 21 },
|
||||
{ item: '示例三', count: 17 },
|
||||
{ item: '示例四', count: 13 },
|
||||
{ item: '示例五', count: 9 }
|
||||
]
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
scale: [{
|
||||
dataKey: 'percent',
|
||||
min: 0,
|
||||
formatter: '.0%'
|
||||
}],
|
||||
pieStyle: {
|
||||
stroke: '#fff',
|
||||
lineWidth: 1
|
||||
},
|
||||
labelConfig: ['percent', {
|
||||
formatter: (val, item) => {
|
||||
return item.point.item + ': ' + val
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
data () {
|
||||
const dv = new DataSet.View().source(this.dataSource)
|
||||
// 计算数据百分比
|
||||
dv.transform({
|
||||
type: 'percent',
|
||||
field: 'count',
|
||||
dimension: 'item',
|
||||
as: 'percent'
|
||||
})
|
||||
return dv.rows
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,367 +0,0 @@
|
||||
# 报表组件文档
|
||||
|
||||
## 柱状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import Bar from '@/components/chart/Bar'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| dataSource | array | ✔️ | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"x": "1月",
|
||||
"y": 320
|
||||
},
|
||||
{
|
||||
"x": "2月",
|
||||
"y": 457
|
||||
},
|
||||
{
|
||||
"x": "3月",
|
||||
"y": 182
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
##### 代码示例
|
||||
|
||||
```html
|
||||
<template>
|
||||
<bar title="柱状图" :dataSource="dataSource" :height="420"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Bar from '@/components/chart/Bar'
|
||||
|
||||
export default {
|
||||
name: 'ChartDemo',
|
||||
components: {
|
||||
Bar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataSource: [
|
||||
{
|
||||
"x": "1月",
|
||||
"y": 320
|
||||
},
|
||||
{
|
||||
"x": "2月",
|
||||
"y": 457
|
||||
},
|
||||
{
|
||||
"x": "3月",
|
||||
"y": 182
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
```
|
||||
|
||||
## 多列柱状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import BarMultid from '@/components/chart/BarMultid'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| fields | array | | 主列字段列表 |
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### fields 示例
|
||||
|
||||
```json
|
||||
["Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug."]
|
||||
```
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "jero", // 列名
|
||||
"Jan.": 18.9,
|
||||
"Feb.": 28.8,
|
||||
"Mar.": 39.3,
|
||||
"Apr.": 81.4,
|
||||
"May": 47,
|
||||
"Jun.": 20.3,
|
||||
"Jul.": 24,
|
||||
"Aug.": 35.6
|
||||
},
|
||||
{
|
||||
"type": "jeroTwo",
|
||||
"Jan.": 12.4,
|
||||
"Feb.": 23.2,
|
||||
"Mar.": 34.5,
|
||||
"Apr.": 99.7,
|
||||
"May": 52.6,
|
||||
"Jun.": 35.5,
|
||||
"Jul.": 37.4,
|
||||
"Aug.": 42.4
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 迷你柱状图
|
||||
|
||||
不带标题和数据轴的柱状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import MiniBar from '@/components/chart/MiniBar'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|---------------|
|
||||
| width | number | | 报表宽度度,默认自适应宽度 |
|
||||
| height | number | | 报表高度,默认200 |
|
||||
| dataSource | array | | 报表数据源 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"x": "1月",
|
||||
"y": 320
|
||||
},
|
||||
{
|
||||
"x": "2月",
|
||||
"y": 457
|
||||
},
|
||||
{
|
||||
"x": "3月",
|
||||
"y": 182
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 面积图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import AreaChartTy from '@/components/chart/AreaChartTy'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| dataSource | array | ✔️ | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
| lineSize | number | | 线的粗细,默认2 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"x": "1月",
|
||||
"y": 320
|
||||
},
|
||||
{
|
||||
"x": "2月",
|
||||
"y": 457
|
||||
},
|
||||
{
|
||||
"x": "3月",
|
||||
"y": 182
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 多行折线图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import LineChartMultid from '@/components/chart/LineChartMultid'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| title | string | | 报表标题 |
|
||||
| fields | array | | 主列字段列表 |
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### fields 示例
|
||||
|
||||
```json
|
||||
["jero", "jeroTwo"]
|
||||
```
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "Jan", // 列名
|
||||
"jero": 7,
|
||||
"jeroTwo": 3.9
|
||||
},
|
||||
{ "type": "Feb", "jero": 6.9, "jeroTwo": 4.2 },
|
||||
{ "type": "Mar", "jero": 9.5, "jeroTwo": 5.7 },
|
||||
{ "type": "Apr", "jero": 14.5, "jeroTwo": 8.5 },
|
||||
{ "type": "May", "jero": 18.4, "jeroTwo": 11.9 },
|
||||
{ "type": "Jun", "jero": 21.5, "jeroTwo": 15.2 },
|
||||
{ "type": "Jul", "jero": 25.2, "jeroTwo": 17 },
|
||||
{ "type": "Aug", "jero": 26.5, "jeroTwo": 16.6 },
|
||||
{ "type": "Sep", "jero": 23.3, "jeroTwo": 14.2 },
|
||||
{ "type": "Oct", "jero": 18.3, "jeroTwo": 10.3 },
|
||||
{ "type": "Nov", "jero": 13.9, "jeroTwo": 6.6 },
|
||||
{ "type": "Dec", "jero": 9.6, "jeroTwo": 4.8 }
|
||||
]
|
||||
```
|
||||
|
||||
## 饼状图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import Pie from '@/components/chart/Pie'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
// 所有的 percent 相加等于 100
|
||||
{ "item": "一月", "percent": 40 },
|
||||
{ "item": "二月", "percent": 21 },
|
||||
{ "item": "三月", "percent": 17 },
|
||||
{ "item": "四月", "percent": 13 },
|
||||
{ "item": "五月", "percent": 9 }
|
||||
]
|
||||
```
|
||||
|
||||
## 雷达图
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import Radar from '@/components/chart/Radar'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|------------|
|
||||
| dataSource | array | | 报表数据源 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
##### dataSource 示例
|
||||
|
||||
```json
|
||||
[
|
||||
// score 最小值为 0,最大值为 100
|
||||
{ "item": "一月", "score": 40 },
|
||||
{ "item": "二月", "score": 20 },
|
||||
{ "item": "三月", "score": 67 },
|
||||
{ "item": "四月", "score": 43 },
|
||||
{ "item": "五月", "score": 90 }
|
||||
]
|
||||
```
|
||||
|
||||
## 进度条
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import MiniProgress from '@/components/chart/MiniProgress'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|------------|--------|----|-------------------|
|
||||
| percentage | number | | 当前进度百分比,默认0,最高100 |
|
||||
| target | number | | 目标值,默认10 |
|
||||
| height | number | | 进度条高度,默认10 |
|
||||
| color | string | | 进度条颜色,默认 #13C2C2 |
|
||||
|
||||
## 仪表盘
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import DashChartDemo from '@/components/chart/DashChartDemo'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|--------|----|----------------|
|
||||
| title | string | | 报表标题 |
|
||||
| value | number | | 当前值,默认6.7,最大为9 |
|
||||
| height | number | | 报表高度,默认254 |
|
||||
|
||||
## 排名列表
|
||||
|
||||
##### 引用方式
|
||||
|
||||
```js
|
||||
import RankList from '@/components/chart/RankList'
|
||||
```
|
||||
|
||||
##### 参数列表
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|--------|----|--------------|
|
||||
| title | string | | 报表标题 |
|
||||
| list | array | | 排名列表数据 |
|
||||
| height | number | | 报表高度,默认自适应高度 |
|
||||
|
||||
##### list 示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "北京朝阳 1 号店",
|
||||
"total": 1981
|
||||
},
|
||||
{ "name": "北京朝阳 2 号店", "total": 1359 },
|
||||
{ "name": "北京朝阳 3 号店", "total": 1354 },
|
||||
{ "name": "北京朝阳 4 号店", "total": 263 },
|
||||
{ "name": "北京朝阳 5 号店", "total": 446 },
|
||||
{ "name": "北京朝阳 6 号店", "total": 796 }
|
||||
]
|
||||
```
|
||||
@@ -1,90 +0,0 @@
|
||||
<template>
|
||||
<v-chart :forceFit="true" :height="height" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
|
||||
<v-tooltip></v-tooltip>
|
||||
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid"/>
|
||||
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid"/>
|
||||
<v-legend dataKey="user" marker="circle" :offset="30"/>
|
||||
<v-coord type="polar" radius="0.8"/>
|
||||
<v-line position="item*score" color="user" :size="2"/>
|
||||
<v-point position="item*score" color="user" :size="4" shape="circle"/>
|
||||
</v-chart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const axis1Opts = {
|
||||
dataKey: 'item',
|
||||
line: null,
|
||||
tickLine: null,
|
||||
grid: {
|
||||
lineStyle: {
|
||||
lineDash: null
|
||||
},
|
||||
hideFirstLine: false
|
||||
}
|
||||
}
|
||||
const axis2Opts = {
|
||||
dataKey: 'score',
|
||||
line: null,
|
||||
tickLine: null,
|
||||
grid: {
|
||||
type: 'polygon',
|
||||
lineStyle: {
|
||||
lineDash: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scale = [
|
||||
{
|
||||
dataKey: 'score',
|
||||
min: 0,
|
||||
max: 100
|
||||
}, {
|
||||
dataKey: 'user',
|
||||
alias: '类型'
|
||||
}
|
||||
]
|
||||
|
||||
const sourceData = [
|
||||
{ item: '示例一', score: 40 },
|
||||
{ item: '示例二', score: 20 },
|
||||
{ item: '示例三', score: 67 },
|
||||
{ item: '示例四', score: 43 },
|
||||
{ item: '示例五', score: 90 }
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'Radar',
|
||||
props: {
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
axis1Opts,
|
||||
axis2Opts,
|
||||
scale,
|
||||
data: sourceData
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dataSource (newVal) {
|
||||
if (newVal.length === 0) {
|
||||
this.data = sourceData
|
||||
} else {
|
||||
this.data = newVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<div class="rank">
|
||||
<h4 class="title">{{ title }}</h4>
|
||||
<ul class="list" :style="{height:height?`${height}px`:'auto',overflow:'auto'}">
|
||||
<li :key="index" v-for="(item, index) in list">
|
||||
<span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
|
||||
<span>{{ item.name }}</span>
|
||||
<span>{{ item.total }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RankList',
|
||||
// ['title', 'list']
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.rank {
|
||||
padding: 0 32px 32px 72px;
|
||||
|
||||
.list {
|
||||
margin: 25px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
margin-top: 16px;
|
||||
|
||||
span {
|
||||
color: rgba(0, 0, 0, .65);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
&:first-child {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 20px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-right: 24px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
&.active {
|
||||
background-color: #314659;
|
||||
color: #fff;
|
||||
}
|
||||
&:last-child {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile .rank {
|
||||
padding: 0 32px 32px 32px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-chart :forceFit="true" :height="height" :data="data">
|
||||
<v-coord type="rect" direction="LB" />
|
||||
<v-tooltip />
|
||||
<v-legend />
|
||||
<v-axis dataKey="State" :label="label" />
|
||||
<v-stack-bar position="State*流程数量" color="流程状态" />
|
||||
</v-chart>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const DataSet = require('@antv/data-set')
|
||||
|
||||
export default {
|
||||
name: 'StackBar',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [
|
||||
{ State: '请假', 流转中: 25, 已归档: 18 },
|
||||
{ State: '出差', 流转中: 30, 已归档: 20 },
|
||||
{ State: '加班', 流转中: 38, 已归档: 42 },
|
||||
{ State: '用车', 流转中: 51, 已归档: 67 }
|
||||
]
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
label: { offset: 12 }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
data () {
|
||||
const dv = new DataSet.View().source(this.dataSource)
|
||||
dv.transform({
|
||||
type: 'fold',
|
||||
fields: ['流转中', '已归档'],
|
||||
key: '流程状态',
|
||||
value: '流程数量',
|
||||
retains: ['State']
|
||||
})
|
||||
return dv.rows
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,66 +0,0 @@
|
||||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart
|
||||
:height="height"
|
||||
:data="data"
|
||||
:scale="scale"
|
||||
:forceFit="true"
|
||||
:padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip/>
|
||||
<v-axis/>
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Bar',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
x: {
|
||||
type: String,
|
||||
default: 'x'
|
||||
},
|
||||
y: {
|
||||
type: String,
|
||||
default: 'y'
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
scale () {
|
||||
return [
|
||||
{ dataKey: 'x', title: this.x, alias: this.x },
|
||||
{ dataKey: 'y', title: this.y, alias: this.y }
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
// this.getMonthBar()
|
||||
},
|
||||
methods: {
|
||||
// getMonthBar() {
|
||||
// this.$http.get('/analysis/month-bar')
|
||||
// .then(res => {
|
||||
// this.data = res.result
|
||||
// })
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<div class="chart-trend">
|
||||
{{ term }}
|
||||
<span>{{ rate }}%</span>
|
||||
<span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Trend',
|
||||
props: {
|
||||
// 同title
|
||||
term: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
// 百分比
|
||||
percentage: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
type: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
target: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
fixed: {
|
||||
type: Number,
|
||||
default: 2
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
trend: (this.type && 'up') || 'down',
|
||||
rate: this.percentage
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const type = this.type === null ? this.value >= this.target : this.type
|
||||
this.trend = type ? 'up' : 'down'
|
||||
this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-trend {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
.trend-icon {
|
||||
font-size: 12px;
|
||||
|
||||
&.up, &.down {
|
||||
margin-left: 4px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
transform: scale(.83);
|
||||
}
|
||||
}
|
||||
|
||||
&.up {
|
||||
color: #f5222d;
|
||||
}
|
||||
&.down {
|
||||
color: #52c41a;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,13 +0,0 @@
|
||||
.antv-chart-mini {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.chart-wrapper {
|
||||
position: absolute;
|
||||
bottom: -28px;
|
||||
width: 100%;
|
||||
|
||||
/* margin: 0 -5px;
|
||||
overflow: hidden;*/
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
export const ChartEventMixins = {
|
||||
methods: {
|
||||
handleClick (event, chart) {
|
||||
this.handleEvent('click', event, chart)
|
||||
},
|
||||
handleEvent (eventName, event, chart) {
|
||||
this.$emit(eventName, event, chart)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<template>
|
||||
<j-modal
|
||||
title="详细信息"
|
||||
:width="1200"
|
||||
:visible="visible"
|
||||
@ok="handleOk"
|
||||
@cancel="close"
|
||||
switch-fullscreen
|
||||
:fullscreen.sync="fullscreen"
|
||||
>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="visible">
|
||||
<slot name="mainForm" :row="row" :column="column"/>
|
||||
<slot name="subForm" :row="row" :column="column"/>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
</j-modal>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { cloneObject } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDetailsModal',
|
||||
inject: ['superTrigger'],
|
||||
data () {
|
||||
return {
|
||||
visible: false,
|
||||
fullscreen: false,
|
||||
row: null,
|
||||
column: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
},
|
||||
methods: {
|
||||
|
||||
open (event) {
|
||||
const { row, column } = event
|
||||
this.row = cloneObject(row)
|
||||
this.column = column
|
||||
this.visible = true
|
||||
},
|
||||
|
||||
close () {
|
||||
this.visible = false
|
||||
},
|
||||
|
||||
handleOk () {
|
||||
this.superTrigger('detailsConfirm', {
|
||||
row: this.row,
|
||||
column: this.column,
|
||||
callback: (success) => {
|
||||
this.visible = !success
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<div :class="boxClass">
|
||||
<a-pagination
|
||||
:disabled="disabled"
|
||||
v-bind="bindProps"
|
||||
@change="handleChange"
|
||||
@showSizeChange="handleShowSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PropTypes from 'ant-design-vue/es/_util/vue-types'
|
||||
|
||||
export default {
|
||||
name: 'JVxePagination',
|
||||
props: {
|
||||
size: String,
|
||||
disabled: PropTypes.bool,
|
||||
pagination: PropTypes.object.def({})
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
defaultPagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: ['10', '20', '30'],
|
||||
showTotal: (total, range) => {
|
||||
return range[0] + '-' + range[1] + ' 共 ' + total + ' 条'
|
||||
},
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
total: 100
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bindProps () {
|
||||
return {
|
||||
...this.defaultPagination,
|
||||
...this.pagination,
|
||||
size: this.size === 'tiny' ? 'small' : ''
|
||||
}
|
||||
},
|
||||
boxClass () {
|
||||
return {
|
||||
'j-vxe-pagination': true,
|
||||
'show-quick-jumper': !!this.bindProps.showQuickJumper
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (current, pageSize) {
|
||||
this.$set(this.pagination, 'current', current)
|
||||
this.$emit('change', { current, pageSize })
|
||||
},
|
||||
handleShowSizeChange (current, pageSize) {
|
||||
this.$set(this.pagination, 'pageSize', pageSize)
|
||||
this.$emit('change', { current, pageSize })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,181 +0,0 @@
|
||||
<template>
|
||||
<a-popover :visible="visible" :placement="placement" overlayClassName="j-vxe-popover-overlay" :overlayStyle="overlayStyle">
|
||||
<div class="j-vxe-popover-title" slot="title">
|
||||
<div>子表</div>
|
||||
<div class="j-vxe-popover-title-close" @click="close">
|
||||
<a-icon type="close"/>
|
||||
</div>
|
||||
</div>
|
||||
<template slot="content">
|
||||
<transition name="fade">
|
||||
<slot v-if="visible" name="subForm" :row="row" :column="column"/>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<div ref="div" class="j-vxe-popover-div"></div>
|
||||
|
||||
</a-popover>
|
||||
</template>
|
||||
<script>
|
||||
import domAlign from 'dom-align'
|
||||
import { getParentNodeByTagName } from '../utils/vxeUtils'
|
||||
import { cloneObject, triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'JVxeSubPopover',
|
||||
data () {
|
||||
return {
|
||||
visible: false,
|
||||
// 当前行
|
||||
row: null,
|
||||
column: null,
|
||||
|
||||
overlayStyle: {
|
||||
width: null,
|
||||
zIndex: 100
|
||||
},
|
||||
placement: 'bottom'
|
||||
}
|
||||
},
|
||||
created () {
|
||||
},
|
||||
methods: {
|
||||
|
||||
toggle (event) {
|
||||
// update-begin-author:taoyan date:20200921 for: 弹出子表时,子表会闪一下,类似重新计算子表的位置
|
||||
if (document.body.clientHeight - event.$event.clientY > 350) {
|
||||
this.placement = 'bottom'
|
||||
} else {
|
||||
this.placement = 'top'
|
||||
}
|
||||
// update-end-author:taoyan date:20200921 for: 弹出子表时,子表会闪一下,类似重新计算子表的位置
|
||||
if (this.row == null) {
|
||||
this.open(event)
|
||||
} else {
|
||||
this.row.id === event.row.id ? this.close() : this.reopen(event)
|
||||
}
|
||||
},
|
||||
|
||||
open (event, level = 0) {
|
||||
if (level > 3) {
|
||||
this.$message.error('打开子表失败')
|
||||
console.warn('【JVxeSubPopover】打开子表失败')
|
||||
return
|
||||
}
|
||||
|
||||
const { row, column, $table, $event: { target } } = event
|
||||
this.row = cloneObject(row)
|
||||
this.column = column
|
||||
|
||||
let className = target.className || ''
|
||||
className = typeof className === 'string' ? className : className.toString()
|
||||
|
||||
// 点击的是expand,不做处理
|
||||
if (className.includes('vxe-table--expand-btn')) {
|
||||
return
|
||||
}
|
||||
// 点击的是checkbox,不做处理
|
||||
if (className.includes('vxe-checkbox--icon') || className.includes('vxe-cell--checkbox')) {
|
||||
return
|
||||
}
|
||||
// 点击的是radio,不做处理
|
||||
if (className.includes('vxe-radio--icon') || className.includes('vxe-cell--radio')) {
|
||||
return
|
||||
}
|
||||
const table = $table.$el
|
||||
const tr = getParentNodeByTagName(target, 'tr')
|
||||
if (table && tr) {
|
||||
const clientWidth = table.clientWidth
|
||||
const clientHeight = tr.clientHeight
|
||||
this.$refs.div.style.width = clientWidth + 'px'
|
||||
this.$refs.div.style.height = clientHeight + 'px'
|
||||
this.overlayStyle.width = Number.parseInt((clientWidth - clientWidth * 0.04) + '') + 'px'
|
||||
this.overlayStyle.maxWidth = this.overlayStyle.width
|
||||
// update-begin-author:taoyan date:20200921 for: 子表弹出位置存在现实位置问题。
|
||||
// let realTable = getParentNodeByTagName(tr, 'table')
|
||||
// let left = realTable.parentNode.scrollLeft
|
||||
let h = event.$event.clientY
|
||||
if (h) {
|
||||
h = h - 140
|
||||
}
|
||||
const toolbar = this.$refs.div.nextSibling
|
||||
domAlign(this.$refs.div, toolbar, {
|
||||
points: ['tl', 'tl'],
|
||||
offset: [0, h],
|
||||
overflow: {
|
||||
alwaysByViewport: true
|
||||
}
|
||||
})
|
||||
// update-end-author:taoyan date:20200921 for: 子表弹出位置存在现实位置问题。
|
||||
this.$nextTick(() => {
|
||||
this.visible = true
|
||||
this.$nextTick(() => {
|
||||
triggerWindowResizeEvent()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
const num = ++level
|
||||
console.warn('【JVxeSubPopover】table or tr 获取失败,正在进行第 ' + num + '次重试', { event, table, tr })
|
||||
window.setTimeout(() => this.open(event, num), 100)
|
||||
}
|
||||
},
|
||||
close () {
|
||||
if (this.visible) {
|
||||
this.row = null
|
||||
this.visible = false
|
||||
}
|
||||
},
|
||||
reopen (event) {
|
||||
this.close()
|
||||
this.open(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.j-vxe-popover-title {
|
||||
.j-vxe-popover-title-close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
text-align: center;
|
||||
line-height: 31px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
cursor: pointer;
|
||||
transition: color 300ms;
|
||||
|
||||
&:hover {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.j-vxe-popover-div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 31px;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.j-vxe-popover-overlay.ant-popover {
|
||||
.ant-popover-title {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,127 +0,0 @@
|
||||
<template>
|
||||
<div :class="boxClass">
|
||||
<!-- 工具按钮 -->
|
||||
<div class="j-vxe-tool-button div" :size="btnSize">
|
||||
<slot v-if="showPrefix" name="toolbarPrefix" :size="btnSize"/>
|
||||
|
||||
<a-button v-if="showAdd" icon="plus" @click="trigger('add')" :disabled="disabled" type="primary">新增</a-button>
|
||||
<a-button v-if="showSave" icon="save" @click="trigger('save')" :disabled="disabled">保存</a-button>
|
||||
<template v-if="selectedRowIds.length > 0">
|
||||
<a-popconfirm
|
||||
v-if="showRemove"
|
||||
:title="`确定要删除这 ${selectedRowIds.length} 项吗?`"
|
||||
@confirm="trigger('remove')"
|
||||
>
|
||||
<a-button icon="minus" :disabled="disabled">删除</a-button>
|
||||
</a-popconfirm>
|
||||
<template v-if="showClearSelection">
|
||||
<a-button icon="delete" @click="trigger('clearSelection')">清空选择</a-button>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<slot v-if="showSuffix" name="toolbarSuffix" :size="btnSize"/>
|
||||
<a v-if="showCollapse" @click="toggleCollapse" style="margin-left: 4px">
|
||||
<span>{{ collapsed ? '展开' : '收起' }}</span>
|
||||
<a-icon :type="collapsed ? 'down' : 'up'"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'JVxeToolbar',
|
||||
props: {
|
||||
toolbarConfig: Object,
|
||||
excludeCode: Array,
|
||||
size: String,
|
||||
disabled: Boolean,
|
||||
disabledRows: Object,
|
||||
selectedRowIds: Array
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 是否收起
|
||||
collapsed: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
boxClass () {
|
||||
return {
|
||||
'j-vxe-toolbar': true,
|
||||
'j-vxe-toolbar-collapsed': this.collapsed
|
||||
}
|
||||
},
|
||||
|
||||
btns () {
|
||||
const arr = this.toolbarConfig.btn || ['add', 'remove', 'clearSelection']
|
||||
const exclude = [...this.excludeCode]
|
||||
// TODO 需要将remove替换batch_delete
|
||||
// 系统默认的批量删除编码配置为 batch_delete 此处需要转化一下
|
||||
if (exclude.indexOf('batch_delete') >= 0) {
|
||||
exclude.push('remove')
|
||||
}
|
||||
// 按钮权限 需要去掉不被授权的按钮
|
||||
return arr.filter(item => {
|
||||
return exclude.indexOf(item) < 0
|
||||
})
|
||||
},
|
||||
slots () {
|
||||
return this.toolbarConfig.slot || ['prefix', 'suffix']
|
||||
},
|
||||
showPrefix () {
|
||||
return this.slots.includes('prefix')
|
||||
},
|
||||
showSuffix () {
|
||||
return this.slots.includes('suffix')
|
||||
},
|
||||
showAdd () {
|
||||
return this.btns.includes('add')
|
||||
},
|
||||
showSave () {
|
||||
return this.btns.includes('save')
|
||||
},
|
||||
showRemove () {
|
||||
return this.btns.includes('remove')
|
||||
},
|
||||
showClearSelection () {
|
||||
if (this.btns.includes('clearSelection')) {
|
||||
// 有禁用行时才显示清空选择按钮
|
||||
// 因为禁用行会阻止选择行,导致无法取消全选
|
||||
const length = Object.keys(this.disabledRows).length
|
||||
return length > 0
|
||||
}
|
||||
return false
|
||||
},
|
||||
showCollapse () {
|
||||
return this.btns.includes('collapse')
|
||||
},
|
||||
|
||||
btnSize () {
|
||||
return this.size === 'tiny' ? 'small' : null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 触发事件 */
|
||||
trigger (name) {
|
||||
this.$emit(name)
|
||||
},
|
||||
// 切换展开收起
|
||||
toggleCollapse () {
|
||||
this.collapsed = !this.collapsed
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.j-vxe-toolbar-collapsed {
|
||||
[data-collapse] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.j-vxe-tool-button.div .ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<div :class="clazz" :style="boxStyle">
|
||||
<a-checkbox
|
||||
ref="checkbox"
|
||||
:checked="innerValue"
|
||||
v-bind="cellProps"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { neverNull } from '@/utils/util'
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeCheckboxCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
computed: {
|
||||
bordered () {
|
||||
return !!this.renderOptions.bordered
|
||||
},
|
||||
scrolling () {
|
||||
return !!this.renderOptions.scrolling
|
||||
},
|
||||
clazz () {
|
||||
return {
|
||||
'j-vxe-checkbox': true,
|
||||
'no-animation': this.scrolling
|
||||
}
|
||||
},
|
||||
boxStyle () {
|
||||
const style = {}
|
||||
// 如果有边框且未设置align属性,就强制居中
|
||||
if (this.bordered && !this.originColumn.align) {
|
||||
style['text-align'] = 'center'
|
||||
}
|
||||
return style
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (event) {
|
||||
this.handleChangeCommon(event.target.checked)
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
visible: true
|
||||
},
|
||||
getValue (value) {
|
||||
const { params: col } = this.column
|
||||
// 处理 customValue
|
||||
if (Array.isArray(col.customValue)) {
|
||||
const customValue = getCustomValue(col)
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? customValue[0] : customValue[1]
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
setValue (value) {
|
||||
const { params: col } = this.column
|
||||
// 判断是否设定了customValue(自定义值)
|
||||
if (Array.isArray(col.customValue)) {
|
||||
const customValue = getCustomValue(col)
|
||||
return neverNull(value).toString() === customValue[0].toString()
|
||||
} else {
|
||||
return !!value
|
||||
}
|
||||
},
|
||||
createValue ({ column }) {
|
||||
const { params: col } = column
|
||||
if (Array.isArray(col.customValue)) {
|
||||
const customValue = getCustomValue(col)
|
||||
return col.defaultChecked ? customValue[0] : customValue[1]
|
||||
} else {
|
||||
return !!col.defaultChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomValue (col) {
|
||||
const customTrue = neverNull(col.customValue[0], true)
|
||||
const customFalse = neverNull(col.customValue[1], false)
|
||||
return [customTrue, customFalse]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// 关闭动画,防止滚动时动态赋值出现问题
|
||||
.j-vxe-checkbox.no-animation {
|
||||
.ant-checkbox-inner,
|
||||
.ant-checkbox-inner::after {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,68 +0,0 @@
|
||||
<template>
|
||||
<a-date-picker
|
||||
ref="datePicker"
|
||||
:value="innerDateValue"
|
||||
allowClear
|
||||
:format="dateFormat"
|
||||
:showTime="isDatetime"
|
||||
dropdownClassName="j-vxe-date-picker"
|
||||
style="min-width: 0;"
|
||||
v-bind="cellProps"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import { JVXETypes } from '@comp/jero/JVxeTable'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDateCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
data () {
|
||||
return {
|
||||
innerDateValue: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isDatetime () {
|
||||
return this.$type === JVXETypes.datetime
|
||||
},
|
||||
dateFormat () {
|
||||
const format = this.originColumn.format
|
||||
return format || (this.isDatetime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler (val) {
|
||||
if (val == null || val === '') {
|
||||
this.innerDateValue = null
|
||||
} else {
|
||||
this.innerDateValue = moment(val, this.dateFormat)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (mom, dateStr) {
|
||||
this.handleChangeCommon(dateStr)
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived (event) {
|
||||
dispatchEvent.call(this, event, 'ant-calendar-picker', el => el.children[0].dispatchEvent(event.$event))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,128 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-input
|
||||
v-show="!departIds"
|
||||
@click="openSelect"
|
||||
placeholder="请点击选择部门"
|
||||
v-model="departNames"
|
||||
readOnly
|
||||
:disabled="componentDisabled"
|
||||
class="jvxe-select-input">
|
||||
<a-icon slot="prefix" type="cluster" title="部门选择控件"/>
|
||||
</a-input>
|
||||
<j-select-depart-modal
|
||||
ref="innerDepartSelectModal"
|
||||
:modal-width="modalWidth"
|
||||
:multi="multi"
|
||||
:rootOpened="rootOpened"
|
||||
:depart-id="departIds"
|
||||
@ok="handleOK"
|
||||
@initComp="initComp"/>
|
||||
<span style="display: inline-block;height:100%;padding-left:14px" v-if="departIds" >
|
||||
<span @click="openSelect" style="display: inline-block;vertical-align: middle">{{ departNames }}</span>
|
||||
<a-icon style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
import JSelectDepartModal from '@/components/jerobiz/modal/JSelectDepartModal'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDepartSelectCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: {
|
||||
JSelectDepartModal
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
departNames: '',
|
||||
departIds: '',
|
||||
selectedOptions: [],
|
||||
customReturnField: 'id'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
custProps () {
|
||||
const { departIds, originColumn: col, caseId, cellProps } = this
|
||||
return {
|
||||
...cellProps,
|
||||
value: departIds,
|
||||
field: col.field || col.key,
|
||||
groupId: caseId,
|
||||
class: 'jvxe-select'
|
||||
}
|
||||
},
|
||||
componentDisabled () {
|
||||
return this.cellProps.disabled === true
|
||||
},
|
||||
modalWidth () {
|
||||
if (this.cellProps.modalWidth) {
|
||||
return this.cellProps.modalWidth
|
||||
} else {
|
||||
return 500
|
||||
}
|
||||
},
|
||||
multi () {
|
||||
console.log(this.cellProps)
|
||||
return this.cellProps.multi !== false
|
||||
},
|
||||
rootOpened () {
|
||||
return this.cellProps.open !== false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler (val) {
|
||||
if (val == null || val === '') {
|
||||
this.departIds = ''
|
||||
} else {
|
||||
this.departIds = val
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openSelect () {
|
||||
this.$refs.innerDepartSelectModal.show()
|
||||
},
|
||||
handleEmpty () {
|
||||
this.handleOK('')
|
||||
},
|
||||
handleOK (rows, idstr) {
|
||||
// let value = ''
|
||||
if (!rows && rows.length <= 0) {
|
||||
this.departNames = ''
|
||||
this.departIds = ''
|
||||
} else {
|
||||
// value = rows.map(row => row[this.customReturnField]).join(',')
|
||||
this.departNames = rows.map(row => row.departName).join(',')
|
||||
this.departIds = idstr
|
||||
}
|
||||
this.handleChangeCommon(this.departIds)
|
||||
},
|
||||
initComp (departNames) {
|
||||
this.departNames = departNames
|
||||
},
|
||||
handleChange (value) {
|
||||
this.handleChangeCommon(value)
|
||||
}
|
||||
},
|
||||
enhanced: {
|
||||
switches: {
|
||||
visible: true
|
||||
},
|
||||
translate: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .jvxe-select-input .ant-input {
|
||||
border: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,138 +0,0 @@
|
||||
<template>
|
||||
<a-dropdown :trigger="['click']">
|
||||
<div class="j-vxe-ds-icons">
|
||||
<a-icon type="align-left" />
|
||||
<a-icon type="align-right" />
|
||||
</div>
|
||||
|
||||
<!-- <div class="j-vxe-ds-btns">-->
|
||||
<!-- <a-button icon="caret-up" size="small" :disabled="disabledMoveUp" @click="handleRowMoveUp"/>-->
|
||||
<!-- <a-button icon="caret-down" size="small" :disabled="disabledMoveDown" @click="handleRowMoveDown"/>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item key="0" :disabled="disabledMoveUp" @click="handleRowMoveUp">向上移</a-menu-item>
|
||||
<a-menu-item key="1" :disabled="disabledMoveDown" @click="handleRowMoveDown">向下移</a-menu-item>
|
||||
<a-menu-divider />
|
||||
<a-menu-item key="3" @click="handleRowInsertDown">插入一行</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDragSortCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
// 排序结果保存字段
|
||||
dragSortKey () {
|
||||
return this.renderOptions.dragSortKey || 'orderNum'
|
||||
},
|
||||
disabledMoveUp () {
|
||||
return this.rowIndex === 0
|
||||
},
|
||||
disabledMoveDown () {
|
||||
return this.rowIndex === (this.fullDataLength - 1)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 向上移 */
|
||||
handleRowMoveUp () {
|
||||
// event.target.blur()
|
||||
if (!this.disabledMoveUp) {
|
||||
this.trigger('rowMoveUp', this.rowIndex)
|
||||
}
|
||||
},
|
||||
/** 向下移 */
|
||||
handleRowMoveDown () {
|
||||
// event.target.blur()
|
||||
if (!this.disabledMoveDown) {
|
||||
this.trigger('rowMoveDown', this.rowIndex)
|
||||
}
|
||||
},
|
||||
/** 插入一行 */
|
||||
handleRowInsertDown () {
|
||||
this.trigger('rowInsertDown', this.rowIndex)
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
// 【功能开关】
|
||||
switches: {
|
||||
editRender: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.j-vxe-ds-icons {
|
||||
position: relative;
|
||||
/*cursor: move;*/
|
||||
cursor: pointer;
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
|
||||
.anticon-align-left,
|
||||
.anticon-align-right {
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
}
|
||||
|
||||
.anticon-align-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.anticon-align-right {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.j-vxe-ds-btns {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 24px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
|
||||
.ant-btn {
|
||||
border: none;
|
||||
|
||||
z-index: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
/*height: 30%;*/
|
||||
height: 40%;
|
||||
display: block;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover {
|
||||
z-index: 1;
|
||||
/* height: 40%;*/
|
||||
|
||||
/* & .anticon-caret-up,*/
|
||||
/* & .anticon-caret-down {*/
|
||||
/* top: 2px;*/
|
||||
/* }*/
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
& .anticon-caret-up,
|
||||
& .anticon-caret-down {
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: 0;
|
||||
transition: top 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,87 +0,0 @@
|
||||
<template>
|
||||
<a-input
|
||||
ref="input"
|
||||
:value="innerValue"
|
||||
v-bind="cellProps"
|
||||
@blur="handleBlur"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { JVXETypes } from '@/components/jero/JVxeTable'
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
const NumberRegExp = /^-?\d+\.?\d*$/
|
||||
export default {
|
||||
name: 'JVxeInputCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
methods: {
|
||||
|
||||
/** 处理change事件 */
|
||||
handleChange (event) {
|
||||
const { $type } = this
|
||||
const { target } = event
|
||||
let { value, selectionStart } = target
|
||||
let change = true
|
||||
if ($type === JVXETypes.inputNumber) {
|
||||
// 判断输入的值是否匹配数字正则表达式,不匹配就还原
|
||||
if (!NumberRegExp.test(value) && (value !== '' && value !== '-')) {
|
||||
change = false
|
||||
value = this.innerValue
|
||||
target.value = value || ''
|
||||
if (typeof selectionStart === 'number') {
|
||||
target.selectionStart = selectionStart - 1
|
||||
target.selectionEnd = selectionStart - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
// 触发事件,存储输入的值
|
||||
if (change) {
|
||||
this.handleChangeCommon(value)
|
||||
}
|
||||
|
||||
if ($type === JVXETypes.inputNumber) {
|
||||
// this.recalcOneStatisticsColumn(col.key)
|
||||
}
|
||||
},
|
||||
|
||||
/** 处理blur失去焦点事件 */
|
||||
handleBlur (event) {
|
||||
const { $type } = this
|
||||
const { target } = event
|
||||
// 判断输入的值是否匹配数字正则表达式,不匹配就置空
|
||||
if ($type === JVXETypes.inputNumber) {
|
||||
if (!NumberRegExp.test(target.value)) {
|
||||
target.value = ''
|
||||
} else {
|
||||
target.value = Number.parseFloat(target.value)
|
||||
}
|
||||
this.handleChangeCommon(target.value)
|
||||
}
|
||||
|
||||
this.handleBlurCommon(target.value)
|
||||
}
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
installOptions: {
|
||||
// 自动聚焦的 class 类名
|
||||
autofocus: '.ant-input'
|
||||
},
|
||||
getValue (value) {
|
||||
if (this.$type === JVXETypes.inputNumber && typeof value === 'string') {
|
||||
if (NumberRegExp.test(value)) {
|
||||
return Number.parseFloat(value)
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,42 +0,0 @@
|
||||
<template>
|
||||
<reload-effect
|
||||
:vNode="innerValue"
|
||||
:effect="reloadEffect"
|
||||
@effect-end="handleEffectEnd"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ReloadEffect from './ReloadEffect'
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeNormalCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: { ReloadEffect },
|
||||
computed: {
|
||||
reloadEffectRowKeysMap () {
|
||||
return this.renderOptions.reloadEffectRowKeysMap
|
||||
},
|
||||
reloadEffect () {
|
||||
return (this.renderOptions.reloadEffect && this.reloadEffectRowKeysMap[this.row.id]) === true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 特效结束
|
||||
handleEffectEnd () {
|
||||
this.$delete(this.reloadEffectRowKeysMap, this.row.id)
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
editRender: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<a-progress
|
||||
:class="clazz"
|
||||
:percent="innerValue"
|
||||
size="small"
|
||||
v-bind="cellProps"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
// JVxe 进度条组件
|
||||
export default {
|
||||
name: 'JVxeProgressCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
clazz () {
|
||||
return {
|
||||
'j-vxe-progress': true,
|
||||
'no-animation': this.scrolling
|
||||
}
|
||||
},
|
||||
scrolling () {
|
||||
return !!this.renderOptions.scrolling
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
editRender: false
|
||||
},
|
||||
setValue (value) {
|
||||
try {
|
||||
if (typeof value !== 'number') {
|
||||
return Number.parseFloat(value)
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
// 关闭进度条的动画,防止滚动时动态赋值出现问题
|
||||
.j-vxe-progress.no-animation {
|
||||
/deep/ .ant-progress-success-bg,
|
||||
/deep/ .ant-progress-bg {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,219 +0,0 @@
|
||||
<template>
|
||||
<a-select
|
||||
ref="select"
|
||||
:value="innerValue"
|
||||
allowClear
|
||||
:filterOption="handleSelectFilterOption"
|
||||
v-bind="selectProps"
|
||||
style="width: 100%;"
|
||||
@blur="handleBlur"
|
||||
@change="handleChange"
|
||||
@search="handleSearchSelect">
|
||||
|
||||
<div v-if="loading" slot="notFoundContent">
|
||||
<a-icon type="loading" />
|
||||
<span> 加载中…</span>
|
||||
</div>
|
||||
|
||||
<template v-for="option of selectOptions">
|
||||
<a-select-option :key="option.value" :value="option.value" :disabled="option.disabled">
|
||||
<span>{{ option.text || option.label || option.title || option.value }}</span>
|
||||
</a-select-option>
|
||||
</template>
|
||||
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { JVXETypes } from '@comp/jero/JVxeTable'
|
||||
import { filterDictText } from '@comp/dict/JDictSelectUtil'
|
||||
|
||||
export default {
|
||||
name: 'JVxeSelectCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
// 异步加载的options(用于多级联动)
|
||||
asyncOptions: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectOptions () {
|
||||
if (this.asyncOptions) {
|
||||
return this.asyncOptions
|
||||
}
|
||||
const { linkage } = this.renderOptions
|
||||
if (linkage) {
|
||||
return this.handleComputedSelectOptions(linkage)
|
||||
}
|
||||
return this.originColumn.options
|
||||
},
|
||||
// 下拉选项
|
||||
selectProps () {
|
||||
const props = { ...this.cellProps }
|
||||
// 判断select是否允许输入
|
||||
const { allowSearch, allowInput } = this.originColumn
|
||||
if (allowInput === true || allowSearch === true) {
|
||||
props.showSearch = true
|
||||
}
|
||||
return props
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const multiple = [JVXETypes.selectMultiple, JVXETypes.list_multi]
|
||||
const search = [JVXETypes.selectSearch, JVXETypes.sel_search]
|
||||
if (multiple.includes(this.$type)) {
|
||||
// 处理多选
|
||||
const props = this.originColumn.props || {}
|
||||
props.mode = 'multiple'
|
||||
props.maxTagCount = 1
|
||||
this.$set(this.originColumn, 'props', props)
|
||||
} else if (search.includes(this.$type)) {
|
||||
// 处理搜索
|
||||
this.$set(this.originColumn, 'allowSearch', true)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (value) {
|
||||
// 处理下级联动
|
||||
const linkage = this.renderOptions.linkage
|
||||
if (linkage) {
|
||||
linkage.linkageSelectChange(this.row, this.originColumn, linkage.config, value)
|
||||
}
|
||||
this.handleChangeCommon(value)
|
||||
},
|
||||
/** 处理blur失去焦点事件 */
|
||||
handleBlur (value) {
|
||||
const { allowInput, options } = this.originColumn
|
||||
|
||||
if (allowInput === true) {
|
||||
// 删除无用的因搜索(用户输入)而创建的项
|
||||
if (typeof value === 'string') {
|
||||
const indexes = []
|
||||
options.forEach((option, index) => {
|
||||
if (option.value.toLocaleString() === value.toLocaleString()) {
|
||||
delete option.searchAdd
|
||||
} else if (option.searchAdd === true) {
|
||||
indexes.push(index)
|
||||
}
|
||||
})
|
||||
// 翻转删除数组中的项
|
||||
for (const index of indexes.reverse()) {
|
||||
options.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.handleBlurCommon(value)
|
||||
},
|
||||
|
||||
/** 用于搜索下拉框中的内容 */
|
||||
handleSelectFilterOption (input, option) {
|
||||
const { allowSearch, allowInput } = this.originColumn
|
||||
if (allowSearch === true || allowInput === true) {
|
||||
// update-begin-author:taoyan date:20200820 for:【专项任务】大连项目反馈行编辑问题处理 下拉框搜索
|
||||
return option.componentOptions.children[0].children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
// update-end-author:taoyan date:20200820 for:【专项任务】大连项目反馈行编辑问题处理 下拉框搜索
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
/** select 搜索时的事件,用于动态添加options */
|
||||
handleSearchSelect (value) {
|
||||
const { allowSearch, allowInput, options } = this.originColumn
|
||||
|
||||
if (allowSearch !== true && allowInput === true) {
|
||||
// 是否找到了对应的项,找不到则添加这一项
|
||||
let flag = false
|
||||
for (const option of options) {
|
||||
if (option.value.toLocaleString() === value.toLocaleString()) {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// !!value :不添加空值
|
||||
if (!flag && !!value) {
|
||||
// searchAdd 是否是通过搜索添加的
|
||||
options.push({ title: value, value: value, searchAdd: true })
|
||||
}
|
||||
}
|
||||
},
|
||||
handleComputedSelectOptions (linkage) {
|
||||
const { getLinkageOptionsSibling, config } = linkage
|
||||
const res = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
|
||||
// 当返回Promise时,说明是多级联动
|
||||
if (res instanceof Promise) {
|
||||
this.loading = true
|
||||
res.then(opt => {
|
||||
this.asyncOptions = opt
|
||||
this.loading = false
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
this.asyncOptions = null
|
||||
return res
|
||||
}
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived (event) {
|
||||
dispatchEvent.call(this, event, 'ant-select')
|
||||
}
|
||||
},
|
||||
translate: {
|
||||
enabled: true,
|
||||
async handler (value) {
|
||||
let options
|
||||
const { linkage } = this.renderOptions
|
||||
// 判断是否是多级联动,如果是就通过接口异步翻译
|
||||
if (linkage) {
|
||||
const { getLinkageOptionsSibling, config } = linkage
|
||||
options = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
|
||||
if (options instanceof Promise) {
|
||||
return new Promise(resolve => {
|
||||
options.then(opt => {
|
||||
resolve(filterDictText(opt, value))
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
options = (this.column.params || {}).options
|
||||
}
|
||||
return filterDictText(options, value)
|
||||
}
|
||||
},
|
||||
getValue (value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(',')
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
setValue (value) {
|
||||
const { column: { params: col }, params: { $table } } = this
|
||||
// 判断是否是多选
|
||||
if ((col.props || {}).mode === 'multiple') {
|
||||
$table.$set(col.props, 'maxTagCount', 1)
|
||||
}
|
||||
if (value != null && value !== '') {
|
||||
if (typeof value === 'string') {
|
||||
return value === '' ? [] : value.split(',')
|
||||
}
|
||||
return value
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,46 +0,0 @@
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
// 插槽
|
||||
export default {
|
||||
name: 'JVxeSlotCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
slotProps () {
|
||||
return {
|
||||
value: this.innerValue,
|
||||
row: this.row,
|
||||
column: this.originColumn,
|
||||
|
||||
params: this.params,
|
||||
$table: this.params.$table,
|
||||
rowId: this.params.rowid,
|
||||
index: this.params.rowIndex,
|
||||
rowIndex: this.params.rowIndex,
|
||||
columnIndex: this.params.columnIndex,
|
||||
|
||||
target: this.renderOptions.target,
|
||||
caseId: this.renderOptions.target.caseId,
|
||||
scrolling: this.renderOptions.scrolling,
|
||||
reloadEffect: this.renderOptions.reloadEffect,
|
||||
|
||||
triggerChange: (v) => this.handleChangeCommon(v)
|
||||
}
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
const { slot } = this.renderOptions
|
||||
if (slot) {
|
||||
return h('div', {}, slot(this.slotProps))
|
||||
} else {
|
||||
return h('div')
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
editRender: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// :isNotPass="notPassedIds.includes(col.key+row.id)"
|
||||
@@ -1,145 +0,0 @@
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
// tags 组件的显示组件
|
||||
export const TagsSpanCell = {
|
||||
name: 'JVxeTagsCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data () {
|
||||
return {
|
||||
innerTags: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler (value) {
|
||||
if (value !== this.innerTags.join(';')) {
|
||||
const rv = replaceValue(value)
|
||||
this.innerTags = rv.split(';')
|
||||
this.handleChangeCommon(rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
renderTags (h) {
|
||||
const tags = []
|
||||
for (const tag of this.innerTags) {
|
||||
if (tag) {
|
||||
const tagProps = {}
|
||||
const tagStyle = {}
|
||||
const setTagColor = this.originColumn.setTagColor
|
||||
if (typeof setTagColor === 'function') {
|
||||
/**
|
||||
* 设置 tag 颜色
|
||||
*
|
||||
* @param event 包含的字段:
|
||||
* event.tagValue 当前tag的值
|
||||
* event.value 当前原始值
|
||||
* event.row 当前行的所有值
|
||||
* event.column 当前列的配置
|
||||
* event.column.own 当前列的原始配置
|
||||
* @return Array | String 可以返回一个数组,数据第一项是tag背景颜色,第二项是字体颜色。也可以返回一个字符串,即tag背景颜色
|
||||
*/
|
||||
const color = setTagColor({
|
||||
tagValue: tag,
|
||||
value: this.innerValue,
|
||||
row: this.row,
|
||||
column: this.column
|
||||
})
|
||||
if (Array.isArray(color)) {
|
||||
tagProps.color = color[0]
|
||||
tagStyle.color = color[1]
|
||||
} else if (color && typeof color === 'string') {
|
||||
tagProps.color = color
|
||||
}
|
||||
}
|
||||
tags.push(h('a-tag', {
|
||||
props: tagProps,
|
||||
style: tagStyle
|
||||
}, [tag]))
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
return h('div', {}, [
|
||||
this.renderTags(h)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// tags 组件的输入框
|
||||
export const TagsInputCell = {
|
||||
name: 'JVxeTagsInputCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data () {
|
||||
return {
|
||||
innerTagValue: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler (value) {
|
||||
if (value !== this.innerTagValue) {
|
||||
this.handleInputChange(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleInputChange (value, event) {
|
||||
this.innerTagValue = replaceValue(value, event)
|
||||
this.handleChangeCommon(this.innerTagValue)
|
||||
return this.innerTagValue
|
||||
}
|
||||
|
||||
},
|
||||
render (h) {
|
||||
return h('a-input', {
|
||||
props: {
|
||||
value: this.innerValue,
|
||||
...this.cellProps
|
||||
},
|
||||
on: {
|
||||
change: (event) => {
|
||||
const { target, target: { value } } = event
|
||||
const newValue = this.handleInputChange(value, event)
|
||||
if (newValue !== value) {
|
||||
target.value = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 将值每隔两位加上一个分号
|
||||
function replaceValue (value, event) {
|
||||
if (value) {
|
||||
// 首先去掉现有的分号
|
||||
value = value.replace(/;/g, '')
|
||||
// 然后再遍历添加分号
|
||||
let rv = ''
|
||||
const splitArr = value.split('')
|
||||
let count = 0
|
||||
splitArr.forEach((val, index) => {
|
||||
rv += val
|
||||
const position = index + 1
|
||||
if (position % 2 === 0 && position < splitArr.length) {
|
||||
count++
|
||||
rv += ';'
|
||||
}
|
||||
})
|
||||
if (event && count > 0) {
|
||||
const { target, target: { selectionStart } } = event
|
||||
target.selectionStart = selectionStart + count
|
||||
target.selectionEnd = selectionStart + count
|
||||
}
|
||||
return rv
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<template>
|
||||
<j-input-pop
|
||||
:value="innerValue"
|
||||
:width="300"
|
||||
:height="210"
|
||||
v-bind="cellProps"
|
||||
style="width: 100%;"
|
||||
@change="handleChangeCommon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JInputPop from '@/components/jero/minipop/JInputPop'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeTextareaCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: { JInputPop },
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
installOptions: {
|
||||
autofocus: '.ant-input'
|
||||
},
|
||||
aopEvents: {
|
||||
editActived (event) {
|
||||
dispatchEvent.call(this, event, 'anticon-fullscreen')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,190 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasFile">
|
||||
<template v-for="(file, fileKey) of [innerFile || {}]">
|
||||
<a-input :key="fileKey" :readOnly="true" :value="file.name">
|
||||
|
||||
<template slot="addonBefore" style="width: 30px">
|
||||
<a-tooltip v-if="file.status === 'uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading" />
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else-if="file.status === 'done'" title="上传完成">
|
||||
<a-icon type="check-circle" style="color:#00DB00;" />
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else :title="file.message||'上传失败'">
|
||||
<a-icon type="exclamation-circle" style="color:red;" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<span v-if="file.status === 'uploading'" slot="addonAfter">{{ Math.floor(file.percent) }}%</span>
|
||||
<template v-else-if="originColumn.allowDownload !== false || originColumn.allowRemove !== false" slot="addonAfter">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight">
|
||||
<a-tooltip title="操作">
|
||||
<a-icon type="setting" style="cursor: pointer;" />
|
||||
</a-tooltip>
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<!-- <a-menu-item @click="handleClickPreviewFile">-->
|
||||
<!-- <span><a-icon type="eye"/> 预览</span>-->
|
||||
<!-- </a-menu-item>-->
|
||||
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
|
||||
<span><a-icon type="download" /> 下载</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
|
||||
<span><a-icon type="delete" /> 删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
</a-input>
|
||||
</template>
|
||||
</template>
|
||||
<a-upload
|
||||
v-show="!hasFile"
|
||||
name="file"
|
||||
:data="{'isup': 1}"
|
||||
:multiple="false"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:showUploadList="false"
|
||||
v-bind="cellProps"
|
||||
@change="handleChangeUpload">
|
||||
<a-button icon="upload">{{ originColumn.btnText || '点击上传' }}</a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import { getFileAccessHttpUrl } from '@api/manage'
|
||||
|
||||
export default {
|
||||
name: 'JVxeUploadCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
data () {
|
||||
return {
|
||||
innerFile: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** upload headers */
|
||||
uploadHeaders () {
|
||||
const { originColumn: col } = this
|
||||
const headers = {}
|
||||
if (col.token === true) {
|
||||
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
hasFile () {
|
||||
return !!this.innerFile
|
||||
},
|
||||
uploadAction () {
|
||||
if (this.originColumn.action) {
|
||||
return this.originColumn.action
|
||||
}
|
||||
return window._CONFIG.domianURL + '/sys/common/upload'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler () {
|
||||
if (this.innerValue) {
|
||||
this.innerFile = this.innerValue
|
||||
} else {
|
||||
this.innerFile = null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleChangeUpload (info) {
|
||||
const { originColumn: col } = this
|
||||
const { file } = info
|
||||
const value = {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
status: file.status,
|
||||
percent: file.percent
|
||||
}
|
||||
if (col.responseName && file.response) {
|
||||
value.responseName = file.response[col.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
if (typeof file.response.success === 'boolean') {
|
||||
if (file.response.success) {
|
||||
value.path = col.responseName ? file.response[col.responseName] : file.response.result.url
|
||||
} else {
|
||||
value.status = 'error'
|
||||
value.message = file.response.message || '未知错误'
|
||||
}
|
||||
} else {
|
||||
// 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
|
||||
value.path = file.response[col.responseName]
|
||||
}
|
||||
} else if (file.status === 'error') {
|
||||
value.message = file.response.message || '未知错误'
|
||||
}
|
||||
this.innerFile = value
|
||||
// 上传完成后触发通用change事件,将值抛出去
|
||||
if (file.status === 'done') {
|
||||
this.handleChangeCommon(this.innerFile)
|
||||
}
|
||||
},
|
||||
|
||||
// handleClickPreviewFile(id) {
|
||||
// this.$message.info('尚未实现')
|
||||
// },
|
||||
|
||||
handleClickDownloadFile () {
|
||||
const { path } = this.value || {}
|
||||
if (path) {
|
||||
const url = getFileAccessHttpUrl(path)
|
||||
window.open(url)
|
||||
}
|
||||
},
|
||||
|
||||
handleClickDeleteFile () {
|
||||
this.handleChangeCommon(null)
|
||||
}
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: { visible: true },
|
||||
getValue: value => fileGetValue(value),
|
||||
setValue: value => fileSetValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
function fileGetValue (value) {
|
||||
if (value && value.path) {
|
||||
return value.path
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function fileSetValue (value) {
|
||||
if (value) {
|
||||
const first = value.split(',')[0]
|
||||
const name = first.substring(first.lastIndexOf('/') + 1)
|
||||
return {
|
||||
name: name,
|
||||
path: value,
|
||||
status: 'done'
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,129 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-input
|
||||
v-show="!userIds"
|
||||
@click="openSelect"
|
||||
placeholder="请选择用户"
|
||||
v-model="userNames"
|
||||
readOnly
|
||||
class="jvxe-select-input"
|
||||
:disabled="componentDisabled">
|
||||
<a-icon slot="prefix" type="user" title="用户选择控件"/>
|
||||
</a-input>
|
||||
<j-select-user-by-dep-modal
|
||||
ref="selectModal"
|
||||
:modal-width="modalWidth"
|
||||
:multi="multi"
|
||||
:user-ids="userIds"
|
||||
@ok="selectOK"
|
||||
@initComp="initComp"/>
|
||||
<span style="display: inline-block;height:100%;padding-left:14px" v-if="userIds" >
|
||||
<span @click="openSelect" style="display: inline-block;vertical-align: middle">{{ userNames }}</span>
|
||||
<a-icon style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- <j-select-user-by-dep
|
||||
v-bind="custProps"
|
||||
@change="handleChange"
|
||||
:trigger-change="true">
|
||||
</j-select-user-by-dep>-->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jero/JVxeTable/mixins/JVxeCellMixins'
|
||||
import JSelectUserByDepModal from '@/components/jerobiz/modal/JSelectUserByDepModal'
|
||||
|
||||
export default {
|
||||
name: 'JVxeUserSelectCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: { JSelectUserByDepModal },
|
||||
data () {
|
||||
return {
|
||||
userIds: '',
|
||||
userNames: '',
|
||||
innerUserValue: '',
|
||||
selectedOptions: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
custProps () {
|
||||
const { userIds, originColumn: col, caseId, cellProps } = this
|
||||
return {
|
||||
...cellProps,
|
||||
value: userIds,
|
||||
field: col.field || col.key,
|
||||
groupId: caseId,
|
||||
class: 'jvxe-select'
|
||||
}
|
||||
},
|
||||
componentDisabled () {
|
||||
console.log('333', this.cellProps)
|
||||
return this.cellProps.disabled === true
|
||||
},
|
||||
modalWidth () {
|
||||
if (this.cellProps.modalWidth) {
|
||||
return this.cellProps.modalWidth
|
||||
} else {
|
||||
return 1250
|
||||
}
|
||||
},
|
||||
multi () {
|
||||
return this.cellProps.multi !== false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler (val) {
|
||||
if (val == null || val === '') {
|
||||
this.userIds = ''
|
||||
} else {
|
||||
this.userIds = val
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openSelect () {
|
||||
this.$refs.selectModal.showModal()
|
||||
},
|
||||
selectOK (rows, idstr) {
|
||||
console.log('当前选中用户', rows)
|
||||
console.log('当前选中用户ID', idstr)
|
||||
if (!rows) {
|
||||
this.userNames = ''
|
||||
this.userIds = ''
|
||||
} else {
|
||||
let temp = ''
|
||||
for (const item of rows) {
|
||||
temp += ',' + item.realname
|
||||
}
|
||||
this.userNames = temp.substring(1)
|
||||
this.userIds = idstr
|
||||
}
|
||||
this.handleChangeCommon(this.userIds)
|
||||
},
|
||||
handleEmpty () {
|
||||
this.selectOK('')
|
||||
},
|
||||
initComp (userNames) {
|
||||
this.userNames = userNames
|
||||
}
|
||||
},
|
||||
enhanced: {
|
||||
switches: {
|
||||
visible: true
|
||||
},
|
||||
translate: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .jvxe-select-input .ant-input {
|
||||
border: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,84 +0,0 @@
|
||||
import '../../less/reload-effect.less'
|
||||
import { randomString } from '@/utils/util'
|
||||
|
||||
// 修改数据特效
|
||||
export default {
|
||||
props: {
|
||||
vNode: null,
|
||||
// 是否启用特效
|
||||
effect: Boolean
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// vNode: null,
|
||||
innerEffect: false,
|
||||
// 应付同时多个特效
|
||||
effectIdx: 0,
|
||||
effectList: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
vNode: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler (vNode, old) {
|
||||
this.innerEffect = this.effect
|
||||
if (this.innerEffect && old != null) {
|
||||
const topLayer = this.renderSpan(old, 'top')
|
||||
this.effectList.push(topLayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 条件渲染内容 span
|
||||
renderVNode () {
|
||||
if (this.vNode == null) {
|
||||
return null
|
||||
}
|
||||
const bottom = this.renderSpan(this.vNode, 'bottom')
|
||||
// 启用了特效,并且有旧数据,就渲染特效顶层
|
||||
if (this.innerEffect && this.effectList.length > 0) {
|
||||
this.$emit('effect-begin')
|
||||
// 1.4s 以后关闭特效
|
||||
window.setTimeout(() => {
|
||||
const item = this.effectList[this.effectIdx]
|
||||
if (item && item.elm) {
|
||||
// 特效结束后,展示先把 display 设为 none,而不是直接删掉该元素,
|
||||
// 目的是为了防止页面重新渲染,导致动画重置
|
||||
item.elm.style.display = 'none'
|
||||
}
|
||||
// 当所有的层级动画都结束时,再删掉所有元素
|
||||
if (++this.effectIdx === this.effectList.length) {
|
||||
this.innerEffect = false
|
||||
this.effectIdx = 0
|
||||
this.effectList = []
|
||||
this.$emit('effect-end')
|
||||
}
|
||||
}, 1400)
|
||||
return [this.effectList, bottom]
|
||||
} else {
|
||||
return bottom
|
||||
}
|
||||
},
|
||||
// 渲染内容 span
|
||||
renderSpan (vNode, layer) {
|
||||
const options = {
|
||||
key: layer + this.effectIdx + randomString(6),
|
||||
class: ['j-vxe-reload-effect-span', `layer-${layer}`],
|
||||
style: {}
|
||||
}
|
||||
if (layer === 'top') {
|
||||
// 最新渲染的在下面
|
||||
options.style['z-index'] = (9999 - this.effectIdx)
|
||||
}
|
||||
return this.$createElement('span', options, [vNode])
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
return h('div', {
|
||||
class: ['j-vxe-reload-effect-box']
|
||||
}, [this.renderVNode()])
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import * as jvxeTypes from './jvxeTypes'
|
||||
import { installCell, mapCell } from './install'
|
||||
import JVxeTable from './components/JVxeTable'
|
||||
|
||||
import JVxeSlotCell from './components/cells/JVxeSlotCell'
|
||||
import JVxeNormalCell from './components/cells/JVxeNormalCell'
|
||||
import JVxeInputCell from './components/cells/JVxeInputCell'
|
||||
import JVxeDateCell from './components/cells/JVxeDateCell'
|
||||
import JVxeSelectCell from './components/cells/JVxeSelectCell'
|
||||
import JVxeCheckboxCell from './components/cells/JVxeCheckboxCell'
|
||||
import JVxeUploadCell from './components/cells/JVxeUploadCell'
|
||||
import { TagsInputCell, TagsSpanCell } from './components/cells/JVxeTagsCell'
|
||||
import JVxeProgressCell from './components/cells/JVxeProgressCell'
|
||||
import JVxeTextareaCell from './components/cells/JVxeTextareaCell'
|
||||
import JVxeDragSortCell from './components/cells/JVxeDragSortCell'
|
||||
import JVxeDepartSelectCell from './components/cells/JVxeDepartSelectCell'
|
||||
import JVxeUserSelectCell from './components/cells/JVxeUserSelectCell'
|
||||
|
||||
// update--begin--autor:lvdandan-----date:20201216------for:JVxeTable--JVXETypes 【online】代码结构调整,便于online打包
|
||||
// 组件类型
|
||||
export const JVXETypes = jvxeTypes.JVXETypes
|
||||
// update--end--autor:lvdandan-----date:20201216------for:JVxeTable--JVXETypes 【online】代码结构调整,便于online打包
|
||||
|
||||
// 注册自定义组件
|
||||
export const AllCells = {
|
||||
...mapCell(JVXETypes.normal, JVxeNormalCell),
|
||||
...mapCell(JVXETypes.input, JVxeInputCell),
|
||||
...mapCell(JVXETypes.inputNumber, JVxeInputCell),
|
||||
...mapCell(JVXETypes.checkbox, JVxeCheckboxCell),
|
||||
...mapCell(JVXETypes.select, JVxeSelectCell),
|
||||
...mapCell(JVXETypes.selectSearch, JVxeSelectCell), // 下拉搜索
|
||||
...mapCell(JVXETypes.selectMultiple, JVxeSelectCell), // 下拉多选
|
||||
...mapCell(JVXETypes.date, JVxeDateCell),
|
||||
...mapCell(JVXETypes.datetime, JVxeDateCell),
|
||||
...mapCell(JVXETypes.upload, JVxeUploadCell),
|
||||
...mapCell(JVXETypes.textarea, JVxeTextareaCell),
|
||||
|
||||
...mapCell(JVXETypes.tags, TagsInputCell, TagsSpanCell),
|
||||
...mapCell(JVXETypes.progress, JVxeProgressCell),
|
||||
|
||||
...mapCell(JVXETypes.rowDragSort, JVxeDragSortCell),
|
||||
...mapCell(JVXETypes.slot, JVxeSlotCell),
|
||||
...mapCell(JVXETypes.departSelect, JVxeDepartSelectCell),
|
||||
...mapCell(JVXETypes.userSelect, JVxeUserSelectCell)
|
||||
|
||||
/* hidden 是特殊的组件,不在这里注册 */
|
||||
}
|
||||
|
||||
export { installCell, mapCell }
|
||||
|
||||
export default JVxeTable
|
||||
@@ -1,105 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import { getEventPath, evil } from '@/utils/util'
|
||||
import JVxeTable, { AllCells, JVXETypes } from './index'
|
||||
import './less/j-vxe-table.less'
|
||||
// 引入 vxe-table
|
||||
import 'xe-utils'
|
||||
import VXETable, { Grid } from 'vxe-table'
|
||||
import VXETablePluginAntd from 'vxe-table-plugin-antd'
|
||||
import 'vxe-table/lib/index.css'
|
||||
import 'vxe-table-plugin-antd/dist/style.css'
|
||||
import { getEnhancedMixins, installAllCell, installOneCell } from '@/components/jero/JVxeTable/utils/cellUtils'
|
||||
|
||||
// VxeGrid所有的方法映射
|
||||
const VxeGridMethodsMap = {}
|
||||
Object.keys(Grid.methods).forEach(key => {
|
||||
// 使用eval可以避免闭包(但是要注意不要写es6的代码)
|
||||
VxeGridMethodsMap[key] = evil(`(function(){return this.$refs.vxe.${key}.apply(this.$refs.vxe,arguments)})`)
|
||||
})
|
||||
// 将Grid所有的方法都映射(继承)到JVxeTable上
|
||||
JVxeTable.methods = Object.assign({}, VxeGridMethodsMap, JVxeTable.methods)
|
||||
|
||||
// VXETable 全局配置
|
||||
const VXETableSettings = {
|
||||
// z-index 起始值
|
||||
zIndex: 1000,
|
||||
table: {
|
||||
validConfig: {
|
||||
// 校验提示方式:强制使用tooltip
|
||||
message: 'tooltip'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行注册方法
|
||||
Vue.use(VXETable, VXETableSettings)
|
||||
VXETable.use(VXETablePluginAntd)
|
||||
Vue.component(JVxeTable.name, JVxeTable)
|
||||
|
||||
// 注册自定义组件
|
||||
installAllCell(VXETable)
|
||||
|
||||
// 添加事件拦截器 event.clearActived
|
||||
// 比如点击了某个组件的弹出层面板之后,此时被激活单元格不应该被自动关闭,通过返回 false 可以阻止默认的行为。
|
||||
VXETable.interceptor.add('event.clearActived', function (params) {
|
||||
// 获取组件增强
|
||||
const col = params.column.params || {}
|
||||
const interceptor = getEnhancedMixins(col.$type, 'interceptor')
|
||||
// 执行增强
|
||||
const flag = interceptor['event.clearActived'].apply(this, arguments)
|
||||
if (flag === false) {
|
||||
return false
|
||||
}
|
||||
// 此处判断一下event是否存在,防止后续内容报错
|
||||
const path = params.$event ? getEventPath(params.$event) : []
|
||||
for (const p of path) {
|
||||
let className = p.className || ''
|
||||
className = typeof className === 'string' ? className : className.toString()
|
||||
|
||||
/* --- 特殊处理以下组件,点击以下标签时不清空编辑状态 --- */
|
||||
|
||||
// 点击的标签是JInputPop
|
||||
if (className.includes('j-input-pop')) {
|
||||
return false
|
||||
}
|
||||
// 点击的标签是JPopup的弹出层、部门选择、用户选择
|
||||
if (className.includes('j-popup-modal') || className.includes('j-depart-select-modal') || className.includes('j-user-select-modal')) {
|
||||
return false
|
||||
}
|
||||
// 执行增强
|
||||
const flag = interceptor['event.clearActived.className'].apply(this, [className, ...arguments])
|
||||
if (flag === false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 注册map
|
||||
* @param type 类型
|
||||
* @param cell 输入组件
|
||||
* @param span 显示组件,可空,默认为 JVxeNormalCell 组件
|
||||
*/
|
||||
export function mapCell (type, cell, span) {
|
||||
const cells = { [type]: cell }
|
||||
if (span) {
|
||||
cells[type + ':span'] = span
|
||||
}
|
||||
return cells
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册自定义组件
|
||||
*
|
||||
* @param type 类型
|
||||
* @param cell 输入组件
|
||||
* @param span 显示组件,可空,默认为 JVxeNormalCell 组件
|
||||
*/
|
||||
export function installCell (type, cell, span) {
|
||||
const exclude = [JVXETypes.rowNumber, JVXETypes.rowCheckbox, JVXETypes.rowRadio, JVXETypes.rowExpand, JVXETypes.rowDragSort]
|
||||
if (exclude.includes(type)) {
|
||||
throw new Error(`【installCell】不能使用"${type}"作为组件的type,因为这是关键字。`)
|
||||
}
|
||||
Object.assign(AllCells, mapCell(type, cell, span))
|
||||
installOneCell(VXETable, type)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// 组件类型
|
||||
export const JVXETypes = {
|
||||
// 为了防止和 vxe 内置的类型冲突,所以加上一个前缀
|
||||
// 前缀是自动加的,代码中直接用就行(JVXETypes.input)
|
||||
_prefix: 'j-',
|
||||
|
||||
// 行号列
|
||||
rowNumber: 'row-number',
|
||||
// 选择列
|
||||
rowCheckbox: 'row-checkbox',
|
||||
// 单选列
|
||||
rowRadio: 'row-radio',
|
||||
// 展开列
|
||||
rowExpand: 'row-expand',
|
||||
// 上下排序
|
||||
rowDragSort: 'row-drag-sort',
|
||||
|
||||
input: 'input',
|
||||
inputNumber: 'inputNumber',
|
||||
textarea: 'textarea',
|
||||
select: 'select',
|
||||
date: 'date',
|
||||
datetime: 'datetime',
|
||||
checkbox: 'checkbox',
|
||||
upload: 'upload',
|
||||
// 下拉搜索
|
||||
selectSearch: 'select-search',
|
||||
// 下拉多选
|
||||
selectMultiple: 'select-multiple',
|
||||
// 进度条
|
||||
progress: 'progress',
|
||||
// 部门选择
|
||||
departSelect: 'sel_depart',
|
||||
// 用户选择
|
||||
userSelect: 'sel_user',
|
||||
|
||||
// 拖轮Tags(暂无用)
|
||||
tags: 'tags',
|
||||
|
||||
slot: 'slot',
|
||||
normal: 'normal',
|
||||
hidden: 'hidden'
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
@import "size/tiny";
|
||||
|
||||
.j-vxe-table-box {
|
||||
|
||||
// 工具栏
|
||||
.j-vxe-toolbar {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
// 分页器
|
||||
.j-vxe-pagination {
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
|
||||
.ant-pagination-options-size-changer.ant-select {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.show-quick-jumper {
|
||||
.ant-pagination-options-size-changer.ant-select {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更改 header 底色
|
||||
.vxe-table.border--default .vxe-table--header-wrapper,
|
||||
.vxe-table.border--full .vxe-table--header-wrapper,
|
||||
.vxe-table.border--outer .vxe-table--header-wrapper {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 更改 tooltip 校验失败的颜色
|
||||
.vxe-table--tooltip-wrapper.vxe-table--valid-error {
|
||||
background-color: #f5222d !important;
|
||||
}
|
||||
|
||||
// 更改 输入框 校验失败的颜色
|
||||
.col--valid-error > .vxe-cell > .ant-input,
|
||||
.col--valid-error > .vxe-cell > .ant-select .ant-input,
|
||||
.col--valid-error > .vxe-cell > .ant-select .ant-select-selection,
|
||||
.col--valid-error > .vxe-cell > .ant-input-number,
|
||||
.col--valid-error > .vxe-cell > .ant-cascader-picker .ant-cascader-input,
|
||||
.col--valid-error > .vxe-cell > .ant-calendar-picker .ant-calendar-picker-input,
|
||||
.col--valid-error > .vxe-tree-cell > .ant-input,
|
||||
.col--valid-error > .vxe-tree-cell > .ant-select .ant-input,
|
||||
.col--valid-error > .vxe-tree-cell > .ant-select .ant-select-selection,
|
||||
.col--valid-error > .vxe-tree-cell > .ant-input-number,
|
||||
.col--valid-error > .vxe-tree-cell > .ant-cascader-picker .ant-cascader-input,
|
||||
.col--valid-error > .vxe-tree-cell > .ant-calendar-picker .ant-calendar-picker-input {
|
||||
border-color: #f5222d !important;
|
||||
}
|
||||
|
||||
// 拖拽排序列样式
|
||||
.vxe-table .col--row-drag-sort .vxe-cell {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
.j-vxe-reload-effect-box {
|
||||
|
||||
&,
|
||||
.j-vxe-reload-effect-span {
|
||||
display: inline;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.j-vxe-reload-effect-span {
|
||||
|
||||
&.layer-top {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
background-color: white;
|
||||
|
||||
transform-origin: 0 0;
|
||||
animation: reload-effect 1.5s forwards;
|
||||
}
|
||||
|
||||
&.layer-bottom {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 定义动画
|
||||
@keyframes reload-effect {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: rotateX(0);
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
.j-vxe-table-box {
|
||||
|
||||
@height: 24px;
|
||||
@lineHeight: 1.5;
|
||||
@spacing: 4px;
|
||||
@fontSize: 14px;
|
||||
@borderRadius: 2px;
|
||||
|
||||
&.size--tiny {
|
||||
|
||||
.vxe-table--header .vxe-cell--checkbox {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
right: 1px;
|
||||
}
|
||||
|
||||
.vxe-table--body .vxe-cell--checkbox {
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.vxe-cell {
|
||||
padding: 0 5px;
|
||||
font-size: @fontSize;
|
||||
line-height: @lineHeight;
|
||||
}
|
||||
|
||||
.vxe-table .vxe-header--column .vxe-cell {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.vxe-body--column.col--actived {
|
||||
padding: 0;
|
||||
|
||||
.vxe-cell {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ant输入框
|
||||
.ant-input,
|
||||
// ant下拉框
|
||||
.ant-select-selection {
|
||||
padding: 2px @spacing;
|
||||
height: @height;
|
||||
font-size: @fontSize;
|
||||
border-radius: @borderRadius;
|
||||
line-height: @lineHeight;
|
||||
}
|
||||
|
||||
// 输入框图标对齐
|
||||
.ant-input-affix-wrapper {
|
||||
& .ant-input-prefix {
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
& .ant-input:not(:first-child) {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮 addon
|
||||
.ant-input-group-addon {
|
||||
border-color: transparent;
|
||||
border-radius: @borderRadius;
|
||||
}
|
||||
|
||||
|
||||
// ant下拉多选框
|
||||
.ant-select-selection--multiple {
|
||||
min-height: @height;
|
||||
|
||||
& .ant-select-selection__rendered > ul > li {
|
||||
height: calc(@height - 6px);
|
||||
font-size: calc(@fontSize - 2px);
|
||||
margin-top: 0;
|
||||
line-height: @lineHeight;
|
||||
padding: 0 18px 0 4px;
|
||||
|
||||
}
|
||||
|
||||
& .ant-select-selection__clear,
|
||||
& .ant-select-arrow {
|
||||
top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ant按钮
|
||||
.ant-upload {
|
||||
width: 100%;
|
||||
|
||||
.ant-btn {
|
||||
width: 100%;
|
||||
height: @height;
|
||||
padding: 0 8px;
|
||||
font-size: @fontSize;
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
border-radius: @borderRadius;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-selection__rendered {
|
||||
line-height: @lineHeight;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
|
||||
// 工具栏
|
||||
.j-vxe-toolbar {
|
||||
margin-bottom: 4px;
|
||||
|
||||
.ant-form-item-label,
|
||||
.ant-form-item-control {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.ant-form-inline .ant-form-item {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** 内置属性 */
|
||||
|
||||
.vxe-table.size--tiny {
|
||||
& .vxe-table--expanded {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
& .vxe-body--expanded-cell {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.size--tiny .vxe-loading .vxe-loading--spinner {
|
||||
width: 38px;
|
||||
height: 38px
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny .vxe-body--column.col--ellipsis,
|
||||
.vxe-table.size--tiny .vxe-footer--column.col--ellipsis,
|
||||
.vxe-table.size--tiny .vxe-header--column.col--ellipsis,
|
||||
.vxe-table.vxe-editable.size--tiny .vxe-body--column {
|
||||
height: @height;
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny .vxe-table--empty-block,
|
||||
.vxe-table.size--tiny .vxe-table--empty-placeholder {
|
||||
min-height: @height;
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny .vxe-body--column:not(.col--ellipsis),
|
||||
.vxe-table.size--tiny .vxe-footer--column:not(.col--ellipsis),
|
||||
.vxe-table.size--tiny .vxe-header--column:not(.col--ellipsis) {
|
||||
padding: 4px 0
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny .vxe-cell .vxe-default-input,
|
||||
.vxe-table.size--tiny .vxe-cell .vxe-default-select,
|
||||
.vxe-table.size--tiny .vxe-cell .vxe-default-textarea {
|
||||
height: @height;
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny .vxe-cell .vxe-default-input[type=date]::-webkit-inner-spin-button {
|
||||
margin-top: 1px
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny.virtual--x .col--ellipsis .vxe-cell,
|
||||
.vxe-table.size--tiny.virtual--y .col--ellipsis .vxe-cell,
|
||||
.vxe-table.size--tiny .vxe-body--column.col--ellipsis .vxe-cell,
|
||||
.vxe-table.size--tiny .vxe-footer--column.col--ellipsis .vxe-cell,
|
||||
.vxe-table.size--tiny .vxe-header--column.col--ellipsis .vxe-cell {
|
||||
max-height: @height;
|
||||
}
|
||||
|
||||
.vxe-table.size--tiny .vxe-cell--checkbox .vxe-checkbox--icon,
|
||||
.vxe-table.size--tiny .vxe-cell--radio .vxe-radio--icon {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
|
||||
.vxe-table.size--tiny .vxe-table--filter-option > .vxe-checkbox--icon,
|
||||
.vxe-table.size--small .vxe-table--filter-option > .vxe-checkbox--icon {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.vxe-modal--wrapper.size--tiny .vxe-export--panel-column-option > .vxe-checkbox--icon,
|
||||
.vxe-modal--wrapper.size--small .vxe-export--panel-column-option > .vxe-checkbox--icon {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.vxe-grid.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-toolbar.size--tiny {
|
||||
font-size: 12px;
|
||||
height: 46px
|
||||
}
|
||||
|
||||
.vxe-toolbar.size--tiny .vxe-custom--option > .vxe-checkbox--icon {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.vxe-pager.size--tiny {
|
||||
font-size: 12px;
|
||||
height: @height;
|
||||
}
|
||||
|
||||
.vxe-checkbox.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-checkbox.size--tiny .vxe-checkbox--icon {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.vxe-radio-button.size--tiny .vxe-radio--label {
|
||||
line-height: 26px
|
||||
}
|
||||
|
||||
.vxe-radio.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-radio.size--tiny .vxe-radio--icon {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.vxe-input.size--tiny {
|
||||
font-size: 12px;
|
||||
height: @height;
|
||||
}
|
||||
|
||||
.vxe-input.size--tiny .vxe-input--inner[type=date]::-webkit-inner-spin-button,
|
||||
.vxe-input.size--tiny .vxe-input--inner[type=month]::-webkit-inner-spin-button,
|
||||
.vxe-input.size--tiny .vxe-input--inner[type=week]::-webkit-inner-spin-button {
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
.vxe-dropdown--panel.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-textarea--autosize.size--tiny,
|
||||
.vxe-textarea.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-textarea.size--tiny:not(.is--autosize) {
|
||||
min-height: @height;
|
||||
}
|
||||
|
||||
.vxe-button.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-button.size--tiny.type--button {
|
||||
height: @height;
|
||||
}
|
||||
|
||||
.vxe-button.size--tiny.type--button.is--circle {
|
||||
min-width: @height;
|
||||
}
|
||||
|
||||
.vxe-button.size--tiny.type--button.is--round {
|
||||
border-radius: 14px
|
||||
}
|
||||
|
||||
.vxe-button.size--tiny .vxe-button--icon,
|
||||
.vxe-button.size--tiny .vxe-button--loading-icon {
|
||||
min-width: 12px
|
||||
}
|
||||
|
||||
.vxe-modal--wrapper.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-form.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-form.size--tiny .vxe-form--item-inner {
|
||||
min-height: 30px
|
||||
}
|
||||
|
||||
.vxe-form.size--tiny .vxe-default-input[type=reset],
|
||||
.vxe-form.size--tiny .vxe-default-input[type=submit] {
|
||||
line-height: 26px
|
||||
}
|
||||
|
||||
.vxe-form.size--tiny .vxe-default-input,
|
||||
.vxe-form.size--tiny .vxe-default-select {
|
||||
height: @height;
|
||||
}
|
||||
|
||||
.vxe-select--panel.size--tiny,
|
||||
.vxe-select.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.vxe-select--panel.size--tiny .vxe-optgroup--title,
|
||||
.vxe-select--panel.size--tiny .vxe-select-option {
|
||||
height: 24px;
|
||||
line-height: 24px
|
||||
}
|
||||
|
||||
.vxe-switch.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
|
||||
.vxe-pulldown--panel.size--tiny,
|
||||
.vxe-pulldown.size--tiny {
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
import PropTypes from 'ant-design-vue/es/_util/vue-types'
|
||||
import { filterDictText } from '@/components/dict/JDictSelectUtil'
|
||||
import { getEnhancedMixins, JVXERenderType, replaceProps } from '@/components/jero/JVxeTable/utils/cellUtils'
|
||||
|
||||
// noinspection JSUnusedLocalSymbols
|
||||
export default {
|
||||
inject: {
|
||||
getParentContainer: { default: () => (node) => node.parentNode }
|
||||
},
|
||||
props: {
|
||||
value: PropTypes.any,
|
||||
row: PropTypes.object,
|
||||
column: PropTypes.object,
|
||||
// 组件参数
|
||||
params: PropTypes.object,
|
||||
// 渲染选项
|
||||
renderOptions: PropTypes.object,
|
||||
// 渲染类型
|
||||
renderType: PropTypes.string.def('default')
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
innerValue: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
caseId () {
|
||||
return this.renderOptions.caseId
|
||||
},
|
||||
originColumn () {
|
||||
return this.column.params || {}
|
||||
},
|
||||
$type () {
|
||||
return this.originColumn.type
|
||||
},
|
||||
rows () {
|
||||
return this.params.data
|
||||
},
|
||||
fullDataLength () {
|
||||
return this.params.$table.tableFullData.length
|
||||
},
|
||||
rowIndex () {
|
||||
return this.params.rowIndex
|
||||
},
|
||||
columnIndex () {
|
||||
return this.params.columnIndex
|
||||
},
|
||||
cellProps () {
|
||||
const { originColumn: col, renderOptions } = this
|
||||
|
||||
const props = {}
|
||||
|
||||
// 输入占位符
|
||||
props.placeholder = replaceProps(col, col.placeholder)
|
||||
|
||||
// 解析props
|
||||
if (typeof col.props === 'object') {
|
||||
Object.keys(col.props).forEach(key => {
|
||||
props[key] = replaceProps(col, col.props[key])
|
||||
})
|
||||
}
|
||||
|
||||
// 判断是否是禁用的列
|
||||
props.disabled = (typeof col.disabled === 'boolean' ? col.disabled : props.disabled)
|
||||
|
||||
// TODO 判断是否是禁用的行
|
||||
// if (props['disabled'] !== true) {
|
||||
// props['disabled'] = ((this.disabledRowIds || []).indexOf(row.id) !== -1)
|
||||
// }
|
||||
|
||||
// 判断是否禁用所有组件
|
||||
if (renderOptions.disabled === true) {
|
||||
props.disabled = true
|
||||
}
|
||||
|
||||
return props
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$type: {
|
||||
immediate: true,
|
||||
handler ($type) {
|
||||
this.enhanced = getEnhancedMixins($type)
|
||||
this.listeners = getListeners.call(this)
|
||||
}
|
||||
},
|
||||
value: {
|
||||
immediate: true,
|
||||
handler (val) {
|
||||
let value = val
|
||||
|
||||
// 验证值格式
|
||||
const originValue = this.row[this.column.property]
|
||||
const getValue = this.enhanced.getValue.call(this, originValue)
|
||||
if (originValue !== getValue) {
|
||||
// 值格式不正确,重新赋值
|
||||
value = getValue
|
||||
vModel.call(this, value)
|
||||
}
|
||||
|
||||
this.innerValue = this.enhanced.setValue.call(this, value)
|
||||
|
||||
// 判断是否启用翻译
|
||||
if (this.renderType === JVXERenderType.spaner && this.enhanced.translate.enabled) {
|
||||
const res = this.enhanced.translate.handler.call(this, value)
|
||||
// 异步翻译,目前仅【多级联动】使用
|
||||
if (res instanceof Promise) {
|
||||
res.then(value => {
|
||||
this.innerValue = value
|
||||
})
|
||||
} else {
|
||||
this.innerValue = res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** 通用处理change事件 */
|
||||
handleChangeCommon (value) {
|
||||
const handle = this.enhanced.getValue.call(this, value)
|
||||
this.trigger('change', { value: handle })
|
||||
// 触发valueChange事件
|
||||
this.parentTrigger('valueChange', {
|
||||
type: this.$type,
|
||||
value: handle,
|
||||
oldValue: this.value,
|
||||
col: this.originColumn,
|
||||
rowIndex: this.params.rowIndex,
|
||||
columnIndex: this.params.columnIndex
|
||||
})
|
||||
},
|
||||
/** 通用处理blur事件 */
|
||||
handleBlurCommon (value) {
|
||||
this.trigger('blur', { value })
|
||||
},
|
||||
|
||||
/**
|
||||
* 如果事件存在的话,就触发
|
||||
* @param name 事件名
|
||||
* @param event 事件参数
|
||||
* @param args 其他附带参数
|
||||
*/
|
||||
trigger (name, event, args = []) {
|
||||
const listener = this.listeners[name]
|
||||
if (typeof listener === 'function') {
|
||||
if (typeof event === 'object') {
|
||||
event = this.packageEvent(name, event)
|
||||
}
|
||||
listener(event, ...args)
|
||||
}
|
||||
},
|
||||
parentTrigger (name, event, args = []) {
|
||||
args.unshift(this.packageEvent(name, event))
|
||||
this.trigger('trigger', name, args)
|
||||
},
|
||||
packageEvent (name, event = {}) {
|
||||
event.row = this.row
|
||||
event.column = this.column
|
||||
// online增强参数兼容
|
||||
event.column.key = this.column.property
|
||||
event.cellTarget = this
|
||||
if (!event.type) {
|
||||
event.type = name
|
||||
}
|
||||
if (!event.cellType) {
|
||||
event.cellType = this.$type
|
||||
}
|
||||
// 是否校验表单,默认为true
|
||||
if (typeof event.validate !== 'boolean') {
|
||||
event.validate = true
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
/**
|
||||
* 【自定义增强】用于实现一些增强事件
|
||||
* 【注】这里只是定义接口,具体功能需要到各个组件内实现(也有部分功能实现)
|
||||
* 【注】该属性不是Vue官方属性,是JVxeTable组件自定义的
|
||||
* 所以方法内的 this 指向并不是当前组件,而是方法自身,
|
||||
* 也就是说并不能 this 打点调实例里的任何方法
|
||||
*/
|
||||
enhanced: {
|
||||
// 注册参数(详见:https://xuliangzhan_admin.gitee.io/vxe-table/#/table/renderer/edit)
|
||||
installOptions: {
|
||||
// 自动聚焦的 class 类名
|
||||
autofocus: ''
|
||||
},
|
||||
// 事件拦截器(用于兼容)
|
||||
interceptor: {
|
||||
// 已实现:event.clearActived
|
||||
// 说明:比如点击了某个组件的弹出层面板之后,此时被激活单元格不应该被自动关闭,通过返回 false 可以阻止默认的行为。
|
||||
'event.clearActived' (params, event, target) {
|
||||
return true
|
||||
},
|
||||
// 自定义:event.clearActived.className
|
||||
// 说明:比原生的多了一个参数:className,用于判断点击的元素的样式名(递归到顶层)
|
||||
'event.clearActived.className' (params, event, target) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
// 【功能开关】
|
||||
switches: {
|
||||
// 是否使用 editRender 模式(仅当前组件,并非全局)
|
||||
// 如果设为true,则表头上方会出现一个可编辑的图标
|
||||
editRender: true,
|
||||
// false = 组件触发后可视);true = 组件一直可视
|
||||
visible: false
|
||||
},
|
||||
// 【切面增强】切面事件处理,一般在某些方法执行后同步执行
|
||||
aopEvents: {
|
||||
// 单元格被激活编辑时会触发该事件
|
||||
editActived () {
|
||||
},
|
||||
// 单元格编辑状态下被关闭时会触发该事件
|
||||
editClosed () {
|
||||
}
|
||||
},
|
||||
// 【翻译增强】可以实现例如select组件保存的value,但是span模式下需要显示成text
|
||||
translate: {
|
||||
// 是否启用翻译
|
||||
enabled: false,
|
||||
/**
|
||||
* 【翻译处理方法】如果handler留空,则使用默认的翻译方法
|
||||
* (this指向当前组件)
|
||||
*
|
||||
* @param value 需要翻译的值
|
||||
* @returns{*} 返回翻译后的数据
|
||||
*/
|
||||
handler (value) {
|
||||
// 默认翻译方法
|
||||
return filterDictText(this.column.own.options, value)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 【获取值增强】组件抛出的值
|
||||
* (this指向当前组件)
|
||||
*
|
||||
* @param value 保存到数据库里的值
|
||||
* @returns{*} 返回处理后的值
|
||||
*/
|
||||
getValue (value) {
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* 【设置值增强】设置给组件的值
|
||||
* (this指向当前组件)
|
||||
*
|
||||
* @param value 组件触发的值
|
||||
* @returns{*} 返回处理后的值
|
||||
*/
|
||||
setValue (value) {
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* 【新增行增强】在用户点击新增时触发的事件,返回新行的默认值
|
||||
*
|
||||
* @param row 行数据
|
||||
* @param column 列配置,.own 是用户配置的参数
|
||||
* @param $table vxe 实例
|
||||
* @param renderOptions 渲染选项
|
||||
* @param params 可以在这里获取 $table
|
||||
*
|
||||
* @returns 返回新值
|
||||
*/
|
||||
createValue ({ row, column, $table, renderOptions, params }) {
|
||||
return (column.params || {}).defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getListeners () {
|
||||
const listeners = Object.assign({}, (this.renderOptions.listeners || {}))
|
||||
if (!listeners.change) {
|
||||
listeners.change = async (event) => {
|
||||
vModel.call(this, event.value)
|
||||
await this.$nextTick()
|
||||
// 处理 change 事件相关逻辑(例如校验)
|
||||
this.params.$table.updateStatus(this.params)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
}
|
||||
|
||||
export function vModel (value, row, property) {
|
||||
if (!row) {
|
||||
row = this.row
|
||||
}
|
||||
if (!property) {
|
||||
property = this.column.property
|
||||
}
|
||||
this.$set(row, property, value)
|
||||
}
|
||||
|
||||
/** 模拟触发事件 */
|
||||
export function dispatchEvent ({ cell, $event }, className, handler) {
|
||||
// alwaysEdit 下不模拟触发事件,否者会导致触发两次
|
||||
if (this && this.alwaysEdit) {
|
||||
return
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
const element = cell ? cell.getElementsByClassName(className) : null
|
||||
if (element && element.length > 0) {
|
||||
if (typeof handler === 'function') {
|
||||
handler(element[0])
|
||||
} else {
|
||||
// 模拟触发点击事件
|
||||
if ($event) {
|
||||
element[0].dispatchEvent($event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 10)
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
import store from '@/store/'
|
||||
import { randomUUID } from '@/utils/util'
|
||||
// vxe socket
|
||||
const vs = {
|
||||
// 页面唯一 id,用于标识同一用户,不同页面的websocket
|
||||
pageId: randomUUID(),
|
||||
// webSocket 对象
|
||||
ws: null,
|
||||
// 一些常量
|
||||
constants: {
|
||||
// 消息类型
|
||||
TYPE: 'type',
|
||||
// 消息数据
|
||||
DATA: 'data',
|
||||
// 消息类型:心跳检测
|
||||
TYPE_HB: 'heart_beat',
|
||||
// 消息类型:通用数据传递
|
||||
TYPE_CSD: 'common_send_date',
|
||||
// 消息类型:更新vxe table数据
|
||||
TYPE_UVT: 'update_vxe_table'
|
||||
},
|
||||
// 心跳检测
|
||||
heartCheck: {
|
||||
// 间隔时间,间隔多久发送一次心跳消息
|
||||
interval: 10000,
|
||||
// 心跳消息超时时间,心跳消息多久没有回复后重连
|
||||
timeout: 6000,
|
||||
timeoutTimer: null,
|
||||
clear () {
|
||||
clearTimeout(this.timeoutTimer)
|
||||
return this
|
||||
},
|
||||
start () {
|
||||
vs.sendMessage(vs.constants.TYPE_HB, '')
|
||||
// 如果超过一定时间还没重置,说明后端主动断开了
|
||||
this.timeoutTimer = window.setTimeout(() => {
|
||||
vs.reconnect()
|
||||
}, this.timeout)
|
||||
return this
|
||||
},
|
||||
// 心跳消息返回
|
||||
back () {
|
||||
this.clear()
|
||||
window.setTimeout(() => this.start(), this.interval)
|
||||
}
|
||||
},
|
||||
|
||||
/** 初始化 WebSocket */
|
||||
initialWebSocket () {
|
||||
if (this.ws === null) {
|
||||
const userId = store.getters.userInfo.id
|
||||
const domain = window._CONFIG.domianWebSocketURL.replace('https://', 'wss://').replace('http://', 'ws://')
|
||||
const url = `${domain}/vxeSocket/${userId}/${this.pageId}`
|
||||
|
||||
this.ws = new WebSocket(url)
|
||||
this.ws.onopen = this.on.open.bind(this)
|
||||
this.ws.onerror = this.on.error.bind(this)
|
||||
this.ws.onmessage = this.on.message.bind(this)
|
||||
this.ws.onclose = this.on.close.bind(this)
|
||||
|
||||
console.log('this.ws: ', this.ws)
|
||||
}
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
sendMessage (type, message) {
|
||||
try {
|
||||
const ws = this.ws
|
||||
if (ws != null && ws.readyState === ws.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
type: type,
|
||||
data: message
|
||||
}))
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('【VXEWebSocket】发送消息失败:(' + err.code + ')')
|
||||
}
|
||||
},
|
||||
|
||||
/** 绑定全局VXE表格 */
|
||||
tableMap: new Map(),
|
||||
CSDMap: new Map(),
|
||||
/** 添加绑定 */
|
||||
addBind (map, key, value) {
|
||||
const binds = map.get(key)
|
||||
if (Array.isArray(binds)) {
|
||||
binds.push(value)
|
||||
} else {
|
||||
map.set(key, [value])
|
||||
}
|
||||
},
|
||||
/** 移除绑定 */
|
||||
removeBind (map, key, value) {
|
||||
const binds = map.get(key)
|
||||
if (Array.isArray(binds)) {
|
||||
for (let i = 0; i < binds.length; i++) {
|
||||
const bind = binds[i]
|
||||
if (bind === value) {
|
||||
binds.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (binds.length === 0) {
|
||||
map.delete(key)
|
||||
}
|
||||
} else {
|
||||
map.delete(key)
|
||||
}
|
||||
},
|
||||
// 呼叫绑定的表单
|
||||
callBind (map, key, callback) {
|
||||
const binds = map.get(key)
|
||||
if (Array.isArray(binds)) {
|
||||
binds.forEach(callback)
|
||||
}
|
||||
},
|
||||
|
||||
lockReconnect: false,
|
||||
/** 尝试重连 */
|
||||
reconnect () {
|
||||
if (this.lockReconnect) return
|
||||
this.lockReconnect = true
|
||||
setTimeout(() => {
|
||||
if (this.ws && this.ws.close) {
|
||||
this.ws.close()
|
||||
}
|
||||
this.ws = null
|
||||
console.info('【VXEWebSocket】尝试重连...')
|
||||
this.initialWebSocket()
|
||||
this.lockReconnect = false
|
||||
}, 5000)
|
||||
},
|
||||
|
||||
on: {
|
||||
open () {
|
||||
console.log('【VXEWebSocket】连接成功')
|
||||
this.heartCheck.start()
|
||||
},
|
||||
error (e) {
|
||||
console.warn('【VXEWebSocket】连接发生错误:', e)
|
||||
this.reconnect()
|
||||
},
|
||||
message (e) {
|
||||
// 解析消息
|
||||
let json
|
||||
try {
|
||||
json = JSON.parse(e.data)
|
||||
} catch (e) {
|
||||
console.warn('【VXEWebSocket】收到无法解析的消息:', e.data)
|
||||
return
|
||||
}
|
||||
const type = json[this.constants.TYPE]
|
||||
const data = json[this.constants.DATA]
|
||||
switch (type) {
|
||||
// 心跳检测
|
||||
case this.constants.TYPE_HB:
|
||||
this.heartCheck.back()
|
||||
break
|
||||
// 通用数据传递
|
||||
case this.constants.TYPE_CSD:
|
||||
this.callBind(this.CSDMap, data.key, (fn) => fn.apply(this, data.args))
|
||||
break
|
||||
// 更新form数据
|
||||
case this.constants.TYPE_UVT:
|
||||
this.callBind(this.tableMap, data.socketKey, (vm) => this.onVM.onUpdateTable.apply(vm, data.args))
|
||||
break
|
||||
default:
|
||||
console.warn('【VXEWebSocket】收到不识别的消息类型:' + type)
|
||||
break
|
||||
}
|
||||
},
|
||||
close (e) {
|
||||
console.log('【VXEWebSocket】连接被关闭:', e)
|
||||
this.reconnect()
|
||||
}
|
||||
},
|
||||
|
||||
onVM: {
|
||||
/** 收到更新表格的消息 */
|
||||
onUpdateTable (row, caseId) {
|
||||
// 判断是不是自己发的消息
|
||||
if (this.caseId !== caseId) {
|
||||
const tableRow = this.getIfRowById(row.id).row
|
||||
// 局部保更新数据
|
||||
if (tableRow) {
|
||||
// 特殊处理拖轮状态
|
||||
if (row.tug_status && tableRow.tug_status) {
|
||||
row.tug_status = Object.assign({}, tableRow.tug_status, row.tug_status)
|
||||
}
|
||||
// 判断是否启用重载特效
|
||||
if (this.reloadEffect) {
|
||||
this.$set(this.reloadEffectRowKeysMap, row.id, true)
|
||||
}
|
||||
Object.keys(row).forEach(key => {
|
||||
if (key !== 'id') {
|
||||
this.$set(tableRow, key, row[key])
|
||||
}
|
||||
})
|
||||
this.$refs.vxe.reloadRow(tableRow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 是否开启使用 webSocket 无痕刷新
|
||||
socketReload: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
socketKey: {
|
||||
type: String,
|
||||
default: 'vxe-default'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
mounted () {
|
||||
if (this.socketReload) {
|
||||
vs.initialWebSocket()
|
||||
vs.addBind(vs.tableMap, this.socketKey, this)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** 发送socket消息更新行 */
|
||||
socketSendUpdateRow (row) {
|
||||
vs.sendMessage(vs.constants.TYPE_UVT, {
|
||||
socketKey: this.socketKey,
|
||||
args: [row, this.caseId]
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
beforeDestroy () {
|
||||
vs.removeBind(vs.tableMap, this.socketKey, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加WebSocket通用数据传递绑定,相同的key可以添加多个方法绑定
|
||||
* @param key 唯一key
|
||||
* @param fn 当消息来的时候触发的回调方法
|
||||
*/
|
||||
export function addBindSocketCSD (key, fn) {
|
||||
if (typeof fn === 'function') {
|
||||
vs.addBind(vs.CSDMap, key, fn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除WebSocket通用数据传递绑定
|
||||
* @param key 唯一key
|
||||
* @param fn 要移除的方法,必须和添加时的方法内存层面上保持一致才可以正确移除
|
||||
*/
|
||||
export function removeBindSocketCSD (key, fn) {
|
||||
if (typeof fn === 'function') {
|
||||
vs.removeBind(vs.CSDMap, key, fn)
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import { AllCells, JVXETypes } from '@/components/jero/JVxeTable'
|
||||
import JVxeCellMixins from '../mixins/JVxeCellMixins'
|
||||
|
||||
export const JVXERenderType = {
|
||||
editer: 'editer',
|
||||
spaner: 'spaner',
|
||||
default: 'default'
|
||||
}
|
||||
|
||||
/** 安装所有vxe组件 */
|
||||
export function installAllCell (VXETable) {
|
||||
// 遍历所有组件批量注册
|
||||
Object.keys(AllCells).forEach(type => installOneCell(VXETable, type))
|
||||
}
|
||||
|
||||
/** 安装单个vxe组件 */
|
||||
export function installOneCell (VXETable, type) {
|
||||
const switches = getEnhancedMixins(type, 'switches')
|
||||
if (switches.editRender === false) {
|
||||
installCellRender(VXETable, type, AllCells[type])
|
||||
} else {
|
||||
installEditRender(VXETable, type, AllCells[type])
|
||||
}
|
||||
}
|
||||
|
||||
/** 注册可编辑组件 */
|
||||
export function installEditRender (VXETable, type, comp, spanComp) {
|
||||
// 获取当前组件的增强
|
||||
const enhanced = getEnhancedMixins(type)
|
||||
// span 组件
|
||||
if (!spanComp && AllCells[type + ':span']) {
|
||||
spanComp = AllCells[type + ':span']
|
||||
} else {
|
||||
spanComp = AllCells[JVXETypes.normal]
|
||||
}
|
||||
// 如果是常显的cell,显示模板和编辑模板都用编辑模板
|
||||
const cellRender = enhanced.switches.visible ? createRender(comp, enhanced, JVXERenderType.editer) : createRender(spanComp, enhanced, JVXERenderType.spaner)
|
||||
// 添加渲染
|
||||
VXETable.renderer.add(JVXETypes._prefix + type, {
|
||||
// 可编辑模板
|
||||
renderEdit: createRender(comp, enhanced, JVXERenderType.editer),
|
||||
// 显示模板
|
||||
renderCell: cellRender,
|
||||
// 增强注册
|
||||
...enhanced.installOptions
|
||||
})
|
||||
}
|
||||
|
||||
/** 注册普通组件 */
|
||||
export function installCellRender (VXETable, type, comp = AllCells[JVXETypes.normal]) {
|
||||
// 获取当前组件的增强
|
||||
const enhanced = getEnhancedMixins(type)
|
||||
VXETable.renderer.add(JVXETypes._prefix + type, {
|
||||
// 默认显示模板
|
||||
renderDefault: createRender(comp, enhanced, JVXERenderType.default),
|
||||
// 增强注册
|
||||
...enhanced.installOptions
|
||||
})
|
||||
}
|
||||
|
||||
export function createRender (comp, enhanced, renderType) {
|
||||
return function (h, renderOptions, params) {
|
||||
return [h(comp, {
|
||||
props: {
|
||||
value: params.row[params.column.property],
|
||||
row: params.row,
|
||||
column: params.column,
|
||||
params: params,
|
||||
renderOptions: renderOptions,
|
||||
renderType: renderType
|
||||
}
|
||||
})]
|
||||
}
|
||||
}
|
||||
|
||||
// 已混入的组件增强
|
||||
const AllCellsMixins = new Map()
|
||||
|
||||
/** 获取某个组件的增强 */
|
||||
export function getEnhanced (type) {
|
||||
const cell = AllCells[type]
|
||||
if (cell && cell.enhanced) {
|
||||
return cell.enhanced
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个组件的增强(混入默认值)
|
||||
*
|
||||
* @param type JVXETypes
|
||||
* @param name 可空,增强名称,留空返回所有增强
|
||||
*/
|
||||
export function getEnhancedMixins (type, name) {
|
||||
const getByName = (e) => name ? e[name] : e
|
||||
if (AllCellsMixins.has(type)) {
|
||||
return getByName(AllCellsMixins.get(type))
|
||||
}
|
||||
const defEnhanced = JVxeCellMixins.enhanced
|
||||
const enhanced = getEnhanced(type)
|
||||
if (enhanced) {
|
||||
Object.keys(defEnhanced).forEach(key => {
|
||||
const def = defEnhanced[key]
|
||||
if (Object.prototype.hasOwnProperty.call(enhanced, key)) {
|
||||
// 方法如果存在就不覆盖
|
||||
if (typeof def !== 'function' && typeof def !== 'string') {
|
||||
enhanced[key] = Object.assign({}, def, enhanced[key])
|
||||
}
|
||||
} else {
|
||||
enhanced[key] = def
|
||||
}
|
||||
})
|
||||
AllCellsMixins.set(type, enhanced)
|
||||
return getByName(enhanced)
|
||||
}
|
||||
AllCellsMixins.set(type, defEnhanced)
|
||||
return getByName(defEnhanced)
|
||||
}
|
||||
|
||||
/** 辅助方法:替换${...}变量 */
|
||||
export function replaceProps (col, value) {
|
||||
if (value && typeof value === 'string') {
|
||||
let text = value
|
||||
text = text.replace(/\${title}/g, col.title)
|
||||
text = text.replace(/\${key}/g, col.key)
|
||||
text = text.replace(/\${defaultValue}/g, col.defaultValue)
|
||||
return text
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
import { getVmParentByName } from '@/utils/util'
|
||||
import { JVXETypes } from '@comp/jero/JVxeTable'
|
||||
|
||||
export const VALIDATE_FAILED = Symbol('')
|
||||
|
||||
/**
|
||||
* 获取指定的 $refs 对象
|
||||
* 有时候可能会遇到组件未挂载到页面中的情况,导致无法获取 $refs 中的某个对象
|
||||
* 这个方法可以等待挂载完成之后再返回 $refs 的对象,避免报错
|
||||
* @author sunjianlei
|
||||
**/
|
||||
export function getRefPromise (vm, name) {
|
||||
return new Promise((resolve) => {
|
||||
(function next () {
|
||||
const ref = vm.$refs[name]
|
||||
if (ref) {
|
||||
resolve(ref)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
next()
|
||||
}, 10)
|
||||
}
|
||||
})()
|
||||
})
|
||||
}
|
||||
|
||||
/** 获取某一数字输入框列中的最大的值 */
|
||||
export function getInputNumberMaxValue (col, rowsValues) {
|
||||
let maxNum = 0
|
||||
Object.values(rowsValues).forEach((rowValue, index) => {
|
||||
const val = rowValue[col.key]; let num
|
||||
try {
|
||||
num = Number.parseFloat(val)
|
||||
} catch {
|
||||
num = 0
|
||||
}
|
||||
// 把首次循环的结果当成最大值
|
||||
if (index === 0) {
|
||||
maxNum = num
|
||||
} else {
|
||||
maxNum = (num > maxNum) ? num : maxNum
|
||||
}
|
||||
})
|
||||
return maxNum
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 根据 tagName 获取父级节点
|
||||
*
|
||||
* @param dom 一级dom节点
|
||||
* @param tagName 标签名,不区分大小写
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
export function getParentNodeByTagName (dom, tagName = 'body') {
|
||||
if (tagName === 'body') {
|
||||
return document.body
|
||||
}
|
||||
if (dom.parentNode) {
|
||||
if (dom.parentNode.tagName.toLowerCase() === tagName.trim().toLowerCase()) {
|
||||
return dom.parentNode
|
||||
} else {
|
||||
return getParentNodeByTagName(dom.parentNode, tagName)
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vxe columns 封装成高级查询可识别的选项
|
||||
* @param columns
|
||||
* @param handler 单独处理方法
|
||||
*/
|
||||
export function vxePackageToSuperQuery (columns, handler) {
|
||||
if (Array.isArray(columns)) {
|
||||
// 高级查询所需要的参数
|
||||
const fieldList = []
|
||||
// 遍历列
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const col = columns[i]
|
||||
if (col.type === JVXETypes.rowCheckbox ||
|
||||
col.type === JVXETypes.rowRadio ||
|
||||
col.type === JVXETypes.rowExpand ||
|
||||
col.type === JVXETypes.rowNumber
|
||||
) {
|
||||
continue
|
||||
}
|
||||
const field = {
|
||||
type: 'string',
|
||||
value: col.key,
|
||||
text: col.title,
|
||||
dictCode: col.dictCode || col.dict
|
||||
}
|
||||
if (col.type === JVXETypes.date || col.type === JVXETypes.datetime) {
|
||||
field.type = col.type
|
||||
field.format = col.format
|
||||
}
|
||||
if (col.type === JVXETypes.inputNumber) {
|
||||
field.type = 'int'
|
||||
}
|
||||
if (Array.isArray(col.options)) {
|
||||
field.options = col.options
|
||||
}
|
||||
if (typeof handler === 'function') {
|
||||
Object.assign(field, handler(col))
|
||||
}
|
||||
fieldList.push(field)
|
||||
}
|
||||
return fieldList
|
||||
} else {
|
||||
console.error('columns必须是一个数组')
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性验证主表单和所有的次表单
|
||||
* @param form 主表单 form 对象
|
||||
* @param cases 接收一个数组,每项都是一个JVxeTable实例
|
||||
* @param autoJumpTab
|
||||
* @returns {Promise<any>}
|
||||
* @author sunjianlei
|
||||
*/
|
||||
export async function validateFormAndTables (form, cases, autoJumpTab) {
|
||||
if (!(form && typeof form.validateFields === 'function')) {
|
||||
throw new Error(`form 参数需要的是一个form对象,而传入的却是${typeof form}`)
|
||||
}
|
||||
let dataMap = {}
|
||||
const values = await new Promise((resolve, reject) => {
|
||||
// 验证主表表单
|
||||
form.validateFields((err, values) => {
|
||||
err ? reject({ error: VALIDATE_FAILED, originError: err }) : resolve(values)
|
||||
})
|
||||
})
|
||||
Object.assign(dataMap, { formValue: values })
|
||||
// 验证所有子表的表单
|
||||
const subData = await validateTables(cases, autoJumpTab)
|
||||
// 合并最终数据
|
||||
dataMap = Object.assign(dataMap, { tablesValue: subData })
|
||||
return dataMap
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性验证主表单和所有的次表单
|
||||
* @param form 主表单 form 对象
|
||||
* @param formData
|
||||
* @param cases 接收一个数组,每项都是一个JVxeTable实例
|
||||
* @param autoJumpTab
|
||||
* @returns {Promise<any>}
|
||||
* @author sunjianlei
|
||||
*/
|
||||
export async function validateFormModelAndTables (form, formData, cases, autoJumpTab) {
|
||||
if (!(form && typeof form.validate === 'function')) {
|
||||
throw new Error(`form 参数需要的是一个form对象,而传入的却是${typeof form}`)
|
||||
}
|
||||
let dataMap = {}
|
||||
const values = await new Promise((resolve, reject) => {
|
||||
// 验证主表表单
|
||||
form.validate((valid) => {
|
||||
valid ? resolve(formData) : reject({ error: VALIDATE_FAILED, originError: valid })
|
||||
})
|
||||
})
|
||||
Object.assign(dataMap, { formValue: values })
|
||||
// 验证所有子表的表单
|
||||
const subData = await validateTables(cases, autoJumpTab)
|
||||
// 合并最终数据
|
||||
dataMap = Object.assign(dataMap, { tablesValue: subData })
|
||||
return dataMap
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并获取一个或多个表格的所有值
|
||||
*
|
||||
* @param cases 接收一个数组,每项都是一个JVxeTable实例
|
||||
* @param autoJumpTab 校验失败后,是否自动跳转tab选项
|
||||
*/
|
||||
export function validateTables (cases, autoJumpTab = true) {
|
||||
if (!Array.isArray(cases)) {
|
||||
throw new Error(`'validateTables'函数的'cases'参数需要的是一个数组,而传入的却是${typeof cases}`)
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const tablesData = []
|
||||
let index = 0
|
||||
if (!cases || cases.length === 0) {
|
||||
resolve()
|
||||
}
|
||||
(function next () {
|
||||
const vm = cases[index]
|
||||
vm.validateTable().then(errMap => {
|
||||
// 校验通过
|
||||
if (!errMap) {
|
||||
tablesData[index] = vm.getAll()
|
||||
// 判断校验是否全部完成,完成返回成功,否则继续进行下一步校验
|
||||
if (++index === cases.length) {
|
||||
resolve(tablesData)
|
||||
} else {
|
||||
(
|
||||
next()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// 尝试获取tabKey,如果在ATab组件内即可获取
|
||||
let paneKey
|
||||
const tabPane = getVmParentByName(vm, 'ATabPane')
|
||||
if (tabPane) {
|
||||
paneKey = tabPane.$vnode.key
|
||||
// 自动跳转到该表格
|
||||
if (autoJumpTab) {
|
||||
const tabs = getVmParentByName(tabPane, 'Tabs')
|
||||
tabs && tabs.setActiveKey && tabs.setActiveKey(paneKey)
|
||||
}
|
||||
}
|
||||
// 出现未验证通过的表单,不再进行下一步校验,直接返回失败
|
||||
reject({ error: VALIDATE_FAILED, index, paneKey, errMap })
|
||||
}
|
||||
})
|
||||
})()
|
||||
})
|
||||
}
|
||||
@@ -119,7 +119,7 @@ export default {
|
||||
mounted () {
|
||||
this.loadData()
|
||||
// this.timerFun();
|
||||
this.initWebSocket()
|
||||
// this.initWebSocket()
|
||||
// this.heartCheckFun();
|
||||
},
|
||||
destroyed: function () { // 离开页面生命周期函数
|
||||
|
||||
@@ -23,13 +23,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JModal from '../jero/JModal'
|
||||
|
||||
export default {
|
||||
name: 'SysAnnouncementModal',
|
||||
components: {
|
||||
JModal
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: '通知消息',
|
||||
|
||||
Reference in New Issue
Block a user