mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2025-12-24 06:56:39 +08:00
JeecgBoot 2.3 里程碑版本发布,支持微服务和单体自由切换、提供新行编辑表格JVXETable
This commit is contained in:
282
ant-design-vue-jeecg/src/components/jeecg/JCodeEditor2.vue
Normal file
282
ant-design-vue-jeecg/src/components/jeecg/JCodeEditor2.vue
Normal file
@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div v-bind="fullScreenParentProps">
|
||||
<a-icon v-if="fullScreen" class="full-screen-icon" :type="iconType" @click="()=>fullCoder=!fullCoder"/>
|
||||
|
||||
<div class="code-editor-cust full-screen-child">
|
||||
<a-textarea auto-size v-model="textareaValue" :placeholder="placeholderShow" @change="handleChange" :style="{'max-height': maxHeight+'px','min-height': minHeight+'px'}"></a-textarea>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/ecmascript-6">
|
||||
export default {
|
||||
name: 'JCodeEditor',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
// 是否显示全屏按钮
|
||||
fullScreen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 全屏以后的z-index
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: 999
|
||||
},
|
||||
// 是否自适应高度,可以传String或Boolean
|
||||
// 传 String 类型只能写"!ie" ,
|
||||
// 填写这个字符串,代表其他浏览器自适应高度
|
||||
// 唯独IE下不自适应高度,因为IE下不支持min、max-height样式
|
||||
// 如果填写的不是"!ie"就视为true
|
||||
autoHeight: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 不自适应高度的情况下生效的固定高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: '240px'
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
minHeight:{
|
||||
type:Number,
|
||||
default: 100,
|
||||
required:false
|
||||
},
|
||||
maxHeight:{
|
||||
type:Number,
|
||||
default: 320,
|
||||
required:false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
textareaValue: '',
|
||||
// 内部真实的内容
|
||||
code: '',
|
||||
iconType: 'fullscreen',
|
||||
hasCode:false,
|
||||
// 默认的语法类型
|
||||
mode: 'javascript',
|
||||
// 编辑器实例
|
||||
coder: null,
|
||||
// 默认配置
|
||||
options: {
|
||||
// 缩进格式
|
||||
tabSize: 2,
|
||||
// 主题,对应主题库 JS 需要提前引入
|
||||
theme: 'panda-syntax',
|
||||
line: true,
|
||||
// extraKeys: {'Ctrl': 'autocomplete'},//自定义快捷键
|
||||
hintOptions: {
|
||||
tables: {
|
||||
users: ['name', 'score', 'birthDate'],
|
||||
countries: ['name', 'population', 'size']
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// code 编辑器 是否全屏
|
||||
fullCoder: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fullCoder:{
|
||||
handler(value) {
|
||||
if(value){
|
||||
this.iconType="fullscreen-exit"
|
||||
}else{
|
||||
this.iconType="fullscreen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
placeholderShow() {
|
||||
if (this.placeholder == null) {
|
||||
return `请在此输入代码`
|
||||
} else {
|
||||
return this.placeholder
|
||||
}
|
||||
},
|
||||
isAutoHeight() {
|
||||
let {autoHeight} = this
|
||||
if (typeof autoHeight === 'string' && autoHeight.toLowerCase().trim() === '!ie') {
|
||||
autoHeight = !(isIE() || isIE11())
|
||||
} else {
|
||||
autoHeight = true
|
||||
}
|
||||
return autoHeight
|
||||
},
|
||||
fullScreenParentProps() {
|
||||
let props = {
|
||||
class: {
|
||||
'full-screen-parent': true,
|
||||
'full-screen': this.fullCoder,
|
||||
'auto-height': this.isAutoHeight
|
||||
},
|
||||
style: {}
|
||||
}
|
||||
if (this.fullCoder) {
|
||||
props.style['z-index'] = this.zIndex
|
||||
}
|
||||
if (!this.isAutoHeight) {
|
||||
props.style['height'] = (typeof this.height === 'number' ? this.height + 'px' : this.height)
|
||||
}
|
||||
return props
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 初始化
|
||||
this._initialize()
|
||||
},
|
||||
methods: {
|
||||
// 初始化
|
||||
_initialize () {
|
||||
this.setCodeContent(this.value)
|
||||
},
|
||||
handleChange(e){
|
||||
this.$emit('input', e.target.value)
|
||||
},
|
||||
getCodeContent(){
|
||||
return this.value
|
||||
},
|
||||
setCodeContent(val){
|
||||
setTimeout(()=>{
|
||||
if(!val){
|
||||
this.textareaValue = ''
|
||||
}else{
|
||||
this.textareaValue = val
|
||||
}
|
||||
},300)
|
||||
},
|
||||
nullTipClick(){
|
||||
this.coder.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.code-editor-cust{
|
||||
flex-grow:1;
|
||||
display:flex;
|
||||
position:relative;
|
||||
height:100%;
|
||||
.CodeMirror{
|
||||
flex-grow:1;
|
||||
z-index:1;
|
||||
.CodeMirror-code{
|
||||
line-height:19px;
|
||||
}
|
||||
|
||||
}
|
||||
.code-mode-select{
|
||||
position:absolute;
|
||||
z-index:2;
|
||||
right:10px;
|
||||
top:10px;
|
||||
max-width:130px;
|
||||
}
|
||||
.CodeMirror{
|
||||
height: auto;
|
||||
min-height:100%;
|
||||
}
|
||||
.null-tip{
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 36px;
|
||||
z-index: 10;
|
||||
color: #ffffffc9;
|
||||
line-height: initial;
|
||||
}
|
||||
.null-tip-hidden{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 全屏样式 */
|
||||
.full-screen-parent {
|
||||
position: relative;
|
||||
|
||||
.full-screen-icon {
|
||||
opacity: 0;
|
||||
color: black;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 24px;
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
z-index: 9;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.full-screen-icon {
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.88);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.full-screen {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: calc(100% - 20px);
|
||||
height: calc(100% - 20px);
|
||||
padding: 10px;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.full-screen-icon {
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.full-screen-child {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.full-screen-child {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.auto-height {
|
||||
.full-screen-child {
|
||||
min-height: 120px;
|
||||
max-height: 320px;
|
||||
height: unset;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.full-screen .full-screen-child {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.CodeMirror-cursor{
|
||||
height:18.4px !important;
|
||||
}
|
||||
</style>
|
||||
@ -39,7 +39,7 @@
|
||||
<div class="thead" ref="thead">
|
||||
<div class="tr" :style="{width: this.realTrWidth}">
|
||||
<!-- 左侧固定td -->
|
||||
<div v-if="dragSort" class="td td-ds" :style="style.tdLeftDs">
|
||||
<div v-if="dragSort" class="td td-ds" :style="style.tdLeft">
|
||||
<span></span>
|
||||
</div>
|
||||
<div v-if="rowSelection" class="td td-cb" :style="style.tdLeft">
|
||||
@ -104,7 +104,7 @@
|
||||
>
|
||||
<!-- 左侧固定td -->
|
||||
|
||||
<div v-if="dragSort" class="td td-ds" :style="style.tdLeftDs" @dblclick="_handleRowInsertDown(rowIndex)" >
|
||||
<div v-if="dragSort" class="td td-ds" :style="style.tdLeft" @dblclick="_handleRowInsertDown(rowIndex)" >
|
||||
<a-dropdown :trigger="['click']" :getPopupContainer="getParentContainer">
|
||||
<div class="td-ds-icons">
|
||||
<a-icon type="align-left"/>
|
||||
@ -584,7 +584,7 @@
|
||||
height: '32px'
|
||||
}"
|
||||
>
|
||||
<div v-if="dragSort" class="td td-ds" :style="style.tdLeftDs">
|
||||
<div v-if="dragSort" class="td td-ds" :style="style.tdLeft">
|
||||
</div>
|
||||
<div v-if="rowSelection" class="td td-cb" :style="style.tdLeft">
|
||||
统计
|
||||
@ -673,7 +673,7 @@
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 页面是否在加载中
|
||||
// 表格内容区域最大高度
|
||||
maxHeight: {
|
||||
type: Number,
|
||||
default: 400
|
||||
@ -725,8 +725,7 @@
|
||||
// 'max-height': '400px'
|
||||
tbody: { left: '0px' },
|
||||
// 左侧固定td的style
|
||||
tdLeft: { 'min-width': '4%', 'max-width': '45px' },
|
||||
tdLeftDs: { 'min-width': '30px', 'max-width': '35px' },
|
||||
tdLeft: {},
|
||||
},
|
||||
// 表单的类型
|
||||
formTypes: FormTypes,
|
||||
@ -1398,7 +1397,6 @@
|
||||
let values = []
|
||||
// 遍历inputValues来获取每行的值
|
||||
for (let value of inputValues) {
|
||||
if (value == null) console.warn(this.caseId, '+++++++++ value.1: ', value, cloneObject(inputValues))
|
||||
let rowIdsFlag = false
|
||||
// 如果带有rowIds,那么就只存这几行的数据
|
||||
if (rowIds == null) {
|
||||
@ -1591,78 +1589,68 @@
|
||||
rowKey = this.getCleanId(rowKey)
|
||||
for (let newValueKey in newValues) {
|
||||
if (newValues.hasOwnProperty(newValueKey)) {
|
||||
let newValue = newValues[newValueKey]
|
||||
let edited = false // 已被修改
|
||||
this.inputValues.forEach(value => {
|
||||
// 在inputValues中找到了该字段
|
||||
if (rowKey === this.getCleanId(value.id)) {
|
||||
if (value.hasOwnProperty(newValueKey)) {
|
||||
edited = true
|
||||
value[newValueKey] = newValue
|
||||
}
|
||||
}
|
||||
})
|
||||
let modelKey = `${newValueKey}${this.caseId}${rowKey}`
|
||||
// 在 selectValues 中寻找值
|
||||
if (!edited) {
|
||||
if (newValue !== 0 && !newValue) {
|
||||
edited = this.setOneValue(this.selectValues, modelKey, undefined)
|
||||
} else {
|
||||
edited = this.setOneValue(this.selectValues, modelKey, newValue)
|
||||
}
|
||||
}
|
||||
// 在 checkboxValues 中寻找值
|
||||
if (!edited) {
|
||||
// checkbox 特殊处理 CustomValue
|
||||
let key = this.valuesHasOwnProperty(this.checkboxValues, modelKey)
|
||||
// 找到对应的column
|
||||
let sourceValue
|
||||
for (let column of this.columns) {
|
||||
if (column.key === newValueKey) {
|
||||
edited = true
|
||||
// 判断是否设定了customValue(自定义值)
|
||||
if (column.customValue instanceof Array) {
|
||||
let customValue = (column.customValue[0] || '').toString()
|
||||
sourceValue = (newValue === customValue)
|
||||
} else {
|
||||
sourceValue = !!newValue
|
||||
for (let column of this.columns) {
|
||||
if (column.key === newValueKey) {
|
||||
let newValue = newValues[newValueKey]
|
||||
this.inputValues.forEach(value => {
|
||||
// 在inputValues中找到了该字段
|
||||
if (rowKey === this.getCleanId(value.id)) {
|
||||
if (value.hasOwnProperty(newValueKey)) {
|
||||
edited = true
|
||||
value[newValueKey] = newValue
|
||||
}
|
||||
}
|
||||
this.$set(this.checkboxValues, key, sourceValue)
|
||||
break
|
||||
})
|
||||
if (!edited) {
|
||||
let modelKey = `${newValueKey}${this.caseId}${rowKey}`
|
||||
if (column.type === FormTypes.select) {
|
||||
if (newValue !== 0 && !newValue) {
|
||||
edited = this.setOneValue(this.selectValues, modelKey, undefined)
|
||||
} else {
|
||||
edited = this.setOneValue(this.selectValues, modelKey, newValue)
|
||||
}
|
||||
} else if (column.type === FormTypes.checkbox) {
|
||||
// checkbox 特殊处理 CustomValue
|
||||
let key = this.valuesHasOwnProperty(this.checkboxValues, modelKey)
|
||||
// 找到对应的column
|
||||
let sourceValue
|
||||
// 判断是否设定了customValue(自定义值)
|
||||
if (column.customValue instanceof Array) {
|
||||
let customValue = (column.customValue[0] || '').toString()
|
||||
sourceValue = (newValue === customValue)
|
||||
} else {
|
||||
sourceValue = !!newValue
|
||||
}
|
||||
this.$set(this.checkboxValues, key, sourceValue)
|
||||
edited = true
|
||||
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
|
||||
edited = this.setOneValue(this.jdateValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.input_pop) {
|
||||
edited = this.setOneValue(this.jInputPopValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.slot) {
|
||||
edited = this.setOneValue(this.slotValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.upload || column.type === FormTypes.image || column.type === FormTypes.file) {
|
||||
edited = this.setOneValue(this.uploadValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.popup) {
|
||||
edited = this.setOneValue(this.popupValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.radio) {
|
||||
edited = this.setOneValue(this.radioValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.list_multi) {
|
||||
edited = this.setOneValue(this.multiSelectValues, modelKey, newValue, true)
|
||||
} else if (column.type === FormTypes.sel_search) {
|
||||
edited = this.setOneValue(this.searchSelectValues, modelKey, newValue)
|
||||
} else {
|
||||
edited = false
|
||||
}
|
||||
}
|
||||
if (edited) {
|
||||
this.elemValueChange(column.type, {[newValueKey]: newValue}, column, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 在 jdateValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.jdateValues, modelKey, newValue)
|
||||
}
|
||||
// 在 jInputPopValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.jInputPopValues, modelKey, newValue)
|
||||
}
|
||||
// 在 slotValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.slotValues, modelKey, newValue)
|
||||
}
|
||||
// 在 uploadValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.uploadValues, modelKey, newValue)
|
||||
}
|
||||
// 在 popupValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.popupValues, modelKey, newValue)
|
||||
}
|
||||
// 在 radioValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.radioValues, modelKey, newValue)
|
||||
}
|
||||
// 在 multiSelectValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.multiSelectValues, modelKey, newValue, true)
|
||||
}
|
||||
// 在 searchSelectValues 中寻找值
|
||||
if (!edited) {
|
||||
edited = this.setOneValue(this.searchSelectValues, modelKey, newValue)
|
||||
console.warn(`JEditableTable.setValues:没有找到"${newValueKey}"列`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2209,6 +2197,9 @@
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
this.uploadValues = this.bindValuesChange(value, id, 'uploadValues')
|
||||
|
||||
// 触发valueChange 事件
|
||||
this.elemValueChange(column.type, row, column, value)
|
||||
},
|
||||
handleMoreOperation(id,flag){
|
||||
//console.log("this.uploadValues[id]",this.uploadValues[id])
|
||||
@ -2747,8 +2738,9 @@
|
||||
flex-direction: column;
|
||||
|
||||
&.td-cb, &.td-num {
|
||||
min-width: 4%;
|
||||
max-width: 45px;
|
||||
width: 45px;
|
||||
min-width: 45px;
|
||||
max-width: 50px;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
@ -2757,6 +2749,9 @@
|
||||
}
|
||||
|
||||
&.td-ds {
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
max-width: 35px;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
@ -2942,6 +2937,10 @@
|
||||
|
||||
label {
|
||||
height: 32px;
|
||||
|
||||
&.ant-checkbox-wrapper {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.j-td-span {
|
||||
|
||||
@ -128,6 +128,13 @@
|
||||
this.reload()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
//update--begin--autor:wangshuai-----date:20200724------for:富文本编辑器切换tab无法修改------
|
||||
let tabLayout = getVmParentByName(this, 'TabLayout')
|
||||
tabLayout.excuteCallback(()=>{
|
||||
this.reload()
|
||||
})
|
||||
//update--begin--autor:wangshuai-----date:20200724------for:文本编辑器切换tab无法修改------
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -171,8 +171,14 @@
|
||||
if(!this.isMultiple){
|
||||
arr.push(uploadFiles[uploadFiles.length-1].response.message)
|
||||
}else{
|
||||
for(var a=0;a<uploadFiles.length;a++){
|
||||
arr.push(uploadFiles[a].response.message)
|
||||
for(let a=0;a<uploadFiles.length;a++){
|
||||
// update-begin-author:taoyan date:20200819 for:【开源问题z】上传图片组件 LOWCOD-783
|
||||
if(uploadFiles[a].status === 'done' ) {
|
||||
arr.push(uploadFiles[a].response.message)
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
// update-end-author:taoyan date:20200819 for:【开源问题z】上传图片组件 LOWCOD-783
|
||||
}
|
||||
}
|
||||
if(arr.length>0){
|
||||
|
||||
@ -24,6 +24,11 @@
|
||||
type:String,
|
||||
required:false,
|
||||
default:''
|
||||
},
|
||||
trim:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
@ -56,8 +61,8 @@
|
||||
let text = this.value
|
||||
switch (this.type) {
|
||||
case JINPUT_QUERY_LIKE:
|
||||
//修复路由传参的值传送到jinput框被前后各截取了一位
|
||||
if(text.indexOf("*") != -1){
|
||||
//修复路由传参的值传送到jinput框被前后各截取了一位 #1336
|
||||
if(text.indexOf("*") != -1){
|
||||
text = text.substring(1,text.length-1);
|
||||
}
|
||||
break;
|
||||
@ -77,6 +82,9 @@
|
||||
},
|
||||
backValue(e){
|
||||
let text = e.target.value
|
||||
if(text && this.trim===true){
|
||||
text = text.trim()
|
||||
}
|
||||
switch (this.type) {
|
||||
case JINPUT_QUERY_LIKE:
|
||||
text = "*"+text+"*";
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
return Object.keys(this.$scopedSlots).filter(key => !this.usedSlots.includes(key))
|
||||
},
|
||||
allSlotsKeys() {
|
||||
return this.slotsKeys.concat(this.scopedSlotsKeys)
|
||||
return Object.keys(this.$slots).concat(Object.keys(this.$scopedSlots))
|
||||
},
|
||||
// 切换全屏的按钮图标
|
||||
fullscreenButtonIcon() {
|
||||
|
||||
@ -498,10 +498,9 @@
|
||||
} else {
|
||||
if (Array.isArray(item.options)) {
|
||||
// 如果有字典属性,就不需要保存 options 了
|
||||
if (item.dictCode) {
|
||||
// 去掉特殊属性
|
||||
delete item.options
|
||||
}
|
||||
//update-begin-author:taoyan date:20200819 for:【开源问题】 高级查询 下拉框作为并且选项很多多多 LOWCOD-779
|
||||
delete item.options
|
||||
//update-end-author:taoyan date:20200819 for:【开源问题】 高级查询 下拉框作为并且选项很多多多 LOWCOD-779
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
77
ant-design-vue-jeecg/src/components/jeecg/JTime.vue
Normal file
77
ant-design-vue-jeecg/src/components/jeecg/JTime.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<a-time-picker
|
||||
:disabled="disabled || readOnly"
|
||||
:placeholder="placeholder"
|
||||
:value="momVal"
|
||||
:format="dateFormat"
|
||||
:getCalendarContainer="getCalendarContainer"
|
||||
@change="handleTimeChange"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
name: 'JTime',
|
||||
props: {
|
||||
placeholder:{
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
value:{
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
dateFormat:{
|
||||
type: String,
|
||||
default: 'HH:mm:ss',
|
||||
required: false
|
||||
},
|
||||
readOnly:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
disabled:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
getCalendarContainer: {
|
||||
type: Function,
|
||||
default: (node) => node.parentNode
|
||||
}
|
||||
},
|
||||
data () {
|
||||
let timeStr = this.value;
|
||||
return {
|
||||
decorator:"",
|
||||
momVal:!timeStr?null:moment(timeStr,this.dateFormat)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if(!val){
|
||||
this.momVal = null
|
||||
}else{
|
||||
this.momVal = moment(val,this.dateFormat)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
moment,
|
||||
handleTimeChange(mom,timeStr){
|
||||
this.$emit('change', timeStr);
|
||||
}
|
||||
},
|
||||
//2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,75 @@
|
||||
<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) {
|
||||
let {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>
|
||||
@ -0,0 +1,67 @@
|
||||
<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>
|
||||
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<a-popover :visible="visible" placement="bottom" 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
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
|
||||
toggle(event) {
|
||||
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
|
||||
}
|
||||
|
||||
let {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
|
||||
}
|
||||
let table = $table.$el
|
||||
let tr = getParentNodeByTagName(target, 'tr')
|
||||
if (table && tr) {
|
||||
let clientWidth = table.clientWidth
|
||||
let 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
|
||||
domAlign(this.$refs.div, tr, {
|
||||
points: ['tl', 'tl'],
|
||||
offset: [0, 0],
|
||||
overflow: {
|
||||
alwaysByViewport: true
|
||||
},
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
this.visible = true
|
||||
this.$nextTick(() => {
|
||||
triggerWindowResizeEvent()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
let 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
@ -0,0 +1,127 @@
|
||||
<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() {
|
||||
let arr = this.toolbarConfig.btn || ['add', 'remove', 'clearSelection']
|
||||
let exclude = [...this.excludeCode]
|
||||
// TODO 需要将remove替换batch_delete
|
||||
// 系统默认的批量删除编码配置为 batch_delete 此处需要转化一下
|
||||
if(exclude.indexOf('batch_delete')>=0){
|
||||
exclude.add('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')) {
|
||||
// 有禁用行时才显示清空选择按钮
|
||||
// 因为禁用行会阻止选择行,导致无法取消全选
|
||||
let 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>
|
||||
@ -0,0 +1,103 @@
|
||||
<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/jeecg/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) {
|
||||
let {own: col} = this.column
|
||||
// 处理 customValue
|
||||
if (Array.isArray(col.customValue)) {
|
||||
let customValue = getCustomValue(col)
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? customValue[0] : customValue[1]
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
setValue(value) {
|
||||
let {own: col} = this.column
|
||||
// 判断是否设定了customValue(自定义值)
|
||||
if (Array.isArray(col.customValue)) {
|
||||
let customValue = getCustomValue(col)
|
||||
return neverNull(value).toString() === customValue[0].toString()
|
||||
} else {
|
||||
return !!value
|
||||
}
|
||||
},
|
||||
createValue({column}) {
|
||||
let {own: col} = column
|
||||
if (Array.isArray(col.customValue)) {
|
||||
let customValue = getCustomValue(col)
|
||||
return col.defaultChecked ? customValue[0] : customValue[1]
|
||||
} else {
|
||||
return !!col.defaultChecked
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomValue(col) {
|
||||
let customTrue = neverNull(col.customValue[0], true)
|
||||
let 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>
|
||||
@ -0,0 +1,66 @@
|
||||
<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 '@/components/jeecg/JVxeTable/index'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDateCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
innerDateValue: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isDatetime() {
|
||||
return this.$type === JVXETypes.datetime
|
||||
},
|
||||
dateFormat() {
|
||||
let format = this.originColumn.format
|
||||
return format ? 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(event, 'ant-calendar-picker', el => el.children[0].dispatchEvent(event.$event)),
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,138 @@
|
||||
<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/jeecg/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.rows.length - 1)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 向上移 */
|
||||
handleRowMoveUp(event) {
|
||||
// event.target.blur()
|
||||
if (!this.disabledMoveUp) {
|
||||
this.trigger('rowMoveUp', this.rowIndex)
|
||||
}
|
||||
},
|
||||
/** 向下移 */
|
||||
handleRowMoveDown(event) {
|
||||
// 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>
|
||||
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<a-input
|
||||
ref="input"
|
||||
:value="innerValue"
|
||||
v-bind="cellProps"
|
||||
@blur="handleBlur"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { JVXETypes } from '@/components/jeecg/JVxeTable'
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
const NumberRegExp = /^-?\d+\.?\d*$/
|
||||
export default {
|
||||
name: 'JVxeInputCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
methods: {
|
||||
|
||||
/** 处理change事件 */
|
||||
handleChange(event) {
|
||||
let {$type} = this
|
||||
let {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) {
|
||||
let {$type} = this
|
||||
let {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>
|
||||
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<reload-effect
|
||||
:vNode="innerValue"
|
||||
:effect="reloadEffect"
|
||||
@effect-end="handleEffectEnd"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ReloadEffect from './ReloadEffect'
|
||||
import JVxeCellMixins from '@/components/jeecg/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>
|
||||
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<a-progress
|
||||
:class="clazz"
|
||||
:percent="innerValue"
|
||||
size="small"
|
||||
v-bind="cellProps"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jeecg/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>
|
||||
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<a-select
|
||||
ref="select"
|
||||
:value="innerValue"
|
||||
allowClear
|
||||
:filterOption="handleSelectFilterOption"
|
||||
v-bind="selectProps"
|
||||
style="width: 100%;"
|
||||
@blur="handleBlur"
|
||||
@change="handleChangeCommon"
|
||||
@search="handleSearchSelect"
|
||||
>
|
||||
|
||||
<template v-for="option of originColumn.options">
|
||||
<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/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { JVXETypes } from '@comp/jeecg/JVxeTable/index'
|
||||
|
||||
export default {
|
||||
name: 'JVxeSelectCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
selectProps() {
|
||||
let props = {...this.cellProps}
|
||||
// 判断select是否允许输入
|
||||
let {allowSearch, allowInput} = this.originColumn
|
||||
if (allowInput === true || allowSearch === true) {
|
||||
props['showSearch'] = true
|
||||
}
|
||||
return props
|
||||
},
|
||||
},
|
||||
created() {
|
||||
let multiple = [JVXETypes.selectMultiple, JVXETypes.list_multi]
|
||||
let search = [JVXETypes.selectSearch, JVXETypes.sel_search]
|
||||
if (multiple.includes(this.$type)) {
|
||||
// 处理多选
|
||||
let 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: {
|
||||
|
||||
/** 处理blur失去焦点事件 */
|
||||
handleBlur(value) {
|
||||
let {allowInput, options} = this.originColumn
|
||||
|
||||
if (allowInput === true) {
|
||||
// 删除无用的因搜索(用户输入)而创建的项
|
||||
if (typeof value === 'string') {
|
||||
let indexes = []
|
||||
options.forEach((option, index) => {
|
||||
if (option.value.toLocaleString() === value.toLocaleString()) {
|
||||
delete option.searchAdd
|
||||
} else if (option.searchAdd === true) {
|
||||
indexes.push(index)
|
||||
}
|
||||
})
|
||||
// 翻转删除数组中的项
|
||||
for (let index of indexes.reverse()) {
|
||||
options.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.handleBlurCommon(value)
|
||||
},
|
||||
|
||||
/** 用于搜索下拉框中的内容 */
|
||||
handleSelectFilterOption(input, option) {
|
||||
let {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) {
|
||||
let {allowSearch, allowInput, options} = this.originColumn
|
||||
|
||||
if (allowSearch !== true && allowInput === true) {
|
||||
// 是否找到了对应的项,找不到则添加这一项
|
||||
let flag = false
|
||||
for (let 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})
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'ant-select'),
|
||||
},
|
||||
translate: {enabled: true},
|
||||
getValue(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(',')
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
setValue(value) {
|
||||
let {column: {own: 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>
|
||||
@ -0,0 +1,46 @@
|
||||
import JVxeCellMixins from '@/components/jeecg/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) {
|
||||
let {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)"
|
||||
@ -0,0 +1,145 @@
|
||||
import JVxeCellMixins from '@/components/jeecg/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(';')) {
|
||||
let rv = replaceValue(value)
|
||||
this.innerTags = rv.split(';')
|
||||
this.handleChangeCommon(rv)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
renderTags(h) {
|
||||
let tags = []
|
||||
for (let tag of this.innerTags) {
|
||||
if (tag) {
|
||||
let tagProps = {}
|
||||
let tagStyle = {}
|
||||
let 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背景颜色
|
||||
*/
|
||||
let 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) => {
|
||||
let {target, target: {value}} = event
|
||||
let newValue = this.handleInputChange(value, event)
|
||||
if (newValue !== value) {
|
||||
target.value = newValue
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// 将值每隔两位加上一个分号
|
||||
function replaceValue(value, event) {
|
||||
if (value) {
|
||||
// 首先去掉现有的分号
|
||||
value = value.replace(/;/g, '')
|
||||
// 然后再遍历添加分号
|
||||
let rv = ''
|
||||
let splitArr = value.split('')
|
||||
let count = 0
|
||||
splitArr.forEach((val, index) => {
|
||||
rv += val
|
||||
let position = index + 1
|
||||
if (position % 2 === 0 && position < splitArr.length) {
|
||||
count++
|
||||
rv += ';'
|
||||
}
|
||||
})
|
||||
if (event && count > 0) {
|
||||
let {target, target: {selectionStart}} = event
|
||||
target.selectionStart = selectionStart + count
|
||||
target.selectionEnd = selectionStart + count
|
||||
}
|
||||
return rv
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<j-input-pop
|
||||
:value="innerValue"
|
||||
:width="300"
|
||||
:height="210"
|
||||
v-bind="cellProps"
|
||||
style="width: 100%;"
|
||||
@change="handleChangeCommon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JInputPop from '@/components/jeecg/minipop/JInputPop'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeTextareaCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: {JInputPop},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
installOptions: {
|
||||
autofocus: '.ant-input',
|
||||
},
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'anticon-fullscreen'),
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasFile" 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="上传失败">
|
||||
<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>
|
||||
<a-upload
|
||||
v-show="!hasFile"
|
||||
name="file"
|
||||
:data="{'isup': 1}"
|
||||
:multiple="false"
|
||||
:action="originColumn.action"
|
||||
: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/jeecg/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() {
|
||||
let {originColumn: col} = this
|
||||
let headers = {}
|
||||
if (col.token === true) {
|
||||
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
|
||||
hasFile() {
|
||||
return this.innerFile != null
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (this.innerValue) {
|
||||
this.innerFile = this.innerValue
|
||||
} else {
|
||||
this.innerFile = null
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleChangeUpload(info) {
|
||||
let {row, originColumn: col} = this
|
||||
let {file} = info
|
||||
let 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') {
|
||||
value['path'] = file.response[col.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
this.innerFile = value
|
||||
},
|
||||
|
||||
// handleClickPreviewFile(id) {
|
||||
// this.$message.info('尚未实现')
|
||||
// },
|
||||
|
||||
handleClickDownloadFile(id) {
|
||||
let {path} = this.value || {}
|
||||
if (path) {
|
||||
let 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) {
|
||||
let first = value.split(',')[0]
|
||||
let name = first.substring(first.lastIndexOf('/') + 1)
|
||||
return {
|
||||
name: name,
|
||||
path: value,
|
||||
status: 'done',
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,84 @@
|
||||
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) {
|
||||
let topLayer = this.renderSpan(old, 'top')
|
||||
this.effectList.push(topLayer)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 条件渲染内容 span
|
||||
renderVNode() {
|
||||
if (this.vNode == null) {
|
||||
return null
|
||||
}
|
||||
let bottom = this.renderSpan(this.vNode, 'bottom')
|
||||
// 启用了特效,并且有旧数据,就渲染特效顶层
|
||||
if (this.innerEffect && this.effectList.length > 0) {
|
||||
this.$emit('effect-begin')
|
||||
// 1.4s 以后关闭特效
|
||||
window.setTimeout(() => {
|
||||
let 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) {
|
||||
let 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()])
|
||||
},
|
||||
}
|
||||
81
ant-design-vue-jeecg/src/components/jeecg/JVxeTable/index.js
Normal file
81
ant-design-vue-jeecg/src/components/jeecg/JVxeTable/index.js
Normal file
@ -0,0 +1,81 @@
|
||||
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'
|
||||
|
||||
// 组件类型
|
||||
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',
|
||||
|
||||
// 拖轮Tags(暂无用)
|
||||
tags: 'tags',
|
||||
|
||||
slot: 'slot',
|
||||
normal: 'normal',
|
||||
hidden: 'hidden',
|
||||
}
|
||||
|
||||
// 注册自定义组件
|
||||
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),
|
||||
|
||||
/* hidden 是特殊的组件,不在这里注册 */
|
||||
}
|
||||
|
||||
export { installCell, mapCell }
|
||||
|
||||
export default JVxeTable
|
||||
105
ant-design-vue-jeecg/src/components/jeecg/JVxeTable/install.js
Normal file
105
ant-design-vue-jeecg/src/components/jeecg/JVxeTable/install.js
Normal file
@ -0,0 +1,105 @@
|
||||
import Vue from 'vue'
|
||||
import { getEventPath } 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/jeecg/JVxeTable/utils/cellUtils'
|
||||
|
||||
// VxeGrid所有的方法映射
|
||||
const VxeGridMethodsMap = {}
|
||||
Object.keys(Grid.methods).forEach(key => {
|
||||
// 使用eval可以避免闭包(但是要注意不要写es6的代码)
|
||||
VxeGridMethodsMap[key] = eval(`(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, event, target) {
|
||||
// 获取组件增强
|
||||
let col = params.column.own
|
||||
const interceptor = getEnhancedMixins(col.$type, 'interceptor')
|
||||
// 执行增强
|
||||
let flag = interceptor['event.clearActived'].apply(this, arguments)
|
||||
if (flag === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
let path = getEventPath(event)
|
||||
for (let 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')) {
|
||||
return false
|
||||
}
|
||||
// 执行增强
|
||||
let 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) {
|
||||
let cells = {[type]: cell}
|
||||
if (span) {
|
||||
cells[type + ':span'] = span
|
||||
}
|
||||
return cells
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册自定义组件
|
||||
*
|
||||
* @param type 类型
|
||||
* @param cell 输入组件
|
||||
* @param span 显示组件,可空,默认为 JVxeNormalCell 组件
|
||||
*/
|
||||
export function installCell(type, cell, span) {
|
||||
let 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)
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
@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%;
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,332 @@
|
||||
.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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,303 @@
|
||||
import PropTypes from 'ant-design-vue/es/_util/vue-types'
|
||||
import { filterDictText } from '@/components/dict/JDictSelectUtil'
|
||||
import { getEnhancedMixins, JVXERenderType, replaceProps } from '@/components/jeecg/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.own
|
||||
},
|
||||
$type() {
|
||||
return this.originColumn.$type
|
||||
},
|
||||
rows() {
|
||||
return this.params.data
|
||||
},
|
||||
rowIndex() {
|
||||
return this.params.rowIndex
|
||||
},
|
||||
columnIndex() {
|
||||
return this.params.columnIndex
|
||||
},
|
||||
cellProps() {
|
||||
let {originColumn: col, renderOptions} = this
|
||||
|
||||
let 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
|
||||
|
||||
// 验证值格式
|
||||
let originValue = this.row[this.column.property]
|
||||
let 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) {
|
||||
this.innerValue = this.enhanced.translate.handler.call(this, value)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** 通用处理change事件 */
|
||||
handleChangeCommon(value) {
|
||||
let 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 = []) {
|
||||
let 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
|
||||
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.own.defaultValue
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function getListeners() {
|
||||
let 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) {
|
||||
window.setTimeout(() => {
|
||||
let element = cell.getElementsByClassName(className)
|
||||
if (element && element.length > 0) {
|
||||
if (typeof handler === 'function') {
|
||||
handler(element[0])
|
||||
} else {
|
||||
// 模拟触发点击事件
|
||||
element[0].dispatchEvent($event)
|
||||
}
|
||||
}
|
||||
}, 10)
|
||||
}
|
||||
@ -0,0 +1,264 @@
|
||||
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['domianURL'].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 {
|
||||
let 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) {
|
||||
let binds = map.get(key)
|
||||
if (Array.isArray(binds)) {
|
||||
binds.push(value)
|
||||
} else {
|
||||
map.set(key, [value])
|
||||
}
|
||||
},
|
||||
/** 移除绑定 */
|
||||
removeBind(map, key, value) {
|
||||
let binds = map.get(key)
|
||||
if (Array.isArray(binds)) {
|
||||
for (let i = 0; i < binds.length; i++) {
|
||||
let 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) {
|
||||
let 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
|
||||
}
|
||||
let type = json[this.constants.TYPE]
|
||||
let 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)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
import { AllCells, JVXETypes } from '@/components/jeecg/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]
|
||||
}
|
||||
// 添加渲染
|
||||
VXETable.renderer.add(JVXETypes._prefix + type, {
|
||||
// 可编辑模板
|
||||
renderEdit: createRender(comp, enhanced, JVXERenderType.editer),
|
||||
// 显示模板
|
||||
renderCell: createRender(spanComp, enhanced, JVXERenderType.spaner),
|
||||
// 增强注册
|
||||
...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,
|
||||
})
|
||||
}
|
||||
|
||||
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) {
|
||||
let 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))
|
||||
}
|
||||
let defEnhanced = JVxeCellMixins.enhanced
|
||||
let enhanced = getEnhanced(type)
|
||||
if (enhanced) {
|
||||
Object.keys(defEnhanced).forEach(key => {
|
||||
let def = defEnhanced[key]
|
||||
if (enhanced.hasOwnProperty(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
|
||||
}
|
||||
@ -0,0 +1,190 @@
|
||||
import { getVmParentByName } from '@/utils/util'
|
||||
import { JVXETypes } from '@comp/jeecg/JVxeTable/index'
|
||||
|
||||
export const VALIDATE_FAILED = Symbol()
|
||||
|
||||
/**
|
||||
* 获取指定的 $refs 对象
|
||||
* 有时候可能会遇到组件未挂载到页面中的情况,导致无法获取 $refs 中的某个对象
|
||||
* 这个方法可以等待挂载完成之后再返回 $refs 的对象,避免报错
|
||||
* @author sunjianlei
|
||||
**/
|
||||
export function getRefPromise(vm, name) {
|
||||
return new Promise((resolve) => {
|
||||
(function next() {
|
||||
let 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) => {
|
||||
let val = rowValue[col.key], 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 | NULL}
|
||||
*/
|
||||
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)) {
|
||||
// 高级查询所需要的参数
|
||||
let fieldList = []
|
||||
// 遍历列
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
let col = columns[i]
|
||||
if (col.type === JVXETypes.rowCheckbox ||
|
||||
col.type === JVXETypes.rowRadio ||
|
||||
col.type === JVXETypes.rowExpand ||
|
||||
col.type === JVXETypes.rowNumber
|
||||
) {
|
||||
continue
|
||||
}
|
||||
let 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, idx))
|
||||
}
|
||||
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 `form 参数需要的是一个form对象,而传入的却是${typeof form}`
|
||||
}
|
||||
let dataMap = {}
|
||||
let values = await new Promise((resolve, reject) => {
|
||||
// 验证主表表单
|
||||
form.validateFields((err, values) => {
|
||||
err ? reject({error: VALIDATE_FAILED, originError: err}) : resolve(values)
|
||||
})
|
||||
})
|
||||
Object.assign(dataMap, {formValue: values})
|
||||
// 验证所有子表的表单
|
||||
let 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 `'validateTables'函数的'cases'参数需要的是一个数组,而传入的却是${typeof cases}`
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let tablesData = []
|
||||
let index = 0
|
||||
if (!cases || cases.length === 0) {
|
||||
resolve()
|
||||
}
|
||||
(function next() {
|
||||
let 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
|
||||
let tabPane = getVmParentByName(vm, 'ATabPane')
|
||||
if (tabPane) {
|
||||
paneKey = tabPane.$vnode.key
|
||||
// 自动跳转到该表格
|
||||
if (autoJumpTab) {
|
||||
let tabs = getVmParentByName(tabPane, 'Tabs')
|
||||
tabs && tabs.setActiveKey && tabs.setActiveKey(paneKey)
|
||||
}
|
||||
}
|
||||
// 出现未验证通过的表单,不再进行下一步校验,直接返回失败
|
||||
reject({error: VALIDATE_FAILED, index, paneKey, errMap})
|
||||
}
|
||||
})
|
||||
})()
|
||||
})
|
||||
}
|
||||
246
ant-design-vue-jeecg/src/components/jeecg/dynamic/JEditorDyn.vue
Normal file
246
ant-design-vue-jeecg/src/components/jeecg/dynamic/JEditorDyn.vue
Normal file
@ -0,0 +1,246 @@
|
||||
<template>
|
||||
<div class="tinymce-containerty" :style="{width:containerWidth}">
|
||||
<textarea :id="tinymceId" class="tinymce-textarea" @change="ada"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* docs:
|
||||
* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
|
||||
*/
|
||||
import load from './load'
|
||||
//const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
|
||||
//const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
|
||||
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
|
||||
const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
|
||||
|
||||
export default {
|
||||
name: 'JEditorDyn',
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
toolbar: {
|
||||
type: [String, Array],
|
||||
required: false,
|
||||
default: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists link unlink image media table | removeformat | fullscreen',
|
||||
},
|
||||
menubar: {
|
||||
type: String,
|
||||
default: 'file edit insert view format table'
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: 360
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: 'auto'
|
||||
},
|
||||
plugins: {
|
||||
type: [String, Array],
|
||||
default: 'lists image link media table textcolor wordcount contextmenu fullscreen'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasChange: false,
|
||||
hasInit: false,
|
||||
tinymceId: this.id,
|
||||
fullscreen: false,
|
||||
languageTypeList: {
|
||||
'en': 'en',
|
||||
'zh': 'zh_CN',
|
||||
'es': 'es_MX',
|
||||
'ja': 'ja'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
containerWidth() {
|
||||
const width = this.width
|
||||
if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
|
||||
return `${width}px`
|
||||
}
|
||||
return width
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (!this.hasChange && this.hasInit) {
|
||||
this.$nextTick(() =>
|
||||
window.tinymce.get(this.tinymceId).setContent(val || ''))
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
activated() {
|
||||
if (window.tinymce) {
|
||||
this.initTinymce()
|
||||
}
|
||||
},
|
||||
deactivated() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
destroyed() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
methods: {
|
||||
ada() {
|
||||
console.log('change')
|
||||
},
|
||||
init() {
|
||||
// dynamic load tinymce from cdn
|
||||
load(tinymceCDN, (err) => {
|
||||
if (err) {
|
||||
this.$message.error(err.message)
|
||||
return
|
||||
}
|
||||
this.initTinymce()
|
||||
})
|
||||
},
|
||||
initTinymce() {
|
||||
const _this = this
|
||||
window.tinymce.init({
|
||||
selector: `#${this.tinymceId}`,
|
||||
language: this.languageTypeList['zh'],
|
||||
height: this.height,
|
||||
body_class: 'panel-body ',
|
||||
object_resizing: false,
|
||||
toolbar: this.toolbar,
|
||||
menubar: false,
|
||||
plugins: this.plugins,
|
||||
end_container_on_empty_block: true,
|
||||
powerpaste_word_import: 'clean',
|
||||
code_dialog_height: 450,
|
||||
code_dialog_width: 1000,
|
||||
advlist_bullet_styles: 'square',
|
||||
advlist_number_styles: 'default',
|
||||
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
|
||||
default_link_target: '_blank',
|
||||
link_title: false,
|
||||
nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin
|
||||
init_instance_callback: editor => {
|
||||
if (_this.value) {
|
||||
editor.setContent(_this.value)
|
||||
}
|
||||
_this.hasInit = true
|
||||
editor.on('NodeChange Change KeyUp SetContent', () => {
|
||||
this.hasChange = true
|
||||
this.$emit('input', editor.getContent())
|
||||
})
|
||||
},
|
||||
setup(editor) {
|
||||
editor.on('FullscreenStateChanged', (e) => {
|
||||
_this.fullscreen = e.state
|
||||
})
|
||||
},
|
||||
// it will try to keep these URLs intact
|
||||
// https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@convert_urls/
|
||||
// https://stackoverflow.com/questions/5196205/disable-tinymce-absolute-to-relative-url-conversions
|
||||
convert_urls: false
|
||||
// 整合七牛上传
|
||||
// images_dataimg_filter(img) {
|
||||
// setTimeout(() => {
|
||||
// const $image = $(img);
|
||||
// $image.removeAttr('width');
|
||||
// $image.removeAttr('height');
|
||||
// if ($image[0].height && $image[0].width) {
|
||||
// $image.attr('data-wscntype', 'image');
|
||||
// $image.attr('data-wscnh', $image[0].height);
|
||||
// $image.attr('data-wscnw', $image[0].width);
|
||||
// $image.addClass('wscnph');
|
||||
// }
|
||||
// }, 0);
|
||||
// return img
|
||||
// },
|
||||
// images_upload_handler(blobInfo, success, failure, progress) {
|
||||
// progress(0);
|
||||
// const token = _this.$store.getters.token;
|
||||
// getToken(token).then(response => {
|
||||
// const url = response.data.qiniu_url;
|
||||
// const formData = new FormData();
|
||||
// formData.append('token', response.data.qiniu_token);
|
||||
// formData.append('key', response.data.qiniu_key);
|
||||
// formData.append('file', blobInfo.blob(), url);
|
||||
// upload(formData).then(() => {
|
||||
// success(url);
|
||||
// progress(100);
|
||||
// })
|
||||
// }).catch(err => {
|
||||
// failure('err')
|
||||
// console.log(err);
|
||||
// });
|
||||
// },
|
||||
})
|
||||
},
|
||||
destroyTinymce() {
|
||||
const tinymce = window.tinymce.get(this.tinymceId)
|
||||
if (this.fullscreen) {
|
||||
tinymce.execCommand('mceFullScreen')
|
||||
}
|
||||
|
||||
if (tinymce) {
|
||||
tinymce.destroy()
|
||||
}
|
||||
},
|
||||
setContent(value) {
|
||||
window.tinymce.get(this.tinymceId).setContent(value)
|
||||
},
|
||||
getContent() {
|
||||
window.tinymce.get(this.tinymceId).getContent()
|
||||
},
|
||||
imageSuccessCBK(arr) {
|
||||
arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.tinymce-containerty {
|
||||
position: relative;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.tinymce-containerty {
|
||||
::v-deep {
|
||||
.mce-fullscreen {
|
||||
z-index: 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tinymce-textarea {
|
||||
visibility: hidden;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.editor-custom-btn-container {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
/*z-index: 2005;*/
|
||||
}
|
||||
|
||||
.fullscreen .editor-custom-btn-container {
|
||||
z-index: 10000;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.editor-upload-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div class="j-markdown-editor" :id="dynamicId"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import load from './load'
|
||||
import { md_js, md_zh_cn_js } from './Resource'
|
||||
import defaultOptions from '@/components/jeecg/JMarkdownEditor/default-options.js'
|
||||
|
||||
export default {
|
||||
name: 'JMdEditorDyn',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return defaultOptions
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'markdown'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '300px'
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'zh-CN'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
dynamicId: this.id
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editorOptions() {
|
||||
const options = Object.assign({}, defaultOptions, this.options)
|
||||
options.initialEditType = this.mode
|
||||
options.height = this.height
|
||||
options.language = this.language
|
||||
return options
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newValue, preValue) {
|
||||
if (newValue !== preValue && newValue !== this.editor.getMarkdown()) {
|
||||
this.editor.setMarkdown(newValue)
|
||||
}
|
||||
},
|
||||
language(val) {
|
||||
this.destroyEditor()
|
||||
this.initEditor()
|
||||
},
|
||||
height(newValue) {
|
||||
this.editor.height(newValue)
|
||||
},
|
||||
mode(newValue) {
|
||||
this.editor.changeMode(newValue)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
destroyed() {
|
||||
this.destroyEditor()
|
||||
},
|
||||
methods: {
|
||||
init(){
|
||||
|
||||
this.initEditor()
|
||||
/* load(md_js,'',()=>{
|
||||
load(md_zh_cn_js,'',()=>{
|
||||
|
||||
})
|
||||
})*/
|
||||
},
|
||||
initEditor() {
|
||||
const Editor = toastui.Editor
|
||||
this.editor = new Editor({
|
||||
el: document.getElementById(this.dynamicId),
|
||||
...this.editorOptions
|
||||
})
|
||||
if (this.value) {
|
||||
this.editor.setMarkdown(this.value)
|
||||
}
|
||||
this.editor.on('change', () => {
|
||||
this.$emit('change', this.editor.getMarkdown())
|
||||
})
|
||||
},
|
||||
destroyEditor() {
|
||||
if (!this.editor) return
|
||||
this.editor.off('change')
|
||||
this.editor.remove()
|
||||
},
|
||||
setMarkdown(value) {
|
||||
this.editor.setMarkdown(value)
|
||||
},
|
||||
getMarkdown() {
|
||||
return this.editor.getMarkdown()
|
||||
},
|
||||
setHtml(value) {
|
||||
this.editor.setHtml(value)
|
||||
},
|
||||
getHtml() {
|
||||
return this.editor.getHtml()
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
|
||||
.j-markdown-editor {
|
||||
/deep/ .tui-editor-defaultUI {
|
||||
.te-mode-switch,
|
||||
.tui-scrollsync
|
||||
{
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,325 @@
|
||||
<template>
|
||||
<div class="jeecg-editor-ty" :class="fullCoder?'jeecg-editor-max':'jeecg-editor-min'">
|
||||
<a-icon v-if="fullScreen" class="full-screen-icon" :type="iconType" @click="()=>fullCoder=!fullCoder"/>
|
||||
<textarea :id="dynamicId" />
|
||||
<span @click="nullTipClick" class="null-tip" :class="{'null-tip-hidden': hasCode}" :style="nullTipStyle">{{ placeholderShow }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import load from './load'
|
||||
import '@/assets/less/codemirror_idea.css'
|
||||
import './cm_sql_hint.js'
|
||||
import { sql_keyword } from './Resource'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'JSqlCodeEditorDyn',
|
||||
props:{
|
||||
id: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return 'vue-editor-' + new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 显示行号
|
||||
lineNumbers: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: 999
|
||||
},
|
||||
autoHeight: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 不自适应高度的情况下生效的固定高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: '240px'
|
||||
},
|
||||
autoHeight: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 是否显示全屏按钮
|
||||
fullScreen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
autoHint:{
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
dynamicId: this.id,
|
||||
coder: '',
|
||||
hasCode: false,
|
||||
code: '',
|
||||
// code 编辑器 是否全屏
|
||||
fullCoder: false,
|
||||
iconType: 'fullscreen',
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
placeholderShow() {
|
||||
if (this.placeholder == null) {
|
||||
return `请在此输入javascript代码`
|
||||
} else {
|
||||
return this.placeholder
|
||||
}
|
||||
},
|
||||
nullTipStyle(){
|
||||
if (this.lineNumbers) {
|
||||
return { left: '36px' }
|
||||
} else {
|
||||
return { left: '12px' }
|
||||
}
|
||||
},
|
||||
isAutoHeight() {
|
||||
let {autoHeight} = this
|
||||
if (typeof autoHeight === 'string' && autoHeight.toLowerCase().trim() === '!ie') {
|
||||
autoHeight = !(isIE() || isIE11())
|
||||
} else {
|
||||
autoHeight = true
|
||||
}
|
||||
return autoHeight
|
||||
},
|
||||
fullScreenParentProps() {
|
||||
let props = {
|
||||
class: {
|
||||
'full-screen-parent': true,
|
||||
'full-screen': this.fullCoder,
|
||||
'auto-height': this.isAutoHeight
|
||||
},
|
||||
style: {}
|
||||
}
|
||||
if (this.fullCoder) {
|
||||
props.style['z-index'] = this.zIndex
|
||||
}
|
||||
if (!this.isAutoHeight) {
|
||||
props.style['height'] = (typeof this.height === 'number' ? this.height + 'px' : this.height)
|
||||
}
|
||||
return props
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fullCoder:{
|
||||
handler(value) {
|
||||
if(value){
|
||||
this.iconType="fullscreen-exit"
|
||||
}else{
|
||||
this.iconType="fullscreen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods:{
|
||||
init(){
|
||||
this.main();
|
||||
},
|
||||
main(){
|
||||
let obj = document.getElementById(this.dynamicId);
|
||||
const that = this;
|
||||
let editor = CodeMirror.fromTextArea(obj,{
|
||||
theme:'idea',
|
||||
lineNumbers: this.lineNumbers,
|
||||
lineWrapping: true,
|
||||
mode: "sql",
|
||||
indentUnit: 1,
|
||||
indentWithTabs: true,
|
||||
styleActiveLine: true,
|
||||
/* styleSelectedText: false, */
|
||||
extraKeys: {
|
||||
"F11": function(cm) {
|
||||
that.fullCoder = !that.fullCoder
|
||||
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
|
||||
},
|
||||
"Esc": function(cm) {
|
||||
that.fullCoder = false
|
||||
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
|
||||
},
|
||||
"Alt-/": function(cm) {
|
||||
cm.showHint();
|
||||
},
|
||||
"Tab": (cm) => {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('add');
|
||||
} else {
|
||||
//cm.indentLine(cm.getCursor().line, "add");
|
||||
//走两格 第三格输入
|
||||
cm.replaceSelection(Array(3).join(" "), "end", "+input");
|
||||
}
|
||||
},
|
||||
"Shift-Tab": (cm) => {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('subtract');
|
||||
} else {
|
||||
// cm.indentLine(cm.getCursor().line, "subtract");
|
||||
const cursor = cm.getCursor();
|
||||
// 光标回退 indexUnit 字符
|
||||
cm.setCursor({line: cursor.line, ch: cursor.ch - 4});
|
||||
}
|
||||
return ;
|
||||
}
|
||||
}
|
||||
})
|
||||
this.coder = editor
|
||||
this.addEvent();
|
||||
this.setCoderValue();
|
||||
this.addSystemHint();
|
||||
},
|
||||
setCoderValue(){
|
||||
if(this.value||this.code){
|
||||
this.hasCode=true
|
||||
this.setCodeContent(this.value || this.code)
|
||||
}else{
|
||||
this.coder.setValue('')
|
||||
this.hasCode=false
|
||||
}
|
||||
},
|
||||
getCodeContent(){
|
||||
return this.code
|
||||
},
|
||||
setCodeContent(val){
|
||||
setTimeout(()=>{
|
||||
if(!val){
|
||||
this.coder.setValue('')
|
||||
}else{
|
||||
this.coder.setValue(val)
|
||||
}
|
||||
},300)
|
||||
},
|
||||
addSystemHint(){
|
||||
this.coder.setOption('hintOptions', {
|
||||
completeSingle: false,
|
||||
tables: sql_keyword
|
||||
});
|
||||
},
|
||||
addEvent(){
|
||||
if(this.autoHint){
|
||||
this.coder.on('cursorActivity', ()=>{
|
||||
this.coder.showHint();
|
||||
});
|
||||
}
|
||||
this.coder.on('change', (coder) => {
|
||||
this.code = coder.getValue()
|
||||
if(this.code){
|
||||
this.hasCode=true
|
||||
}else{
|
||||
this.hasCode=false
|
||||
}
|
||||
if (this.$emit) {
|
||||
this.$emit('input', this.code)
|
||||
}
|
||||
});
|
||||
this.coder.on('focus', () => {
|
||||
this.hasCode=true
|
||||
});
|
||||
this.coder.on('blur', () => {
|
||||
if(this.code){
|
||||
this.hasCode=true
|
||||
}else{
|
||||
this.hasCode=false
|
||||
}
|
||||
});
|
||||
},
|
||||
loadResource(src,type){
|
||||
return new Promise((resolve,reject)=>{
|
||||
load(src,type,(msg)=>{
|
||||
if(!msg){
|
||||
resolve();
|
||||
}else{
|
||||
reject(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
nullTipClick(){
|
||||
this.coder.focus()
|
||||
},
|
||||
fullToggle(){
|
||||
this.fullCoder = !this.fullCoder
|
||||
this.coder.setOption("fullScreen", this.fullCoder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
<style lang="less" >
|
||||
.jeecg-editor-ty{
|
||||
position: relative;
|
||||
|
||||
.full-screen-icon {
|
||||
opacity: 0;
|
||||
color: black;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 24px;
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 2px;
|
||||
z-index: 9;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
&:hover {
|
||||
.full-screen-icon {
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.88);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.null-tip{
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 36px;
|
||||
z-index: 10;
|
||||
font-size:16px;
|
||||
color: #acaaaac9;
|
||||
line-height: initial;
|
||||
}
|
||||
.null-tip-hidden{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.jeecg-editor-max{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
|
||||
.CodeMirror{
|
||||
position: inherit !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.full-screen-icon{
|
||||
z-index:9999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<div class="jeecg-editor-ty" :class="fullCoder?'jeecg-editor-max':'jeecg-editor-min'">
|
||||
<a-icon v-if="fullScreen" class="full-screen-icon" :type="iconType" @click="()=>fullCoder=!fullCoder"/>
|
||||
<textarea :id="dynamicId" />
|
||||
<span @click="nullTipClick" class="null-tip" :class="{'null-tip-hidden': hasCode}" :style="nullTipStyle">{{ placeholderShow }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import '@/assets/less/codemirror_idea.css'
|
||||
import './cm_hint.js'
|
||||
|
||||
export default {
|
||||
name: 'JsCodeEditorDyn',
|
||||
props:{
|
||||
id: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return 'vue-editor-' + new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 显示行号
|
||||
lineNumbers: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: 999
|
||||
},
|
||||
autoHeight: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 不自适应高度的情况下生效的固定高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: '240px'
|
||||
},
|
||||
autoHeight: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 是否显示全屏按钮
|
||||
fullScreen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
dynamicId: this.id,
|
||||
coder: '',
|
||||
hasCode: false,
|
||||
code: '',
|
||||
// code 编辑器 是否全屏
|
||||
fullCoder: false,
|
||||
iconType: 'fullscreen',
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
placeholderShow() {
|
||||
if (this.placeholder == null) {
|
||||
return `请在此输入javascript代码`
|
||||
} else {
|
||||
return this.placeholder
|
||||
}
|
||||
},
|
||||
nullTipStyle(){
|
||||
if (this.lineNumbers) {
|
||||
return { left: '36px' }
|
||||
} else {
|
||||
return { left: '12px' }
|
||||
}
|
||||
},
|
||||
isAutoHeight() {
|
||||
let {autoHeight} = this
|
||||
if (typeof autoHeight === 'string' && autoHeight.toLowerCase().trim() === '!ie') {
|
||||
autoHeight = !(isIE() || isIE11())
|
||||
} else {
|
||||
autoHeight = true
|
||||
}
|
||||
return autoHeight
|
||||
},
|
||||
fullScreenParentProps() {
|
||||
let props = {
|
||||
class: {
|
||||
'full-screen-parent': true,
|
||||
'full-screen': this.fullCoder,
|
||||
'auto-height': this.isAutoHeight
|
||||
},
|
||||
style: {}
|
||||
}
|
||||
if (this.fullCoder) {
|
||||
props.style['z-index'] = this.zIndex
|
||||
}
|
||||
if (!this.isAutoHeight) {
|
||||
props.style['height'] = (typeof this.height === 'number' ? this.height + 'px' : this.height)
|
||||
}
|
||||
return props
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fullCoder:{
|
||||
handler(value) {
|
||||
if(value){
|
||||
this.iconType="fullscreen-exit"
|
||||
}else{
|
||||
this.iconType="fullscreen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods:{
|
||||
init(){
|
||||
this.main();
|
||||
},
|
||||
main(){
|
||||
let obj = document.getElementById(this.dynamicId);
|
||||
const that = this;
|
||||
let editor = CodeMirror.fromTextArea(obj,{
|
||||
theme:'idea',
|
||||
lineNumbers: this.lineNumbers,
|
||||
lineWrapping: true,
|
||||
mode: "javascript",
|
||||
indentUnit: 1,
|
||||
indentWithTabs: true,
|
||||
styleActiveLine: true,
|
||||
/* styleSelectedText: false, */
|
||||
extraKeys: {
|
||||
"F11": function(cm) {
|
||||
that.fullCoder = !that.fullCoder
|
||||
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
|
||||
},
|
||||
"Esc": function(cm) {
|
||||
that.fullCoder = false
|
||||
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
|
||||
},
|
||||
"Alt-/": function(cm) {
|
||||
let a = cm.getValue()+""
|
||||
console.log('a',a)
|
||||
cm.showHint();
|
||||
},
|
||||
"Tab": (cm) => {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('add');
|
||||
} else {
|
||||
//cm.indentLine(cm.getCursor().line, "add");
|
||||
//走两格 第三格输入
|
||||
cm.replaceSelection(Array(3).join(" "), "end", "+input");
|
||||
}
|
||||
},
|
||||
"Shift-Tab": (cm) => {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('subtract');
|
||||
} else {
|
||||
// cm.indentLine(cm.getCursor().line, "subtract");
|
||||
const cursor = cm.getCursor();
|
||||
// 光标回退 indexUnit 字符
|
||||
cm.setCursor({line: cursor.line, ch: cursor.ch - 4});
|
||||
}
|
||||
return ;
|
||||
}
|
||||
}
|
||||
})
|
||||
this.coder = editor
|
||||
this.addEvent();
|
||||
this.setCoderValue();
|
||||
},
|
||||
setCoderValue(){
|
||||
if(this.value||this.code){
|
||||
this.hasCode=true
|
||||
this.setCodeContent(this.value || this.code)
|
||||
}else{
|
||||
this.coder.setValue('')
|
||||
this.hasCode=false
|
||||
}
|
||||
},
|
||||
getCodeContent(){
|
||||
return this.code
|
||||
},
|
||||
setCodeContent(val){
|
||||
setTimeout(()=>{
|
||||
if(!val){
|
||||
this.coder.setValue('')
|
||||
}else{
|
||||
this.coder.setValue(val)
|
||||
}
|
||||
},300)
|
||||
},
|
||||
addEvent(){
|
||||
const that = this;
|
||||
this.coder.on('cursorActivity',function(wl) {
|
||||
let arr = wl.state.activeLines
|
||||
if(arr && arr.length>0){
|
||||
let text = arr[0].text
|
||||
if(text.lastIndexOf('that.')>=0){
|
||||
that.coder.showHint();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.coder.on('change', (coder) => {
|
||||
this.code = coder.getValue()
|
||||
if(this.code){
|
||||
this.hasCode=true
|
||||
}else{
|
||||
this.hasCode=false
|
||||
}
|
||||
if (this.$emit) {
|
||||
this.$emit('input', this.code)
|
||||
}
|
||||
});
|
||||
this.coder.on('focus', () => {
|
||||
this.hasCode=true
|
||||
});
|
||||
this.coder.on('blur', () => {
|
||||
if(this.code){
|
||||
this.hasCode=true
|
||||
}else{
|
||||
this.hasCode=false
|
||||
}
|
||||
});
|
||||
},
|
||||
loadResource(src,type){
|
||||
return new Promise((resolve,reject)=>{
|
||||
load(src,type,(msg)=>{
|
||||
if(!msg){
|
||||
resolve();
|
||||
}else{
|
||||
reject(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
nullTipClick(){
|
||||
this.coder.focus()
|
||||
},
|
||||
fullToggle(){
|
||||
this.fullCoder = !this.fullCoder
|
||||
this.coder.setOption("fullScreen", this.fullCoder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
<style lang="less" >
|
||||
.jeecg-editor-ty{
|
||||
position: relative;
|
||||
|
||||
.full-screen-icon {
|
||||
opacity: 0;
|
||||
color: black;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 24px;
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 2px;
|
||||
z-index: 9;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
&:hover {
|
||||
.full-screen-icon {
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.88);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.null-tip{
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 36px;
|
||||
z-index: 10;
|
||||
font-size:16px;
|
||||
color: #acaaaac9;
|
||||
line-height: initial;
|
||||
}
|
||||
.null-tip-hidden{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.jeecg-editor-max{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
|
||||
.CodeMirror{
|
||||
position: inherit !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.full-screen-icon{
|
||||
z-index:9999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,24 @@
|
||||
|
||||
/**js编辑器关键词用于提示*/
|
||||
const js_keyword = [
|
||||
'that',
|
||||
'getAction','postAction','deleteAction',
|
||||
'beforeAdd','beforeEdit','beforeDelete','mounted','created','show'
|
||||
]
|
||||
|
||||
/**js编辑器 方法名用于提示*/
|
||||
const js_method = [
|
||||
'.getSelectOptions','.changeOptions','.triggleChangeValues','.immediateEnhance ','.simpleDateFormat','.lodash'
|
||||
]
|
||||
|
||||
/**sql编辑器 表名字段名用于提示*/
|
||||
const sql_keyword = {
|
||||
sys_user: ['USERNAME', 'REALNAME', 'ID','BIRTHDAY','AGE'],
|
||||
demo: ['name', 'age', 'id', 'sex']
|
||||
}
|
||||
|
||||
export {
|
||||
js_keyword,
|
||||
js_method,
|
||||
sql_keyword
|
||||
}
|
||||
177
ant-design-vue-jeecg/src/components/jeecg/dynamic/cm_hint.js
Normal file
177
ant-design-vue-jeecg/src/components/jeecg/dynamic/cm_hint.js
Normal file
@ -0,0 +1,177 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
import { js_keyword, js_method } from './Resource'
|
||||
(function(mod) {
|
||||
mod(CodeMirror);
|
||||
/*if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env*/
|
||||
|
||||
})(function(CodeMirror) {
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
function forEach(arr, f) {
|
||||
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
|
||||
}
|
||||
|
||||
function arrayContains(arr, item) {
|
||||
if (!Array.prototype.indexOf) {
|
||||
var i = arr.length;
|
||||
while (i--) {
|
||||
if (arr[i] === item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return arr.indexOf(item) != -1;
|
||||
}
|
||||
|
||||
function scriptHint(editor, keywords, getToken, options) {
|
||||
// Find the token at the cursor
|
||||
var cur = editor.getCursor(), token = getToken(editor, cur);
|
||||
if (/\b(?:string|comment)\b/.test(token.type)) return;
|
||||
var innerMode = CodeMirror.innerMode(editor.getMode(), token.state);
|
||||
if (innerMode.mode.helperType === "json") return;
|
||||
token.state = innerMode.state;
|
||||
if('.' === token.string){
|
||||
let arr = []
|
||||
for(let k of js_method){
|
||||
arr.push(k)
|
||||
}
|
||||
return {
|
||||
list: arr,
|
||||
from: Pos(cur.line, token.start),
|
||||
to: Pos(cur.line, token.end)
|
||||
};
|
||||
}
|
||||
// If it's not a 'word-style' token, ignore the token.
|
||||
if (!/^[\w$_]*$/.test(token.string)) {
|
||||
token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
||||
type: token.string == "." ? "property" : null};
|
||||
} else if (token.end > cur.ch) {
|
||||
token.end = cur.ch;
|
||||
token.string = token.string.slice(0, cur.ch - token.start);
|
||||
}
|
||||
|
||||
var tprop = token;
|
||||
// If it is a property, find out what it is a property of.
|
||||
while (tprop.type == "property") {
|
||||
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
||||
if (tprop.string != ".") return;
|
||||
tprop = getToken(editor, Pos(cur.line, tprop.start));
|
||||
if (!context) var context = [];
|
||||
context.push(tprop);
|
||||
}
|
||||
return {list: getCompletions(token, context, keywords, options),
|
||||
from: Pos(cur.line, token.start),
|
||||
to: Pos(cur.line, token.end)};
|
||||
}
|
||||
|
||||
function javascriptHint(editor, options) {
|
||||
return scriptHint(editor, javascriptKeywords,
|
||||
function (e, cur) {return e.getTokenAt(cur);},
|
||||
options);
|
||||
};
|
||||
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
|
||||
|
||||
function getCoffeeScriptToken(editor, cur) {
|
||||
// This getToken, it is for coffeescript, imitates the behavior of
|
||||
// getTokenAt method in javascript.js, that is, returning "property"
|
||||
// type and treat "." as indepenent token.
|
||||
var token = editor.getTokenAt(cur);
|
||||
if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
|
||||
token.end = token.start;
|
||||
token.string = '.';
|
||||
token.type = "property";
|
||||
}
|
||||
else if (/^\.[\w$_]*$/.test(token.string)) {
|
||||
token.type = "property";
|
||||
token.start++;
|
||||
token.string = token.string.replace(/\./, '');
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
function coffeescriptHint(editor, options) {
|
||||
return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
|
||||
}
|
||||
CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
|
||||
|
||||
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
|
||||
"toUpperCase toLowerCase split concat match replace search").split(" ");
|
||||
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
|
||||
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
|
||||
var funcProps = "prototype apply call bind".split(" ");
|
||||
|
||||
var javascriptKeywords = ("break case catch class const continue debugger default delete do else export extends false finally for function " +
|
||||
"if in import instanceof new null return super switch this throw true try typeof var void while with yield that").split(" ");
|
||||
for(let jk of js_keyword){
|
||||
javascriptKeywords.push(jk)
|
||||
}
|
||||
var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
|
||||
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
|
||||
|
||||
function forAllProps(obj, callback) {
|
||||
if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
|
||||
for (var name in obj) callback(name)
|
||||
} else {
|
||||
for (var o = obj; o; o = Object.getPrototypeOf(o))
|
||||
Object.getOwnPropertyNames(o).forEach(callback)
|
||||
}
|
||||
}
|
||||
|
||||
function getCompletions(token, context, keywords, options) {
|
||||
var found = [], start = token.string, global = options && options.globalScope || window;
|
||||
function maybeAdd(str) {
|
||||
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
|
||||
}
|
||||
function gatherCompletions(obj) {
|
||||
if (typeof obj == "string") forEach(stringProps, maybeAdd);
|
||||
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
|
||||
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
|
||||
forAllProps(obj, maybeAdd)
|
||||
}
|
||||
|
||||
if (context && context.length) {
|
||||
// If this is a property, see if it belongs to some object we can
|
||||
// find in the current environment.
|
||||
var obj = context.pop(), base;
|
||||
if (obj.type && obj.type.indexOf("variable") === 0) {
|
||||
if (options && options.additionalContext)
|
||||
base = options.additionalContext[obj.string];
|
||||
if (!options || options.useGlobalScope !== false)
|
||||
base = base || global[obj.string];
|
||||
} else if (obj.type == "string") {
|
||||
base = "";
|
||||
} else if (obj.type == "atom") {
|
||||
base = 1;
|
||||
} else if (obj.type == "function") {
|
||||
if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
|
||||
(typeof global.jQuery == 'function'))
|
||||
base = global.jQuery();
|
||||
else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
|
||||
base = global._();
|
||||
}
|
||||
while (base != null && context.length)
|
||||
base = base[context.pop().string];
|
||||
if (base != null) gatherCompletions(base);
|
||||
} else {
|
||||
// If not, just look in the global object, any local scope, and optional additional-context
|
||||
// (reading into JS mode internals to get at the local and global variables)
|
||||
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
|
||||
for (var c = token.state.context; c; c = c.prev)
|
||||
for (var v = c.vars; v; v = v.next) maybeAdd(v.name)
|
||||
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
|
||||
if (options && options.additionalContext != null)
|
||||
for (var key in options.additionalContext)
|
||||
maybeAdd(key);
|
||||
if (!options || options.useGlobalScope !== false)
|
||||
gatherCompletions(global);
|
||||
forEach(keywords, maybeAdd);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
});
|
||||
305
ant-design-vue-jeecg/src/components/jeecg/dynamic/cm_sql_hint.js
Normal file
305
ant-design-vue-jeecg/src/components/jeecg/dynamic/cm_sql_hint.js
Normal file
@ -0,0 +1,305 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
/*if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
|
||||
else */
|
||||
// Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var tables;
|
||||
var defaultTable;
|
||||
var keywords;
|
||||
var identifierQuote;
|
||||
var CONS = {
|
||||
QUERY_DIV: ";",
|
||||
ALIAS_KEYWORD: "AS"
|
||||
};
|
||||
var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
|
||||
|
||||
function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
|
||||
|
||||
function getKeywords(editor) {
|
||||
var mode = editor.doc.modeOption;
|
||||
if (mode === "sql") mode = "text/x-sql";
|
||||
return CodeMirror.resolveMode(mode).keywords;
|
||||
}
|
||||
|
||||
function getIdentifierQuote(editor) {
|
||||
var mode = editor.doc.modeOption;
|
||||
if (mode === "sql") mode = "text/x-sql";
|
||||
return CodeMirror.resolveMode(mode).identifierQuote || "`";
|
||||
}
|
||||
|
||||
function getText(item) {
|
||||
return typeof item == "string" ? item : item.text;
|
||||
}
|
||||
|
||||
function wrapTable(name, value) {
|
||||
if (isArray(value)) value = {columns: value}
|
||||
if (!value.text) value.text = name
|
||||
return value
|
||||
}
|
||||
|
||||
function parseTables(input) {
|
||||
var result = {}
|
||||
if (isArray(input)) {
|
||||
for (var i = input.length - 1; i >= 0; i--) {
|
||||
var item = input[i]
|
||||
result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
|
||||
}
|
||||
} else if (input) {
|
||||
for (var name in input)
|
||||
result[name.toUpperCase()] = wrapTable(name, input[name])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function getTable(name) {
|
||||
return tables[name.toUpperCase()]
|
||||
}
|
||||
|
||||
function shallowClone(object) {
|
||||
var result = {};
|
||||
for (var key in object) if (object.hasOwnProperty(key))
|
||||
result[key] = object[key];
|
||||
return result;
|
||||
}
|
||||
|
||||
function match(string, word) {
|
||||
var len = string.length;
|
||||
var sub = getText(word).substr(0, len);
|
||||
return string.toUpperCase() === sub.toUpperCase();
|
||||
}
|
||||
|
||||
function addMatches(result, search, wordlist, formatter) {
|
||||
if (isArray(wordlist)) {
|
||||
for (var i = 0; i < wordlist.length; i++)
|
||||
if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
|
||||
} else {
|
||||
for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
|
||||
var val = wordlist[word]
|
||||
if (!val || val === true)
|
||||
val = word
|
||||
else
|
||||
val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
|
||||
if (match(search, val)) result.push(formatter(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cleanName(name) {
|
||||
// Get rid name from identifierQuote and preceding dot(.)
|
||||
if (name.charAt(0) == ".") {
|
||||
name = name.substr(1);
|
||||
}
|
||||
// replace doublicated identifierQuotes with single identifierQuotes
|
||||
// and remove single identifierQuotes
|
||||
var nameParts = name.split(identifierQuote+identifierQuote);
|
||||
for (var i = 0; i < nameParts.length; i++)
|
||||
nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
|
||||
return nameParts.join(identifierQuote);
|
||||
}
|
||||
|
||||
function insertIdentifierQuotes(name) {
|
||||
var nameParts = getText(name).split(".");
|
||||
for (var i = 0; i < nameParts.length; i++)
|
||||
nameParts[i] = identifierQuote +
|
||||
// doublicate identifierQuotes
|
||||
nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
|
||||
identifierQuote;
|
||||
var escaped = nameParts.join(".");
|
||||
if (typeof name == "string") return escaped;
|
||||
name = shallowClone(name);
|
||||
name.text = escaped;
|
||||
return name;
|
||||
}
|
||||
|
||||
function nameCompletion(cur, token, result, editor) {
|
||||
// Try to complete table, column names and return start position of completion
|
||||
var useIdentifierQuotes = false;
|
||||
var nameParts = [];
|
||||
var start = token.start;
|
||||
var cont = true;
|
||||
while (cont) {
|
||||
cont = (token.string.charAt(0) == ".");
|
||||
useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
|
||||
|
||||
start = token.start;
|
||||
nameParts.unshift(cleanName(token.string));
|
||||
|
||||
token = editor.getTokenAt(Pos(cur.line, token.start));
|
||||
if (token.string == ".") {
|
||||
cont = true;
|
||||
token = editor.getTokenAt(Pos(cur.line, token.start));
|
||||
}
|
||||
}
|
||||
|
||||
// Try to complete table names
|
||||
var string = nameParts.join(".");
|
||||
addMatches(result, string, tables, function(w) {
|
||||
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
|
||||
});
|
||||
|
||||
// Try to complete columns from defaultTable
|
||||
addMatches(result, string, defaultTable, function(w) {
|
||||
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
|
||||
});
|
||||
|
||||
// Try to complete columns
|
||||
string = nameParts.pop();
|
||||
var table = nameParts.join(".");
|
||||
|
||||
var alias = false;
|
||||
var aliasTable = table;
|
||||
// Check if table is available. If not, find table by Alias
|
||||
if (!getTable(table)) {
|
||||
var oldTable = table;
|
||||
table = findTableByAlias(table, editor);
|
||||
if (table !== oldTable) alias = true;
|
||||
}
|
||||
|
||||
var columns = getTable(table);
|
||||
if (columns && columns.columns)
|
||||
columns = columns.columns;
|
||||
|
||||
if (columns) {
|
||||
addMatches(result, string, columns, function(w) {
|
||||
var tableInsert = table;
|
||||
if (alias == true) tableInsert = aliasTable;
|
||||
if (typeof w == "string") {
|
||||
w = tableInsert + "." + w;
|
||||
} else {
|
||||
w = shallowClone(w);
|
||||
w.text = tableInsert + "." + w.text;
|
||||
}
|
||||
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
|
||||
});
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
function eachWord(lineText, f) {
|
||||
var words = lineText.split(/\s+/)
|
||||
for (var i = 0; i < words.length; i++)
|
||||
if (words[i]) f(words[i].replace(/[,;]/g, ''))
|
||||
}
|
||||
|
||||
function findTableByAlias(alias, editor) {
|
||||
var doc = editor.doc;
|
||||
var fullQuery = doc.getValue();
|
||||
var aliasUpperCase = alias.toUpperCase();
|
||||
var previousWord = "";
|
||||
var table = "";
|
||||
var separator = [];
|
||||
var validRange = {
|
||||
start: Pos(0, 0),
|
||||
end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
|
||||
};
|
||||
|
||||
//add separator
|
||||
var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
|
||||
while(indexOfSeparator != -1) {
|
||||
separator.push(doc.posFromIndex(indexOfSeparator));
|
||||
indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
|
||||
}
|
||||
separator.unshift(Pos(0, 0));
|
||||
separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
|
||||
|
||||
//find valid range
|
||||
var prevItem = null;
|
||||
var current = editor.getCursor()
|
||||
for (var i = 0; i < separator.length; i++) {
|
||||
if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
|
||||
validRange = {start: prevItem, end: separator[i]};
|
||||
break;
|
||||
}
|
||||
prevItem = separator[i];
|
||||
}
|
||||
|
||||
if (validRange.start) {
|
||||
var query = doc.getRange(validRange.start, validRange.end, false);
|
||||
|
||||
for (var i = 0; i < query.length; i++) {
|
||||
var lineText = query[i];
|
||||
eachWord(lineText, function(word) {
|
||||
var wordUpperCase = word.toUpperCase();
|
||||
if (wordUpperCase === aliasUpperCase && getTable(previousWord))
|
||||
table = previousWord;
|
||||
if (wordUpperCase !== CONS.ALIAS_KEYWORD)
|
||||
previousWord = word;
|
||||
});
|
||||
if (table) break;
|
||||
}
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
CodeMirror.registerHelper("hint", "sql", function(editor, options) {
|
||||
tables = parseTables(options && options.tables)
|
||||
var defaultTableName = options && options.defaultTable;
|
||||
var disableKeywords = options && options.disableKeywords;
|
||||
defaultTable = defaultTableName && getTable(defaultTableName);
|
||||
keywords = getKeywords(editor);
|
||||
identifierQuote = getIdentifierQuote(editor);
|
||||
|
||||
if (defaultTableName && !defaultTable)
|
||||
defaultTable = findTableByAlias(defaultTableName, editor);
|
||||
|
||||
defaultTable = defaultTable || [];
|
||||
|
||||
if (defaultTable.columns)
|
||||
defaultTable = defaultTable.columns;
|
||||
|
||||
var cur = editor.getCursor();
|
||||
var result = [];
|
||||
var token = editor.getTokenAt(cur), start, end, search;
|
||||
if (token.end > cur.ch) {
|
||||
token.end = cur.ch;
|
||||
token.string = token.string.slice(0, cur.ch - token.start);
|
||||
}
|
||||
|
||||
if (token.string.match(/^[.`"'\w@][\w$#]*$/g)) {
|
||||
search = token.string;
|
||||
start = token.start;
|
||||
end = token.end;
|
||||
} else {
|
||||
start = end = cur.ch;
|
||||
search = "";
|
||||
}
|
||||
if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
|
||||
start = nameCompletion(cur, token, result, editor);
|
||||
} else {
|
||||
var objectOrClass = function(w, className) {
|
||||
if (typeof w === "object") {
|
||||
w.className = className;
|
||||
} else {
|
||||
w = { text: w, className: className };
|
||||
}
|
||||
return w;
|
||||
};
|
||||
addMatches(result, search, defaultTable, function(w) {
|
||||
return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table");
|
||||
});
|
||||
addMatches(
|
||||
result,
|
||||
search,
|
||||
tables, function(w) {
|
||||
return objectOrClass(w, "CodeMirror-hint-table");
|
||||
}
|
||||
);
|
||||
if (!disableKeywords)
|
||||
addMatches(result, search, keywords, function(w) {
|
||||
return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword");
|
||||
});
|
||||
}
|
||||
|
||||
return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
|
||||
});
|
||||
});
|
||||
92
ant-design-vue-jeecg/src/components/jeecg/dynamic/load.js
Normal file
92
ant-design-vue-jeecg/src/components/jeecg/dynamic/load.js
Normal file
@ -0,0 +1,92 @@
|
||||
let callbacks = []
|
||||
|
||||
function loadSuccess(key) {
|
||||
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
|
||||
// check is successfully downloaded script
|
||||
return window[key]
|
||||
}
|
||||
|
||||
const load = (src, type, callback) => {
|
||||
if(type=='link'){
|
||||
|
||||
loadStyle(src, callback)
|
||||
}else{
|
||||
let loadKey = ''
|
||||
if(src.indexOf('tinymce')>=0){
|
||||
loadKey = 'tinymce'
|
||||
}else if(src.indexOf('codemirror')>=0){
|
||||
loadKey = 'CodeMirror'
|
||||
}
|
||||
const scriptTag = document.getElementById(src)
|
||||
//const cb = callback || function() {}
|
||||
if (!scriptTag) {
|
||||
const script = document.createElement('script')
|
||||
script.src = src // src url for the third-party library being loaded.
|
||||
script.id = src
|
||||
script.onload=()=>callback()
|
||||
script.onerror=()=>callback('加载失败:'+src)
|
||||
document.body.appendChild(script)
|
||||
//callbacks.push(cb)
|
||||
// const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
|
||||
// onEnd(script)
|
||||
}else{
|
||||
if (loadSuccess(loadKey)) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
if (scriptTag) {
|
||||
/* else {
|
||||
callbacks.push(cb)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
function stdOnEnd(script) {
|
||||
script['onload'] = function() {
|
||||
// this.onload = null here is necessary
|
||||
// because even IE9 works not like others
|
||||
this.onerror = this.onload = null
|
||||
for (const cb of callbacks) {
|
||||
cb(null, script)
|
||||
}
|
||||
callbacks = null
|
||||
}
|
||||
script['onerror'] = function() {
|
||||
this.onerror = this.onload = null
|
||||
cb(new Error('Failed to load ' + src), script)
|
||||
}
|
||||
}
|
||||
|
||||
function ieOnEnd(script) {
|
||||
script.onreadystatechange = function() {
|
||||
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
|
||||
this.onreadystatechange = null
|
||||
for (const cb of callbacks) {
|
||||
cb(null, script) // there is no way to catch loading errors in IE8
|
||||
}
|
||||
callbacks = null
|
||||
}
|
||||
}
|
||||
|
||||
function loadStyle(src, callback) {
|
||||
const link = document.getElementById(src)
|
||||
if (!link) {
|
||||
const link = document.createElement('link')
|
||||
link.setAttribute("rel", "stylesheet");
|
||||
link.setAttribute("type", "text/css");
|
||||
link.setAttribute("href", src);
|
||||
link.id = src
|
||||
let heads = document.getElementsByTagName("head")
|
||||
if(heads.length){
|
||||
heads[0].appendChild(link)
|
||||
}else{
|
||||
document.documentElement.appendChild(link)
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default load
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-modal
|
||||
title="文件上传"
|
||||
:title="fileType === 'image' ? '图片上传' : '文件上传'"
|
||||
:width="width"
|
||||
:visible="visible"
|
||||
@ok="ok"
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<a-icon slot="suffix" type="fullscreen" @click.stop="pop" />
|
||||
</a-input>
|
||||
<div slot="content">
|
||||
<textarea :value="inputContent" :disabled="disabled" @input="handleInputChange" :style="{ height: height + 'px', width: width + 'px' }"></textarea>
|
||||
<a-textarea ref="textarea" :value="inputContent" :disabled="disabled" @input="handleInputChange" :style="{ height: height + 'px', width: width + 'px' }"/>
|
||||
</div>
|
||||
</a-popover>
|
||||
</template>
|
||||
@ -84,6 +84,9 @@
|
||||
},
|
||||
pop(){
|
||||
this.visible=true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.textarea.focus()
|
||||
})
|
||||
},
|
||||
getPopupContainer(node){
|
||||
if(!this.popContainer){
|
||||
|
||||
@ -214,8 +214,31 @@
|
||||
return filterObj(param);
|
||||
},
|
||||
handleChangeInTableSelect(selectedRowKeys, selectionRows) {
|
||||
//update-begin-author:taoyan date:2020902 for:【issue】开源online的几个问题 LOWCOD-844
|
||||
if(!selectedRowKeys || selectedRowKeys.length==0){
|
||||
this.table.selectionRows = []
|
||||
}else if(selectedRowKeys.length == selectionRows.length){
|
||||
this.table.selectionRows = selectionRows
|
||||
}else{
|
||||
//当两者长度不一的时候 需要判断
|
||||
let keys = this.table.selectedRowKeys
|
||||
let rows = this.table.selectionRows;
|
||||
//这个循环 添加新的记录
|
||||
for(let i=0;i<selectionRows.length;i++){
|
||||
let combineKey = this.combineRowKey(selectionRows[i])
|
||||
if(keys.indexOf(combineKey)<0){
|
||||
//如果 原来的key 不包含当前记录 push
|
||||
rows.push(selectionRows[i])
|
||||
}
|
||||
}
|
||||
//这个循环 移除取消选中的数据
|
||||
this.table.selectionRows = rows.filter(item=>{
|
||||
let combineKey = this.combineRowKey(item)
|
||||
return selectedRowKeys.indexOf(combineKey)>=0
|
||||
})
|
||||
}
|
||||
//update-end-author:taoyan date:2020902 for:【issue】开源online的几个问题 LOWCOD-844
|
||||
this.table.selectedRowKeys = selectedRowKeys
|
||||
this.table.selectionRows = selectionRows
|
||||
},
|
||||
handleChangeInTable(pagination, filters, sorter) {
|
||||
//分页、排序、筛选变化时触发
|
||||
|
||||
Reference in New Issue
Block a user