This commit is contained in:
fengchenchen
2023-08-14 17:10:11 +08:00
commit 37f6ec895d
1145 changed files with 437754 additions and 0 deletions
@@ -0,0 +1,387 @@
<template>
<j-modal
centered
:title="name + '选择'"
:width="width"
:visible="visible"
switchFullscreen
@ok="handleOk"
@cancel="close"
cancelText="关闭">
<a-row :gutter="18">
<a-col :span="16">
<!-- 查询区域 -->
<a-form layout="inline" class="j-inline-form">
<!-- 固定条件 -->
<a-form-item :label="(queryParamText||name)">
<j-input v-model="queryParam[queryParamCode||valueKey]" :placeholder="'请输入' + (queryParamText||name)" @pressEnter="searchQuery"/>
</a-form-item>
<!-- 动态生成的查询条件 -->
<j-select-biz-query-item v-if="queryConfig.length>0" v-show="showMoreQueryItems" :queryParam="queryParam" :queryConfig="queryConfig" @pressEnter="searchQuery"/>
<!-- 按钮 -->
<a-button :style="{marginBottom:'12px'}" type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button :style="{marginBottom:'12px'}" type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
<a v-if="queryConfig.length>0" @click="showMoreQueryItems=!showMoreQueryItems" style="margin-left: 8px">
{{ showMoreQueryItems ? '收起' : '展开' }}
<a-icon :type="showMoreQueryItems ? 'up' : 'down'"/>
</a>
</a-form>
<a-table
size="middle"
bordered
:rowKey="rowKey"
:columns="innerColumns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:scroll="{ y: 240 }"
:rowSelection="{selectedRowKeys, onChange: onSelectChange, type: multiple ? 'checkbox':'radio'}"
:customRow="customRowFn"
@change="handleTableChange">
</a-table>
</a-col>
<a-col :span="8">
<a-card :title="'已选' + name" :bordered="false" :head-style="{padding:0}" :body-style="{padding:0}">
<a-table size="middle" :rowKey="rowKey" bordered v-bind="selectedTable">
<span slot="action" slot-scope="text, record, index">
<a @click="handleDeleteSelected(record, index)">删除</a>
</span>
</a-table>
</a-card>
</a-col>
</a-row>
</j-modal>
</template>
<script>
import { getAction } from '@/api/manage'
import Ellipsis from '@/components/Ellipsis'
import { JeroListMixin } from '@/mixins/JeroListMixin'
import { pushIfNotExist } from '@/utils/util'
import JSelectBizQueryItem from './JSelectBizQueryItem'
import { cloneDeep } from 'lodash'
export default {
name: 'JSelectBizComponentModal',
mixins: [JeroListMixin],
components: { Ellipsis, JSelectBizQueryItem },
props: {
value: {
type: Array,
default: () => []
},
visible: {
type: Boolean,
default: false
},
valueKey: {
type: String,
required: true
},
multiple: {
type: Boolean,
default: true
},
width: {
type: Number,
default: 900
},
name: {
type: String,
default: ''
},
listUrl: {
type: String,
required: true,
default: ''
},
// 根据 value 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realname
valueUrl: {
type: String,
default: ''
},
displayKey: {
type: String,
default: null
},
columns: {
type: Array,
required: true,
default: () => []
},
// 查询条件Code
queryParamCode: {
type: String,
default: null
},
// 查询条件文字
queryParamText: {
type: String,
default: null
},
// 查询配置
queryConfig: {
type: Array,
default: () => []
},
rowKey: {
type: String,
default: 'id'
},
// 过长裁剪长度,设置为 -1 代表不裁剪
ellipsisLength: {
type: Number,
default: 12
}
},
data () {
return {
innerValue: [],
// 已选择列表
selectedTable: {
pagination: false,
scroll: { y: 240 },
columns: [
{
...this.columns[0],
width: this.columns[0].widthRight || this.columns[0].width
},
{ title: '操作', dataIndex: 'action', align: 'center', width: 60, scopedSlots: { customRender: 'action' } }
],
dataSource: []
},
renderEllipsis: (value) => (<ellipsis length={this.ellipsisLength}>{value}</ellipsis>),
url: { list: this.listUrl },
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
options: [],
dataSourceMap: {},
showMoreQueryItems: false
}
},
computed: {
// 表头
innerColumns () {
const columns = cloneDeep(this.columns)
columns.forEach(column => {
// 给所有的列加上过长裁剪
if (this.ellipsisLength !== -1) {
const myCustomRender = column.customRender
column.customRender = (text, record, index) => {
let value = text
if (typeof myCustomRender === 'function') {
// noinspection JSVoidFunctionReturnValueUsed
value = myCustomRender(text, record, index)
}
if (typeof value === 'string') {
return this.renderEllipsis(value)
}
return value
}
}
})
return columns
}
},
watch: {
value: {
deep: true,
immediate: true,
handler (val) {
this.innerValue = cloneDeep(val)
this.selectedRowKeys = []
this.valueWatchHandler(val)
this.queryOptionsByValue(val)
}
},
dataSource: {
deep: true,
handler (val) {
this.emitOptions(val)
this.valueWatchHandler(this.innerValue)
}
},
selectedRowKeys: {
immediate: true,
deep: true,
handler (val) {
// update--begin--autor:scott-----date:20200927------for:选取职务名称出现全选 #1753-----
if (this.innerValue) {
this.innerValue.length = 0
}
// update--end--autor:scott-----date:20200927------for:选取职务名称出现全选 #1753-----
this.selectedTable.dataSource = val.map(key => {
for (const data of this.dataSource) {
if (data[this.rowKey] === key) {
pushIfNotExist(this.innerValue, data[this.valueKey])
return data
}
}
for (const data of this.selectedTable.dataSource) {
if (data[this.rowKey] === key) {
pushIfNotExist(this.innerValue, data[this.valueKey])
return data
}
}
console.warn('未找到选择的行信息,key' + key)
return {}
})
}
}
},
methods: {
/** 关闭弹窗 */
close () {
this.$emit('update:visible', false)
},
valueWatchHandler (val) {
val.forEach(item => {
this.dataSource.concat(this.selectedTable.dataSource).forEach(data => {
if (data[this.valueKey] === item) {
pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
}
})
})
},
queryOptionsByValue (value) {
if (!value || value.length === 0) {
return
}
// 判断options是否存在value,如果已存在数据就不再请求后台了
let notExist = false
for (const val of value) {
let find = false
for (const option of this.options) {
if (val === option.value) {
find = true
break
}
}
if (!find) {
notExist = true
break
}
}
if (!notExist) return
getAction(this.valueUrl || this.listUrl, {
// 这里最后加一个 , 的原因是无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
[this.valueKey]: value.join(',') + ',',
pageNo: 1,
pageSize: value.length
}).then((res) => {
if (res.success) {
let dataSource = res.result
if (!(dataSource instanceof Array)) {
dataSource = res.result.records
}
this.emitOptions(dataSource, (data) => {
pushIfNotExist(this.innerValue, data[this.valueKey])
pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
pushIfNotExist(this.selectedTable.dataSource, data, this.rowKey)
})
}
})
},
emitOptions (dataSource, callback) {
dataSource.forEach(data => {
const key = data[this.valueKey]
this.dataSourceMap[key] = data
pushIfNotExist(this.options, { label: data[this.displayKey || this.valueKey], value: key }, 'value')
// typeof callback === 'function' ? callback(data) : ''
if (typeof callback === 'function') {
callback(data)
}
})
this.$emit('options', this.options, this.dataSourceMap)
},
/** 完成选择 */
handleOk () {
const value = this.selectedTable.dataSource.map(data => data[this.valueKey])
this.$emit('input', value)
this.close()
},
/** 删除已选择的 */
handleDeleteSelected (record/* , index */) {
this.selectedRowKeys.splice(this.selectedRowKeys.indexOf(record[this.rowKey]), 1)
// update--begin--autor:wangshuai-----date:20200722------forJSelectBizComponent组件切换页数值问题------
this.selectedTable.dataSource.splice(this.selectedTable.dataSource.indexOf(record), 1)
this.innerValue.splice(this.innerValue.indexOf(record[this.valueKey]), 1)
console.log('this.selectedRowKeys:', this.selectedRowKeys)
console.log('this.selectedTable.dataSource:', this.selectedTable.dataSource)
// update--begin--autor:wangshuai-----date:20200722------forJSelectBizComponent组件切换页数值问题------
},
customRowFn (record) {
return {
on: {
click: () => {
const key = record[this.rowKey]
if (!this.multiple) {
this.selectedRowKeys = [key]
this.selectedTable.dataSource = [record]
} else {
const index = this.selectedRowKeys.indexOf(key)
if (index === -1) {
this.selectedRowKeys.push(key)
this.selectedTable.dataSource.push(record)
} else {
this.handleDeleteSelected(record, index)
}
}
}
}
}
}
}
}
</script>
<style lang="less" scoped>
.full-form-item {
display: flex;
margin-right: 0;
/deep/ .ant-form-item-control-wrapper {
flex: 1 1;
display: inline-block;
}
}
.j-inline-form {
/deep/ .ant-form-item {
margin-bottom: 12px;
}
/deep/ .ant-form-item-label {
line-height: 32px;
width: auto;
}
/deep/ .ant-form-item-control {
height: 32px;
line-height: 32px;
}
}
</style>
@@ -0,0 +1,50 @@
export default {
name: 'JSelectBizQueryItem',
props: {
queryParam: Object,
queryConfig: Array
},
data () {
return {}
},
methods: {
renderQueryItem () {
return this.queryConfig.map(queryItem => {
const { key, label, queryModel, placeholder, dictCode, props, customRender } = queryItem
const options = {
props: {},
on: {
pressEnter: () => this.$emit('pressEnter')
}
}
if (props != null) {
Object.assign(options.props, props)
}
if (placeholder === undefined) {
if (dictCode) {
options.props.placeholder = `请选择${label}`
} else {
options.props.placeholder = `请输入${label}`
}
} else {
options.props.placeholder = placeholder
}
let input
if (typeof customRender === 'function') {
input = customRender.call(this, { key, options, queryParam: this.queryParam })
} else if (dictCode) {
input = <j-dict-select-tag {...options} vModel={this.queryParam[key]} dictCode={dictCode} style="width:180px;"/>
} else if (queryModel && queryModel === 'eq') {
input = <a-input {...options} vModel={this.queryParam[key]}/>
} else {
input = <j-input {...options} vModel={this.queryParam[key]}/>
}
return <a-form-item key={key} label={label}>{input}</a-form-item>
})
}
},
render () {
return <span>{this.renderQueryItem()}</span>
}
}
@@ -0,0 +1,36 @@
# JSelectBizComponent
Jero 选择组件的公共可复用组件
## 引用方式
```js
import JSelectBizComponent from '@/src/components/jerobiz/JSelectBizComponent'
export default {
components: { JSelectBizComponent }
}
```
## 参数
### 配置参数
| 参数名 | 类型 | 必填 | 默认值 | 备注 |
|-----------------------|---------|------|--------------|--------------------------------------------------------------------------------------|
| rowKey | String | | "id" | 唯一标识的字段名 |
| value(v-model) | String | | "" | 默认选择的数据,多个用半角逗号分割 |
| name | String | | "" | 显示名字,例如选择用户就填写"用户" |
| listUrl | String | 是 | | 数据请求地址,必须是封装了分页的地址 |
| valueUrl | String | | "" | 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realname |
| displayKey | String | | null | 显示在标签上的字段 key ,不传则直接显示数据 |
| returnKeys | Array | | ['id', 'id'] | v-model 绑定的 keys,是个数组,默认使用第二项,当配置了 `returnId=true` 就返回第一项 |
| returnId | Boolean | | false | 返回ID,设为true后将返回配置的 `returnKeys` 中的第一项 |
| selectButtonText | String | | "选择" | 选择按钮的文字 |
| queryParamText | String | | null | 查询条件显示文字,不传则使用 `name` |
| columns | Array | 是 | | 列配置项,与antd的table的配置完全一致。列的第一项会被配置成右侧已选择的列表上 |
| columns[0].widthRight | String | | null | 仅列的第一项可以应用此配置,表示右侧已选择列表的宽度,建议 `70%`,不传则应用`width` |
| placeholder | String | | "请选择" | 占位符 |
| disabled | Boolean | | false | 是否禁用 |
| multiple | Boolean | | false | 是否可多选 |
| buttons | Boolean | | true | 是否显示"选择"按钮,如果不显示,可以直接点击文本框打开选择界面 |
@@ -0,0 +1,168 @@
<template>
<a-row class="j-select-biz-component-box" type="flex" :gutter="8">
<a-col class="left" :class="{'full': !buttons}">
<slot name="left">
<a-select
mode="multiple"
:placeholder="placeholder"
v-model="selectValue"
:options="selectOptions"
allowClear
:disabled="disabled"
:open="selectOpen"
style="width: 100%;"
@dropdownVisibleChange="handleDropdownVisibleChange"
@click.native="visible=(buttons || disabled ?visible:true)"
/>
</slot>
</a-col>
<a-col v-if="buttons" class="right">
<a-button type="primary" icon="search" :disabled="disabled" @click="visible=true">{{selectButtonText}}</a-button>
</a-col>
<j-select-biz-component-modal
v-model="selectValue"
:visible.sync="visible"
v-bind="modalProps"
@options="handleOptions"
/>
</a-row>
</template>
<script>
import JSelectBizComponentModal from './JSelectBizComponentModal'
export default {
name: 'JSelectBizComponent',
components: { JSelectBizComponentModal },
props: {
value: {
type: String,
default: ''
},
/** 是否返回 id,默认 false,返回 code */
returnId: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
},
// 是否支持多选,默认 true
multiple: {
type: Boolean,
default: true
},
// 是否显示按钮,默认 true
buttons: {
type: Boolean,
default: true
},
// 显示的 Key
displayKey: {
type: String,
default: null
},
// 返回的 key
returnKeys: {
type: Array,
default: () => ['id', 'id']
},
// 选择按钮文字
selectButtonText: {
type: String,
default: '选择'
}
},
data () {
return {
selectValue: [],
selectOptions: [],
dataSourceMap: {},
visible: false,
selectOpen: false
}
},
computed: {
valueKey () {
return this.returnId ? this.returnKeys[0] : this.returnKeys[1]
},
modalProps () {
return Object.assign({
valueKey: this.valueKey,
multiple: this.multiple,
returnKeys: this.returnKeys,
displayKey: this.displayKey || this.valueKey
}, this.$attrs)
}
},
watch: {
value: {
immediate: true,
handler (val) {
if (val) {
this.selectValue = val.split(',')
} else {
this.selectValue = []
}
}
},
selectValue: {
deep: true,
handler (val) {
const rows = val.map(key => this.dataSourceMap[key])
const data = val.join(',')
if (data !== this.value) {
this.$emit('select', rows)
this.$emit('input', data)
this.$emit('change', data)
}
}
}
},
methods: {
handleOptions (options, dataSourceMap) {
this.selectOptions = options
this.dataSourceMap = dataSourceMap
},
handleDropdownVisibleChange () {
// 解决antdv自己的bug —— open 设置为 false 了,点击后还是添加了 open 样式,导致点击事件失效
this.selectOpen = true
this.$nextTick(() => {
this.selectOpen = false
})
}
}
}
</script>
<style lang="less" scoped>
.j-select-biz-component-box {
@width: 82px;
.left {
//width: calc(100% - @width - 8px);
width: calc(100% - @width);
}
.right {
width: @width;
}
.full {
width: 100%;
}
/deep/ .ant-select-search__field {
display: none !important;
}
}
</style>
+181
View File
@@ -0,0 +1,181 @@
<template>
<div class="components-input-demo-presuffix">
<!---->
<a-input @click="openModal" :placeholder="$t('selectDepartment.clickSelectDepartment')" v-model="textVals" readOnly :disabled="disabled">
<a-icon slot="prefix" type="cluster" title="部门选择控件"/>
<a-icon v-if="storeVals" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<j-select-depart-modal
ref="innerDepartSelectModal"
:modal-width="modalWidth"
:multi="multi"
:rootOpened="rootOpened"
:depart-id="value"
:store="storeField"
:text="textField"
:treeOpera="treeOpera"
@ok="handleOK"
@initComp="initComp"/>
</div>
</template>
<script>
import JSelectDepartModal from './modal/JSelectDepartModal'
import { underLinetoHump } from '@/components/_util/StringUtil'
export default {
name: 'JSelectDepart',
components: {
JSelectDepartModal
},
props: {
modalWidth: {
type: Number,
default: 500,
required: false
},
multi: {
type: Boolean,
default: false,
required: false
},
rootOpened: {
type: Boolean,
default: true,
required: false
},
value: {
type: String,
required: false
},
disabled: {
type: Boolean,
required: false,
default: false
},
// 自定义返回字段,默认返回 id
customReturnField: {
type: String,
default: ''
},
backDepart: {
type: Boolean,
default: false,
required: false
},
// 存储字段 [key field]
store: {
type: String,
default: 'id',
required: false
},
// 显示字段 [label field]
text: {
type: String,
default: 'departName',
required: false
},
treeOpera: {
type: Boolean,
default: false,
required: false
}
},
data () {
return {
visible: false,
confirmLoading: false,
storeVals: '', // [key values]
textVals: '' // [label values]
}
},
computed: {
storeField () {
let field = this.customReturnField
if (!field) {
field = this.store
}
return underLinetoHump(field)
},
textField () {
return underLinetoHump(this.text)
}
},
mounted () {
this.storeVals = this.value
},
watch: {
value (val) {
this.storeVals = val
}
},
methods: {
initComp (textVals) {
this.textVals = textVals
},
// 返回选中的部门信息
backDepartInfo () {
if (this.backDepart === true) {
if (this.storeVals && this.storeVals.length > 0) {
const arr1 = this.storeVals.split(',')
const arr2 = this.textVals.split(',')
const info = []
for (let i = 0; i < arr1.length; i++) {
info.push({
value: arr1[i],
text: arr2[i]
})
}
this.$emit('back', info)
}
}
},
openModal () {
this.$refs.innerDepartSelectModal.show()
},
handleOK (rows) {
if (!rows && rows.length <= 0) {
this.textVals = ''
this.storeVals = ''
} else {
const arr1 = []
const arr2 = []
for (const dep of rows) {
arr1.push(dep[this.storeField])
arr2.push(dep[this.textField])
}
this.storeVals = arr1.join(',')
this.textVals = arr2.join(',')
}
this.$emit('change', this.storeVals)
this.backDepartInfo()
},
getDepartNames () {
return this.departNames
},
handleEmpty () {
this.handleOK('')
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style scoped>
.components-input-demo-presuffix .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.components-input-demo-presuffix .anticon-close-circle:hover {
color: #f5222d;
}
.components-input-demo-presuffix .anticon-close-circle:active {
color: #666;
}
</style>
@@ -0,0 +1,74 @@
<template>
<!-- 定义在这里的参数都是不可在外部覆盖的防止出现问题 -->
<j-select-biz-component
:value="value"
:ellipsisLength="25"
:listUrl="url.list"
:columns="columns"
v-on="$listeners"
v-bind="attrs"
/>
</template>
<script>
// import JDate from '@comp/jero/JDate'
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectMultiUser',
components: { /* JDate, */ JSelectBizComponent },
props: {
value: null, // any type
queryConfig: {
type: Array,
default: () => []
}
},
data () {
return {
url: { list: '/sys/user/page' },
columns: [
{ title: '用户账号', align: 'center', width: '25%', dataIndex: 'username' },
{ title: '用户姓名', align: 'center', width: '25%', widthRight: '70%', dataIndex: 'realname' },
{ title: '性别', align: 'center', dataIndex: 'sex_dictText' },
{ title: '电话', align: 'center', width: '20%', dataIndex: 'phone' },
{
title: '部门',
align: 'center',
dataIndex: 'orgCodeTxt'
}
],
// 定义在这里的参数都是可以在外部传递覆盖的,可以更灵活的定制化使用的组件
default: {
name: '用户',
width: 1200,
displayKey: 'realname',
returnKeys: ['id', 'username'],
queryParamText: '账号'
},
// 多条件查询配置
queryConfigDefault: [
{
key: 'realname',
label: '姓名'
},
{
key: 'sex',
label: '性别',
// 如果包含 dictCode,那么就会显示成下拉框
dictCode: 'sex'
}
]
}
},
computed: {
attrs () {
return Object.assign(this.default, this.$attrs, {
queryConfig: this.queryConfigDefault.concat(this.queryConfig)
})
}
}
}
</script>
<style lang="less" scoped></style>
+35
View File
@@ -0,0 +1,35 @@
<template>
<j-select-biz-component
:value="value"
name="角色"
displayKey="roleName"
:returnKeys="returnKeys"
:listUrl="url.list"
:columns="columns"
queryParamText="角色编码"
v-on="$listeners"
v-bind="$attrs"
/>
</template>
<script>
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectRole',
components: { JSelectBizComponent },
props: ['value'],
data () {
return {
returnKeys: ['id', 'roleCode'],
url: { list: '/sys/role/page' },
columns: [
{ title: '角色名称', dataIndex: 'roleName', align: 'center', width: 120 },
{ title: '角色编码', dataIndex: 'roleCode', align: 'center', width: 120 }
]
}
}
}
</script>
<style lang="less" scoped></style>
+146
View File
@@ -0,0 +1,146 @@
<template>
<div>
<a-input-search
v-model="textVals"
placeholder="请先选择用户"
readOnly
unselectable="on"
@search="onSearchDepUser">
<a-button slot="enterButton" :disabled="disabled">选择用户</a-button>
</a-input-search>
<j-select-user-by-dep-modal
ref="selectModal"
:modal-width="modalWidth"
:multi="multi"
@ok="selectOK"
:user-ids="value"
:store="storeField"
:text="textField"
@initComp="initComp"/>
</div>
</template>
<script>
import JSelectUserByDepModal from './modal/JSelectUserByDepModal'
import { underLinetoHump } from '@/components/_util/StringUtil'
export default {
name: 'JSelectUserByDep',
components: { JSelectUserByDepModal },
props: {
modalWidth: {
type: Number,
default: 1250,
required: false
},
value: {
type: String,
required: false
},
disabled: {
type: Boolean,
required: false,
default: false
},
multi: {
type: Boolean,
default: true,
required: false
},
backUser: {
type: Boolean,
default: false,
required: false
},
// 存储字段 [key field]
store: {
type: String,
default: 'username',
required: false
},
// 显示字段 [label field]
text: {
type: String,
default: 'realname',
required: false
}
},
data () {
return {
storeVals: '', // [key values]
textVals: '' // [label values]
}
},
computed: {
storeField () {
let field = this.customReturnField
if (!field) {
field = this.store
}
return underLinetoHump(field)
},
textField () {
return underLinetoHump(this.text)
}
},
mounted () {
this.storeVals = this.value
},
watch: {
value (val) {
this.storeVals = val
}
},
model: {
prop: 'value',
event: 'change'
},
methods: {
initComp (textVals) {
this.textVals = textVals
},
// 返回选中的用户信息
backUserInfo () {
if (this.backUser === true) {
if (this.storeVals && this.storeVals.length > 0) {
const arr1 = this.storeVals.split(',')
const arr2 = this.textVals.split(',')
const info = []
for (let i = 0; i < arr1.length; i++) {
info.push({
value: arr1[i],
text: arr2[i]
})
}
this.$emit('back', info)
}
}
},
onSearchDepUser () {
this.$refs.selectModal.showModal()
},
selectOK (rows) {
console.log('当前选中用户', rows)
if (!rows) {
this.storeVals = ''
this.textVals = ''
} else {
const temp1 = []
const temp2 = []
for (const item of rows) {
temp1.push(item[this.storeField])
temp2.push(item[this.textField])
}
this.storeVals = temp1.join(',')
this.textVals = temp2.join(',')
}
this.$emit('change', this.storeVals)
this.backUserInfo()
}
}
}
</script>
<style scoped>
</style>
+137
View File
@@ -0,0 +1,137 @@
# JSelectDepart 部门选择组件
选择部门组件,存储部门ID,显示部门名称
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| modalWidth |Number | | 弹框宽度 默认500 |
| multi |Boolean | | 是否多选 默认false |
| rootOpened |Boolean | | 是否展开根节点 默认true |
| disabled |Boolean | | 是否禁用 默认false|
使用示例
----
```vue
<template>
<a-form :form="form">
<a-form-item label="部门选择v-decorator" style="width: 300px">
<j-select-depart v-decorator="['bumen']"/>
{{ getFormFieldValue('bumen') }}
</a-form-item>
<a-form-item label="部门选择v-model" style="width: 300px">
<j-select-depart v-model="bumen"/>
{{ bumen }}
</a-form-item>
<a-form-item label="部门多选v-model" style="width: 300px">
<j-select-depart v-model="bumens" :multi="true"/>
{{ bumens }}
</a-form-item>
</a-form >
</template>
<script>
import JSelectDepart from '@/components/jerobiz/JSelectDepart'
export default {
components: {JSelectDepart},
data() {
return {
form: this.$form.createForm(this),
bumen:"",
bumens:""
}
},
methods:{
getFormFieldValue(field){
return this.form.getFieldValue(field)
}
}
}
</script>
```
# JSelectMultiUser 用户多选组件
使用示例
----
```vue
<template>
<a-form :form="form">
<a-form-item label="用户选择v-decorator" style="width: 500px">
<j-select-multi-user v-decorator="['users']"/>
{{ getFormFieldValue('users') }}
</a-form-item>
<a-form-item label="用户选择v-model" style="width: 500px">
<j-select-multi-user v-model="users" ></j-select-multi-user>
{{ users }}
</a-form-item>
</a-form >
</template>
<script>
import JSelectMultiUser from '@/components/jerobiz/JSelectMultiUser'
export default {
components: {JSelectMultiUser},
data() {
return {
form: this.$form.createForm(this),
users:"",
}
},
methods:{
getFormFieldValue(field){
return this.form.getFieldValue(field)
}
}
}
</script>
```
# JSelectUserByDep 根据部门选择用户
## 参数配置
| 参数 | 类型 | 必填 |说明|
|--------------|---------|----|---------|
| modalWidth |Number | | 弹框宽度 默认1250 |
| disabled |Boolean | | 是否禁用 |
使用示例
----
```vue
<template>
<a-form :form="form">
<a-form-item label="用户选择v-decorator" style="width: 500px">
<j-select-user-by-dep v-decorator="['users']"/>
{{ getFormFieldValue('users') }}
</a-form-item>
<a-form-item label="用户选择v-model" style="width: 500px">
<j-select-user-by-dep v-model="users" ></j-select-user-by-dep>
{{ users }}
</a-form-item>
</a-form >
</template>
<script>
import JSelectUserByDep from '@/components/jerobiz/JSelectUserByDep'
export default {
components: {JSelectUserByDep},
data() {
return {
form: this.$form.createForm(this),
users:"",
}
},
methods:{
getFormFieldValue(field){
return this.form.getFieldValue(field)
}
}
}
</script>
```
@@ -0,0 +1,305 @@
<template>
<j-modal
:title="$t('selectDepartment.selectDepartment')"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel"
@update:fullscreen="isFullscreen"
wrapClassName="j-depart-select-modal"
switchFullscreen
:cancelText="$t('close')">
<a-spin tip="Loading..." :spinning="false">
<a-input-search style="margin-bottom: 1px" :placeholder="$t('selectDepartment.enterDepartmentPressEnter')" @search="onSearch" />
<a-tree
:checkable="true"
:class="treeScreenClass"
:treeData="treeData"
:checkStrictly="checkStrictly"
@check="onCheck"
@select="onSelect"
@expand="onExpand"
:autoExpandParent="autoExpandParent"
:expandedKeys="expandedKeys"
:checkedKeys="checkedKeys">
<template slot="title" slot-scope="{title}">
<span v-if="title.indexOf(searchValue) > -1">
{{title.substr(0, title.indexOf(searchValue))}}
<span style="color: #f50">{{searchValue}}</span>
{{title.substr(title.indexOf(searchValue) + searchValue.length)}}
</span>
<span v-else>{{title}}</span>
</template>
</a-tree>
</a-spin>
<!--底部父子关联操作和确认取消按钮-->
<template slot="footer" v-if="treeOpera && multi">
<div class="drawer-bootom-button">
<a-dropdown style="float: left" :trigger="['click']" placement="topCenter">
<a-menu slot="overlay">
<a-menu-item key="1" @click="switchCheckStrictly(1)">{{ $t('parentChildConnection') }}</a-menu-item>
<a-menu-item key="2" @click="switchCheckStrictly(2)">{{ $t('cancelConnection') }}</a-menu-item>
</a-menu>
<a-button>
{{ $t('treeOperation') }} <a-icon type="up" />
</a-button>
</a-dropdown>
<a-button @click="handleCancel" type="primary" style="margin-right: 0.8rem">{{ $t('close') }}</a-button>
<a-button @click="handleSubmit" type="primary" >{{ $t('confirm') }}</a-button>
</div>
</template>
</j-modal>
</template>
<script>
import { queryDepartTreeList } from '@/api/api'
export default {
name: 'JSelectDepartModal',
props: ['modalWidth', 'multi', 'rootOpened', 'departId', 'store', 'text', 'treeOpera'],
data () {
return {
visible: false,
confirmLoading: false,
treeData: [],
autoExpandParent: true,
expandedKeys: [],
dataList: [],
checkedKeys: [],
checkedRows: [],
searchValue: '',
checkStrictly: true,
fullscreen: false
}
},
created () {
this.loadDepart()
},
watch: {
departId () {
this.initDepartComponent()
},
visible: {
handler () {
this.initDepartComponent(true)
}
}
},
computed: {
treeScreenClass () {
return {
'my-dept-select-tree': true,
fullscreen: this.fullscreen
}
}
},
methods: {
show () {
this.visible = true
this.checkedRows = []
this.checkedKeys = []
},
loadDepart () {
// 这个方法是找到所有的部门信息
queryDepartTreeList().then(res => {
if (res.success) {
const arr = [...res.result]
this.reWriterWithSlot(arr)
this.treeData = arr
this.initDepartComponent()
if (this.rootOpened) {
this.initExpandedKeys(res.result)
}
}
})
},
initDepartComponent (flag) {
const arr = []
// 该方法两个地方用 1.visible改变事件重新设置选中项 2.组件编辑页面回显
const fieldName = flag === true ? 'key' : this.text
if (this.departId) {
const arr2 = this.departId.split(',')
for (const item of this.dataList) {
if (arr2.indexOf(item[this.store]) >= 0) {
arr.push(item[fieldName])
}
}
}
if (flag === true) {
this.checkedKeys = [...arr]
} else {
this.$emit('initComp', arr.join(','))
}
},
reWriterWithSlot (arr) {
for (const item of arr) {
if (item.children && item.children.length > 0) {
this.reWriterWithSlot(item.children)
const temp = Object.assign({}, item)
temp.children = {}
this.dataList.push(temp)
} else {
this.dataList.push(item)
item.scopedSlots = { title: 'title' }
}
}
},
initExpandedKeys (arr) {
if (arr && arr.length > 0) {
const keys = []
for (const item of arr) {
if (item.children && item.children.length > 0) {
keys.push(item.id)
}
}
this.expandedKeys = [...keys]
} else {
this.expandedKeys = []
}
},
onCheck (checkedKeys, info) {
if (!this.multi) {
const arr = checkedKeys.checked.filter(item => this.checkedKeys.indexOf(item) < 0)
this.checkedKeys = [...arr]
this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef]
} else {
if (this.checkStrictly) {
this.checkedKeys = checkedKeys.checked
} else {
this.checkedKeys = checkedKeys
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
},
onSelect (selectedKeys, info) {
// 取消关联的情况下才走onSelect的逻辑
if (this.checkStrictly) {
const keys = []
keys.push(selectedKeys[0])
if (!this.checkedKeys || this.checkedKeys.length === 0 || !this.multi) {
this.checkedKeys = [...keys]
this.checkedRows = [info.node.dataRef]
} else {
const currKey = info.node.dataRef.key
if (this.checkedKeys.indexOf(currKey) >= 0) {
this.checkedKeys = this.checkedKeys.filter(item => item !== currKey)
} else {
this.checkedKeys.push(...keys)
}
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
},
onExpand (expandedKeys) {
this.expandedKeys = expandedKeys
this.autoExpandParent = false
},
handleSubmit () {
if (!this.checkedKeys || this.checkedKeys.length === 0) {
this.$emit('ok', '')
} else {
const checkRow = this.getCheckedRows(this.checkedKeys)
const keyStr = this.checkedKeys.join(',')
this.$emit('ok', checkRow, keyStr)
}
this.handleClear()
},
handleCancel () {
this.handleClear()
},
handleClear () {
this.visible = false
this.checkedKeys = []
},
getParentKey (currKey, treeData) {
let parentKey
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i]
if (node.children) {
if (node.children.some(item => item.key === currKey)) {
parentKey = node.key
} else if (this.getParentKey(currKey, node.children)) {
parentKey = this.getParentKey(currKey, node.children)
}
}
}
return parentKey
},
onSearch (value) {
const expandedKeys = this.dataList.map((item) => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.key, this.treeData)
}
return null
}).filter((item, i, self) => item && self.indexOf(item) === i)
Object.assign(this, {
expandedKeys,
searchValue: value,
autoExpandParent: true
})
},
// 根据 checkedKeys 获取 rows
getCheckedRows (checkedKeys) {
const forChildren = (list, key) => {
for (const item of list) {
if (item.id === key) {
return item
}
if (item.children instanceof Array) {
const value = forChildren(item.children, key)
if (value != null) {
return value
}
}
}
return null
}
const rows = []
for (const key of checkedKeys) {
const row = forChildren(this.treeData, key)
if (row != null) {
rows.push(row)
}
}
return rows
},
switchCheckStrictly (v) {
if (v === 1) {
this.checkStrictly = false
} else if (v === 2) {
this.checkStrictly = true
}
},
isFullscreen (val) {
this.fullscreen = val
}
}
}
</script>
<style lang="less" scoped>
// 限制部门选择树高度,避免部门太多时点击确定不便
.my-dept-select-tree{
height:350px;
&.fullscreen{
height: calc(100vh - 250px);
}
overflow-y: scroll;
}
.drawer-bootom-button {
position: absolute;
bottom: 0;
width: 100%;
border-top: 1px solid #e8e8e8;
padding: 10px 16px;
text-align: right;
left: 0;
background: #fff;
border-radius: 0 0 2px 2px;
}
</style>
@@ -0,0 +1,351 @@
<template>
<j-modal
:width="modalWidth"
:visible="visible"
:title="title"
switchFullscreen
wrapClassName="j-user-select-modal"
@ok="handleSubmit"
@cancel="close"
style="top:50px"
cancelText="关闭"
>
<a-row :gutter="10" style="background-color: #ececec; padding: 10px; margin: -10px">
<a-col :md="6" :sm="24">
<a-card :bordered="false">
<!--组织机构-->
<a-directory-tree
selectable
:selectedKeys="selectedDepIds"
:checkStrictly="true"
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="departTree"
:expandAction="false"
@select="onDepSelect"
:load-data="onLoadDepartment"
/>
</a-card>
</a-col>
<a-col :md="18" :sm="24">
<a-card :bordered="false">
<div :style="{marginBottom:'12px'}">
<label :style="{marginRight:'5px'}">用户账号:</label>
<j-input
:style="{width:'150px'}"
placeholder="请输入账号"
v-model="queryParam.username"></j-input>
<label :style="{marginRight:'5px', marginLeft: '16px'}">用户姓名:</label>
<j-input
:style="{width:'150px'}"
placeholder="请输入姓名"
v-model="queryParam.realname"></j-input>
<a-button type="primary" @click="onSearch(1)" style="margin-left: 20px" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset(1)" style="margin-left: 20px" icon="redo">重置</a-button>
</div>
<!--用户列表-->
<a-table
ref="table"
:scroll="scrollTrigger"
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
:loading="loading"
@change="handleTableChange">
</a-table>
</a-card>
</a-col>
</a-row>
</j-modal>
</template>
<script>
import { filterObj } from '@/utils/util'
import { getUserList, queryUserByDepId, queryDepartTreeSync } from '@/api/api'
import { getAction } from '@/api/manage'
export default {
name: 'JSelectUserByDepModal',
components: {},
props: ['modalWidth', 'multi', 'userIds', 'store', 'text'],
data () {
return {
queryParam: {
username: '',
realname: ''
},
columns: [
{
title: '用户账号',
align: 'center',
dataIndex: 'username'
},
{
title: '用户姓名',
align: 'center',
dataIndex: 'realname'
},
{
title: '性别',
align: 'center',
dataIndex: 'sex_dictText'
},
{
title: '电话',
align: 'center',
dataIndex: 'phone'
},
{
title: '部门',
align: 'center',
dataIndex: 'orgCodeTxt'
}
],
scrollTrigger: {},
dataSource: [],
selectedRowKeys: [],
selectUserRows: [],
selectUserIds: [],
title: '根据部门选择用户',
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter: {
column: 'createTime',
order: 'desc'
},
selectedDepIds: [],
departTree: [],
visible: false,
form: this.$form.createForm(this),
loading: false,
expandedKeys: []
}
},
computed: {
// 计算属性的 getter
getType: function () {
return this.multi === true ? 'checkbox' : 'radio'
}
},
watch: {
userIds: {
immediate: true,
handler () {
this.initUserNames()
}
}
},
created () {
// 该方法触发屏幕自适应
this.resetScreenSize()
this.loadData()
},
methods: {
initUserNames () {
if (this.userIds) {
// 这里最后加一个 , 的原因是无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
const values = this.userIds.split(',') + ','
const param = { [this.store]: values }
getAction('/sys/user/getMultiUser', param).then((list) => {
this.selectionRows = []
const selectedRowKeys = []
const textArray = []
if (list && list.length > 0) {
for (const user of list) {
textArray.push(user[this.text])
selectedRowKeys.push(user.id)
this.selectionRows.push(user)
}
}
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', textArray.join(','))
})
} else {
// JSelectUserByDep组件bug issues/I16634
this.$emit('initComp', '')
// 前端用户选择单选无法置空的问题 #2610
this.selectedRowKeys = []
}
},
async loadData (arg) {
if (arg === 1) {
this.ipagination.current = 1
}
if (this.selectedDepIds && this.selectedDepIds.length > 0) {
await this.initQueryUserByDepId(this.selectedDepIds)
} else {
this.loading = true
const params = this.getQueryParams()// 查询条件
await getUserList(params).then((res) => {
if (res.success) {
this.dataSource = res.result.records
this.ipagination.total = res.result.total
}
}).finally(() => {
this.loading = false
})
}
},
// 触发屏幕自适应
resetScreenSize () {
const screenWidth = document.body.clientWidth
if (screenWidth < 500) {
this.scrollTrigger = { x: 800 }
} else {
this.scrollTrigger = {}
}
},
showModal () {
this.visible = true
this.queryDepartTree()
this.initUserNames()
this.loadData()
this.form.resetFields()
},
getQueryParams () {
const param = Object.assign({}, this.queryParam, this.isorter)
param.field = this.getQueryField()
param.pageNo = this.ipagination.current
param.pageSize = this.ipagination.pageSize
return filterObj(param)
},
getQueryField () {
let str = 'id,'
for (let a = 0; a < this.columns.length; a++) {
str += ',' + this.columns[a].dataIndex
}
return str
},
searchReset (num) {
const that = this
if (num !== 0) {
that.queryParam = {}
that.loadData(1)
}
that.selectedRowKeys = []
that.selectUserIds = []
that.selectedDepIds = []
},
close () {
this.searchReset(0)
this.visible = false
},
handleTableChange (pagination, filters, sorter) {
// TODO 筛选
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.field
this.isorter.order = sorter.order === 'ascend' ? 'asc' : 'desc'
}
this.ipagination = pagination
this.loadData()
},
handleSubmit () {
const that = this
this.getSelectUserRows()
that.$emit('ok', that.selectUserRows)
that.searchReset(0)
that.close()
},
// 获取选择用户信息
getSelectUserRows (/* rowId */) {
const dataSource = this.dataSource
let userIds = ''
this.selectUserRows = []
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectUserRows.push(dataSource[i])
userIds = userIds + ',' + dataSource[i].username
}
}
this.selectUserIds = userIds.substring(1)
},
// 点击树节点,筛选出对应的用户
onDepSelect (selectedDepIds) {
if (selectedDepIds[0] != null) {
this.initQueryUserByDepId(selectedDepIds) // 调用方法根据选选择的id查询用户信息
if (this.selectedDepIds[0] !== selectedDepIds[0]) {
this.selectedDepIds = [selectedDepIds[0]]
}
}
},
onSelectChange (selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys
this.selectionRows = selectionRows
},
onSearch () {
this.loadData(1)
},
// 根据选择的id来查询用户信息
initQueryUserByDepId (selectedDepIds) {
this.loading = true
const params = this.getQueryParams()
return queryUserByDepId({ id: selectedDepIds.toString(), ...params }).then((res) => {
if (res.success) {
this.dataSource = res.result
this.ipagination.total = res.result.length
}
}).finally(() => {
this.loading = false
})
},
queryDepartTree () {
// update-begin-author:taoyan date:20211202 for: 异步加载部门树 https://github.com/jeecgboot/jeecg-boot/issues/3196
this.expandedKeys = []
this.departTree = []
queryDepartTreeSync().then((res) => {
if (res.success) {
for (let i = 0; i < res.result.length; i++) {
const temp = res.result[i]
this.departTree.push(temp)
}
}
})
},
onLoadDepartment (treeNode) {
return new Promise(resolve => {
queryDepartTreeSync({ pid: treeNode.dataRef.id }).then((res) => {
if (res.success) {
// 判断chidlren是否为空,并修改isLeaf属性值
if (res.result.length === 0) {
treeNode.dataRef.isLeaf = true
} else {
treeNode.dataRef.children = res.result
}
}
})
resolve()
})
},
// update-end-author:taoyan date:20211202 for: 异步加载部门树 https://github.com/jeecgboot/jeecg-boot/issues/3196
modalFormOk () {
this.loadData()
}
}
}
</script>
<style scoped>
.ant-table-tbody .ant-table-row td {
padding-top: 10px;
padding-bottom: 10px;
}
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
}
</style>
@@ -0,0 +1,121 @@
<template>
<a-modal
title="用户列表"
:width="1000"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel">
<a-table
ref="table"
bordered
size="middle"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"></a-table>
</a-modal>
</template>
<script>
import { getUserList } from '@/api/api'
import { JeroListMixin } from '@/mixins/JeroListMixin'
export default {
name: 'SelectUserListModal',
mixins: [JeroListMixin],
data () {
return {
title: '操作',
visible: false,
model: {},
confirmLoading: false,
url: {
add: '/act/model/create',
list: '/sys/user/page'
},
columns: [
{
title: '用户账号',
align: 'center',
dataIndex: 'username',
fixed: 'left',
width: 200
},
{
title: '用户姓名',
align: 'center',
dataIndex: 'realname'
},
{
title: '性别',
align: 'center',
dataIndex: 'sex_dictText'
},
{
title: '手机号码',
align: 'center',
dataIndex: 'phone'
},
{
title: '邮箱',
align: 'center',
dataIndex: 'email'
},
{
title: '状态',
align: 'center',
dataIndex: 'status_dictText'
}
]
}
},
created () {
// Step.2 加载用户数据
getUserList().then((res) => {
if (res.success) {
this.dataSource = res.result.records
this.ipagination.total = res.result.total
}
})
},
methods: {
open () {
this.visible = true
// Step.1 清空选中用户
this.selectedRowKeys = []
this.selectedRows = []
},
close () {
this.$emit('close')
this.visible = false
},
handleChange (info) {
const file = info.file
if (file.response.success) {
this.$message.success(file.response.message)
this.$emit('ok')
this.close()
} else {
this.$message.warn(file.response.message)
this.close()
}
},
handleCancel () {
this.close()
},
handleSubmit () {
this.$emit('ok', this.selectionRows)
this.close()
}
}
}
</script>
<style>
</style>
@@ -0,0 +1,214 @@
<template>
<span v-if="syncToApp || syncToLocal">
<j-third-app-dropdown v-if="enabledTypes.wechatEnterprise" type="wechatEnterprise" name="企微" v-bind="bindAttrs" v-on="bindEvents"/>
<j-third-app-dropdown v-if="enabledTypes.dingtalk" type="dingtalk" name="钉钉" v-bind="bindAttrs" v-on="bindEvents"/>
</span>
<span v-else>未设置任何同步方向</span>
</template>
<script>
import { getAction } from '@/api/manage'
import { cloneObject } from '@/utils/util'
import JThirdAppDropdown from './JThirdAppDropdown'
const backEndUrl = {
// 获取启用的第三方App
getEnabledType: '/sys/thirdApp/getEnabledType',
// 企业微信
wechatEnterprise: {
user: '/sys/thirdApp/sync/wechatEnterprise/user',
depart: '/sys/thirdApp/sync/wechatEnterprise/depart'
},
// 钉钉
dingtalk: {
user: '/sys/thirdApp/sync/dingtalk/user',
depart: '/sys/thirdApp/sync/dingtalk/depart'
}
}
export default {
name: 'JThirdAppButton',
components: { JThirdAppDropdown },
props: {
// 同步类型,可以是 user、depart
bizType: {
type: String,
required: true
},
// 是否允许同步到第三方APP
syncToApp: Boolean,
// 是否允许第三方APP同步到本地
syncToLocal: Boolean,
// 选择的行
selectedRowKeys: Array
},
data () {
return {
enabledTypes: {},
attrs: {
dingtalk: {}
}
}
},
computed: {
bindAttrs () {
return {
syncToApp: this.syncToApp,
syncToLocal: this.syncToLocal
}
},
bindEvents () {
return {
'to-app': this.onToApp,
'to-local': this.onToLocal
}
}
},
created () {
this.loadEnabledTypes()
},
methods: {
handleMenuClick () {
console.log(arguments)
},
onToApp (e) {
this.doSync(e.type, '/toApp')
},
onToLocal (e) {
this.doSync(e.type, '/toLocal')
},
// 获取启用的第三方App
async loadEnabledTypes () {
this.enabledTypes = await loadEnabledTypes()
},
// 开始同步第三方App
doSync (type, direction) {
const urls = backEndUrl[type]
if (!(urls && urls[this.bizType])) {
console.warn('配置出错')
return
}
const url = urls[this.bizType] + direction
let selectedRowKeys = this.selectedRowKeys
let content = '确定要开始同步全部数据吗?可能花费较长时间!'
if (Array.isArray(selectedRowKeys) && selectedRowKeys.length > 0) {
content = `确定要开始同步这 ${selectedRowKeys.length} 项吗?`
} else {
selectedRowKeys = []
}
return new Promise((resolve/* , reject */) => {
const model = this.$confirm({
title: '同步',
content,
onOk: () => {
model.update({
keyboard: false,
okText: '同步中…',
cancelButtonProps: { props: { disabled: true } }
})
return getAction(url, {
ids: selectedRowKeys.join(',')
}).then(res => {
let options = null
if (res.result) {
options = {
width: 600,
title: res.message,
content: (h) => {
let nodes
const successInfo = [
`成功信息如下:`,
this.renderTextarea(h, res.result.successInfo.map((v, i) => `${i + 1}. ${v}`).join('\n'))
]
if (res.success) {
nodes = [
...successInfo,
h('br'),
`无失败信息!`
]
} else {
nodes = [
`失败信息如下:`,
this.renderTextarea(h, res.result.failInfo.map((v, i) => `${i + 1}. ${v}`).join('\n')),
h('br'),
...successInfo
]
}
return nodes
}
}
}
if (res.success) {
if (options != null) {
this.$success(options)
} else {
this.$message.success(res.message)
}
this.$emit('sync-ok')
} else {
if (options != null) {
this.$warning(options)
} else {
this.$message.warning(res.message)
}
this.$emit('sync-error')
}
}).catch(() => model.destroy()).finally(() => {
resolve()
this.$emit('sync-finally', {
type,
direction,
isToApp: direction === '/toApp',
isToLocal: direction === '/toLocal'
})
})
},
onCancel () {
resolve()
}
})
})
},
renderTextarea (h, value) {
return h('a-textarea', {
props: {
value: value,
readOnly: true,
autosize: { minRows: 5, maxRows: 10 }
},
style: {
// 关闭textarea的自动换行,使其可以左右滚动
whiteSpace: 'pre',
overflow: 'auto'
}
})
}
}
}
// 启用了哪些第三方App(在此缓存)
let enabledTypes = null
// 获取启用的第三方App
export async function loadEnabledTypes () {
// 获取缓存
if (enabledTypes != null) {
return cloneObject(enabledTypes)
} else {
const { success, result } = await getAction(backEndUrl.getEnabledType)
if (success) {
// 在此缓存
enabledTypes = cloneObject(result)
return result
} else {
console.warn('getEnabledType查询失败:', result)
}
}
return {}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,34 @@
<template>
<a-dropdown v-if="syncToApp && syncToLocal">
<a-button type="primary" icon="sync">同步{{name}}</a-button>
<a-menu slot="overlay" @click="handleMenuClick">
<a-menu-item v-if="syncToApp" key="to-app">同步到{{name}}</a-menu-item>
<a-menu-item v-if="syncToLocal" key="to-local">同步到本地</a-menu-item>
</a-menu>
</a-dropdown>
<a-button v-else-if="syncToApp" type="primary" icon="sync" @click="handleMenuClick({key:'to-app'})">同步{{name}}</a-button>
<a-button v-else type="primary" icon="sync" @click="handleMenuClick({key:'to-local'})">同步{{name}}到本地</a-button>
</template>
<script>
/* JThirdAppButton 的子组件,不可单独使用 */
export default {
name: 'JThirdAppDropdown',
props: {
type: String,
name: String,
syncToApp: Boolean,
syncToLocal: Boolean
},
methods: {
handleMenuClick (event) {
this.$emit(event.key, { type: this.type })
}
}
}
</script>
<style scoped>
</style>