Merge remote-tracking branch 'origin/dev' into dev
commit
815b8e956b
|
@ -366,14 +366,17 @@ CAPTCHA_CHALLENGE_FUNCT = "captcha.helpers.math_challenge" # 加减乘除验证
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
API_LOG_ENABLE = True
|
# 是否启动API日志记录
|
||||||
|
API_LOG_ENABLE = locals().get("API_LOG_ENABLE", True)
|
||||||
|
# API 日志记录的请求方式
|
||||||
|
API_LOG_METHODS = locals().get("API_LOG_METHODS", ["POST", "UPDATE", "DELETE", "PUT"])
|
||||||
# API_LOG_METHODS = 'ALL' # ['POST', 'DELETE']
|
# API_LOG_METHODS = 'ALL' # ['POST', 'DELETE']
|
||||||
API_LOG_METHODS = ["POST", "UPDATE", "DELETE", "PUT"] # ['POST', 'DELETE']
|
# 在操作日志中详细记录的请求模块映射
|
||||||
API_MODEL_MAP = {
|
API_MODEL_MAP = locals().get("API_MODEL_MAP", {
|
||||||
"/token/": "登录模块",
|
"/token/": "登录模块",
|
||||||
"/api/login/": "登录模块",
|
"/api/login/": "登录模块",
|
||||||
"/api/plugins_market/plugins/": "插件市场",
|
"/api/logout/": "登录模块",
|
||||||
}
|
})
|
||||||
|
|
||||||
DJANGO_CELERY_BEAT_TZ_AWARE = False
|
DJANGO_CELERY_BEAT_TZ_AWARE = False
|
||||||
CELERY_TIMEZONE = "Asia/Shanghai" # celery 时区问题
|
CELERY_TIMEZONE = "Asia/Shanghai" # celery 时区问题
|
||||||
|
|
|
@ -39,6 +39,11 @@ DEBUG = True
|
||||||
ENABLE_LOGIN_ANALYSIS_LOG = True
|
ENABLE_LOGIN_ANALYSIS_LOG = True
|
||||||
# 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
|
# 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
|
||||||
LOGIN_NO_CAPTCHA_AUTH = True
|
LOGIN_NO_CAPTCHA_AUTH = True
|
||||||
|
# 是否启动API日志记录
|
||||||
|
API_LOG_ENABLE = locals().get("API_LOG_ENABLE", True)
|
||||||
|
# API 日志记录的请求方式
|
||||||
|
API_LOG_METHODS = locals().get("API_LOG_METHODS", ["POST", "UPDATE", "DELETE", "PUT"])
|
||||||
|
# API_LOG_METHODS = 'ALL' # ['POST', 'DELETE']
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ****************** 其他 配置 ******************* #
|
# ****************** 其他 配置 ******************* #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
|
|
|
@ -21,18 +21,21 @@ class FileSerializer(CustomModelSerializer):
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
file_engine = dispatch.get_system_config_values("fileStorageConfig.file_engine")
|
file_engine = dispatch.get_system_config_values("fileStorageConfig.file_engine") or 'local'
|
||||||
file_backup = dispatch.get_system_config_values("fileStorageConfig.file_backup")
|
file_backup = dispatch.get_system_config_values("fileStorageConfig.file_backup")
|
||||||
file = self.initial_data.get('file')
|
file = self.initial_data.get('file')
|
||||||
file_size = file.size
|
file_size = file.size
|
||||||
validated_data['name'] = file.name
|
validated_data['name'] = file.name
|
||||||
validated_data['size'] = file_size
|
validated_data['size'] = file_size
|
||||||
validated_data['md5sum'] = hashlib.md5().hexdigest()
|
md5 = hashlib.md5()
|
||||||
|
for chunk in file.chunks():
|
||||||
|
md5.update(chunk)
|
||||||
|
validated_data['md5sum'] = md5.hexdigest()
|
||||||
validated_data['engine'] = file_engine
|
validated_data['engine'] = file_engine
|
||||||
validated_data['mime_type'] = file.content_type
|
validated_data['mime_type'] = file.content_type
|
||||||
if file_backup:
|
if file_backup:
|
||||||
validated_data['url'] = file
|
validated_data['url'] = file
|
||||||
if file_engine =='oss':
|
if file_engine == 'oss':
|
||||||
from dvadmin_cloud_storage.views.aliyun import ali_oss_upload
|
from dvadmin_cloud_storage.views.aliyun import ali_oss_upload
|
||||||
file_path = ali_oss_upload(file)
|
file_path = ali_oss_upload(file)
|
||||||
if file_path:
|
if file_path:
|
||||||
|
|
|
@ -21,9 +21,9 @@ def import_to_data(file_url, field_data, m2m_fields=None):
|
||||||
file_path_dir = os.path.join(settings.BASE_DIR, file_url)
|
file_path_dir = os.path.join(settings.BASE_DIR, file_url)
|
||||||
workbook = openpyxl.load_workbook(file_path_dir)
|
workbook = openpyxl.load_workbook(file_path_dir)
|
||||||
table = workbook[workbook.sheetnames[0]]
|
table = workbook[workbook.sheetnames[0]]
|
||||||
theader = tuple(table.values)[0] #Excel的表头
|
theader = tuple(table.values)[0] # Excel的表头
|
||||||
is_update = '更新主键(勿改)' in theader #是否导入更新
|
is_update = '更新主键(勿改)' in theader # 是否导入更新
|
||||||
if is_update is False: #不是更新时,删除id列
|
if is_update is False: # 不是更新时,删除id列
|
||||||
field_data.pop('id')
|
field_data.pop('id')
|
||||||
# 获取参数映射
|
# 获取参数映射
|
||||||
validation_data_dict = {}
|
validation_data_dict = {}
|
||||||
|
@ -35,9 +35,10 @@ def import_to_data(file_url, field_data, m2m_fields=None):
|
||||||
for k, v in choices.get("data").items():
|
for k, v in choices.get("data").items():
|
||||||
data_dict[k] = v
|
data_dict[k] = v
|
||||||
elif choices.get("queryset") and choices.get("values_name"):
|
elif choices.get("queryset") and choices.get("values_name"):
|
||||||
data_list = choices.get("queryset").values(choices.get("values_name"), "id")
|
data_list = choices.get("queryset").values(choices.get("values_name"),
|
||||||
|
choices.get("values_value", "id"))
|
||||||
for ele in data_list:
|
for ele in data_list:
|
||||||
data_dict[ele.get(choices.get("values_name"))] = ele.get("id")
|
data_dict[ele.get(choices.get("values_name"))] = ele.get(choices.get("values_value", "id"))
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
validation_data_dict[key] = data_dict
|
validation_data_dict[key] = data_dict
|
||||||
|
@ -53,12 +54,11 @@ def import_to_data(file_url, field_data, m2m_fields=None):
|
||||||
values = items[1]
|
values = items[1]
|
||||||
value_type = 'str'
|
value_type = 'str'
|
||||||
if isinstance(values, dict):
|
if isinstance(values, dict):
|
||||||
value_type = values.get('type','str')
|
value_type = values.get('type', 'str')
|
||||||
cell_value = table.cell(row=row + 1, column=index + 2).value
|
cell_value = table.cell(row=row + 1, column=index + 2).value
|
||||||
if cell_value is None or cell_value=='':
|
if cell_value is None or cell_value == '':
|
||||||
continue
|
continue
|
||||||
elif value_type == 'date':
|
elif value_type == 'date':
|
||||||
print(61, datetime.strptime(str(cell_value), '%Y-%m-%d %H:%M:%S').date())
|
|
||||||
try:
|
try:
|
||||||
cell_value = datetime.strptime(str(cell_value), '%Y-%m-%d %H:%M:%S').date()
|
cell_value = datetime.strptime(str(cell_value), '%Y-%m-%d %H:%M:%S').date()
|
||||||
except:
|
except:
|
||||||
|
@ -66,7 +66,7 @@ def import_to_data(file_url, field_data, m2m_fields=None):
|
||||||
elif value_type == 'datetime':
|
elif value_type == 'datetime':
|
||||||
cell_value = datetime.strptime(str(cell_value), '%Y-%m-%d %H:%M:%S')
|
cell_value = datetime.strptime(str(cell_value), '%Y-%m-%d %H:%M:%S')
|
||||||
else:
|
else:
|
||||||
# 由于excel导入数字类型后,会出现数字加 .0 的,进行处理
|
# 由于excel导入数字类型后,会出现数字加 .0 的,进行处理
|
||||||
if type(cell_value) is float and str(cell_value).split(".")[1] == "0":
|
if type(cell_value) is float and str(cell_value).split(".")[1] == "0":
|
||||||
cell_value = int(str(cell_value).split(".")[0])
|
cell_value = int(str(cell_value).split(".")[0])
|
||||||
elif type(cell_value) is str:
|
elif type(cell_value) is str:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from types import FunctionType, MethodType
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
@ -68,6 +69,8 @@ class ImportSerializerMixin:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
assert self.import_field_dict, "'%s' 请配置对应的导出模板字段。" % self.__class__.__name__
|
assert self.import_field_dict, "'%s' 请配置对应的导出模板字段。" % self.__class__.__name__
|
||||||
|
if isinstance(self.import_field_dict, MethodType) or isinstance(self.import_field_dict, FunctionType):
|
||||||
|
self.import_field_dict = self.import_field_dict()
|
||||||
# 导出模板
|
# 导出模板
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
# 示例数据
|
# 示例数据
|
||||||
|
@ -160,6 +163,8 @@ class ImportSerializerMixin:
|
||||||
assert self.import_field_dict, "'%s' 请配置对应的导入模板字段。" % self.__class__.__name__
|
assert self.import_field_dict, "'%s' 请配置对应的导入模板字段。" % self.__class__.__name__
|
||||||
assert self.import_serializer_class, "'%s' 请配置对应的导入序列化器。" % self.__class__.__name__
|
assert self.import_serializer_class, "'%s' 请配置对应的导入序列化器。" % self.__class__.__name__
|
||||||
data = self.import_serializer_class(queryset, many=True, request=request).data
|
data = self.import_serializer_class(queryset, many=True, request=request).data
|
||||||
|
if isinstance(self.import_field_dict, MethodType) or isinstance(self.import_field_dict, FunctionType):
|
||||||
|
self.import_field_dict = self.import_field_dict()
|
||||||
# 导出excel 表
|
# 导出excel 表
|
||||||
response = HttpResponse(content_type="application/msexcel")
|
response = HttpResponse(content_type="application/msexcel")
|
||||||
response["Access-Control-Expose-Headers"] = f"Content-Disposition"
|
response["Access-Control-Expose-Headers"] = f"Content-Disposition"
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"vue": "2.7.14",
|
"vue": "2.7.14",
|
||||||
"vue-echarts": "^6.5.4",
|
"vue-echarts": "^6.5.4",
|
||||||
"vue-grid-layout": "^2.4.0",
|
"vue-grid-layout": "^2.4.0",
|
||||||
|
"vue-html2pdf": "^1.8.0",
|
||||||
"vue-i18n": "^8.15.1",
|
"vue-i18n": "^8.15.1",
|
||||||
"vue-infinite-scroll": "^2.0.2",
|
"vue-infinite-scroll": "^2.0.2",
|
||||||
"vue-router": "^3.6.5",
|
"vue-router": "^3.6.5",
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,176 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div style="position: absolute;z-index: 10000">
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
round
|
||||||
|
size="small"
|
||||||
|
v-if="downloadButtonShow"
|
||||||
|
:style="downloadButtonStyle"
|
||||||
|
@click="generateReport"
|
||||||
|
:loading="downloadLoading"
|
||||||
|
>{{ downloadButtonTitle }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
round
|
||||||
|
size="small"
|
||||||
|
v-if="previewButtonShow"
|
||||||
|
style="position: fixed; right: 50px; bottom: 70px"
|
||||||
|
@click="previewPdf"
|
||||||
|
:loading="previewLoading"
|
||||||
|
>{{ previewButtonTitle }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<vue-html2pdf
|
||||||
|
:show-layout="true"
|
||||||
|
:float-layout="false"
|
||||||
|
:enable-download="false"
|
||||||
|
:preview-modal="preview"
|
||||||
|
:filename="filename"
|
||||||
|
:paginate-elements-by-height="1100"
|
||||||
|
:pdf-quality="2"
|
||||||
|
pdf-format="a4"
|
||||||
|
pdf-orientation="portrait"
|
||||||
|
pdf-content-width="100%"
|
||||||
|
:manual-pagination="true"
|
||||||
|
:html-to-pdf-options="{ margin: [5, 5, 0, 5] }"
|
||||||
|
@beforeDownload="beforeDownload($event)"
|
||||||
|
ref="html2Pdf"
|
||||||
|
>
|
||||||
|
<section slot="pdf-content">
|
||||||
|
<slot></slot>
|
||||||
|
</section>
|
||||||
|
</vue-html2pdf>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SongtiSCBlack from '@/assets/fonts/SongtiSCBlack'
|
||||||
|
import VueHtml2pdf from 'vue-html2pdf'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'dvaHtml2pdf',
|
||||||
|
components: {
|
||||||
|
VueHtml2pdf
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
filename: { // 导出pdf文件名称
|
||||||
|
type: String,
|
||||||
|
require: true
|
||||||
|
},
|
||||||
|
company: { // 企业名称
|
||||||
|
type: String,
|
||||||
|
default: 'xxx '
|
||||||
|
},
|
||||||
|
// 是否显示下载按钮
|
||||||
|
downloadButtonShow: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 下载按钮样式
|
||||||
|
downloadButtonStyle: {
|
||||||
|
type: Object,
|
||||||
|
default () {
|
||||||
|
return {
|
||||||
|
position: 'fixed', right: '50px', bottom: '30px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 下载按钮标题
|
||||||
|
downloadButtonTitle: {
|
||||||
|
type: String,
|
||||||
|
default: '下载报告'
|
||||||
|
},
|
||||||
|
// 是否显示预览按钮
|
||||||
|
previewButtonShow: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 预览按钮样式
|
||||||
|
previewButtonStyle: {
|
||||||
|
type: Object,
|
||||||
|
default () {
|
||||||
|
return {
|
||||||
|
position: 'fixed', right: '50px', bottom: '70px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 预览按钮标题
|
||||||
|
previewButtonTitle: {
|
||||||
|
type: String,
|
||||||
|
default: '预览报告'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
preview: false,
|
||||||
|
downloadLoading: false,
|
||||||
|
previewLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async beforeDownload ({ html2pdf, options, pdfContent }) {
|
||||||
|
if (this.preview) return
|
||||||
|
await html2pdf()
|
||||||
|
.set(options)
|
||||||
|
.from(pdfContent)
|
||||||
|
.toPdf()
|
||||||
|
.get('pdf')
|
||||||
|
.then((pdf) => {
|
||||||
|
const totalPages = pdf.internal.getNumberOfPages()
|
||||||
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
pdf.setPage(i)
|
||||||
|
pdf.addFileToVFS('MyFont.ttf', SongtiSCBlack)
|
||||||
|
pdf.addFont('MyFont.ttf', 'MyFont', 'normal')
|
||||||
|
pdf.setFont('MyFont')
|
||||||
|
pdf.setFontSize(10)
|
||||||
|
pdf.setTextColor(150)
|
||||||
|
pdf.text(
|
||||||
|
'第 ' + i + '页 共 ' + totalPages + '页',
|
||||||
|
pdf.internal.pageSize.getWidth() * 0.45,
|
||||||
|
pdf.internal.pageSize.getHeight() - 2
|
||||||
|
)
|
||||||
|
pdf.text(
|
||||||
|
this.company,
|
||||||
|
pdf.internal.pageSize.getWidth() * 0.79,
|
||||||
|
pdf.internal.pageSize.getHeight() - 2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.save(this.filename)
|
||||||
|
},
|
||||||
|
generateReport () {
|
||||||
|
this.preview = false
|
||||||
|
this.downloadLoading = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.html2Pdf.generatePdf()
|
||||||
|
const _this = this
|
||||||
|
setTimeout(function () {
|
||||||
|
_this.downloadLoading = false
|
||||||
|
}, 4000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
previewPdf () {
|
||||||
|
this.preview = true
|
||||||
|
this.previewLoading = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.html2Pdf.generatePdf()
|
||||||
|
const _this = this
|
||||||
|
setTimeout(function () {
|
||||||
|
_this.previewLoading = false
|
||||||
|
}, 4000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,7 +1,7 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
|
||||||
import d2Container from './d2-container'
|
import d2Container from './d2-container'
|
||||||
|
import tableProgress from './table-progress/lib/table-progress.vue'
|
||||||
// 注意 有些组件使用异步加载会有影响
|
// 注意 有些组件使用异步加载会有影响
|
||||||
Vue.component('d2-container', d2Container)
|
Vue.component('d2-container', d2Container)
|
||||||
Vue.component('d2-icon', () => import('./d2-icon'))
|
Vue.component('d2-icon', () => import('./d2-icon'))
|
||||||
|
@ -11,3 +11,5 @@ Vue.component('foreignKey', () => import('./foreign-key/index.vue'))
|
||||||
Vue.component('manyToMany', () => import('./many-to-many/index.vue'))
|
Vue.component('manyToMany', () => import('./many-to-many/index.vue'))
|
||||||
Vue.component('d2p-tree-selector', () => import('./tree-selector/lib/tree-selector.vue'))
|
Vue.component('d2p-tree-selector', () => import('./tree-selector/lib/tree-selector.vue'))
|
||||||
Vue.component('dept-format', () => import('./dept-format/lib/dept-format.vue'))
|
Vue.component('dept-format', () => import('./dept-format/lib/dept-format.vue'))
|
||||||
|
Vue.component('dvaHtml2pdf', () => import('./dvaHtml2pdf/index.vue'))
|
||||||
|
Vue.component('table-progress', tableProgress)
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* @创建文件时间: 2021-08-02 23:55:30
|
||||||
|
* @Auther: 猿小天
|
||||||
|
* @最后修改人: 猿小天
|
||||||
|
* @最后修改时间: 2021-08-08 12:27:45
|
||||||
|
* 联系Qq:1638245306
|
||||||
|
* @文件介绍:
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
// 字段类型配置,注册之后即可在crud.js中使用了
|
||||||
|
'selector-table': {
|
||||||
|
// 表单组件配置
|
||||||
|
form: { component: { name: 'selector-table-input', props: { color: 'danger' } } },
|
||||||
|
// 行组件配置
|
||||||
|
component: { name: 'values-format', props: {} },
|
||||||
|
// 行展示时居中
|
||||||
|
align: 'center'
|
||||||
|
// 您还可以写更多默认配置
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { d2CrudPlus } from 'd2-crud-plus'
|
||||||
|
import group from './group'
|
||||||
|
|
||||||
|
function install (Vue, options) {
|
||||||
|
Vue.component('selector-table-input', () => import('./selector-table'))
|
||||||
|
if (d2CrudPlus != null) {
|
||||||
|
// 注册字段类型`demo-extend`
|
||||||
|
d2CrudPlus.util.columnResolve.addTypes(group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 导出install
|
||||||
|
export default {
|
||||||
|
install
|
||||||
|
}
|
|
@ -0,0 +1,397 @@
|
||||||
|
<template>
|
||||||
|
<div ref="selectedTableRef">
|
||||||
|
<el-popover
|
||||||
|
placement="bottom"
|
||||||
|
width="400"
|
||||||
|
trigger="click"
|
||||||
|
@show="visibleChange">
|
||||||
|
<div class="option">
|
||||||
|
<el-input style="margin-bottom: 10px" v-model="search" clearable placeholder="请输入关键词" @change="getDict"
|
||||||
|
@clear="getDict">
|
||||||
|
<el-button style="width: 100px" slot="append" icon="el-icon-search"></el-button>
|
||||||
|
</el-input>
|
||||||
|
<el-table
|
||||||
|
ref="tableRef"
|
||||||
|
:data="tableData"
|
||||||
|
size="mini"
|
||||||
|
border
|
||||||
|
:row-key="dict.value"
|
||||||
|
style="width: 400px"
|
||||||
|
max-height="200"
|
||||||
|
height="200"
|
||||||
|
:highlight-current-row="!_elProps.tableConfig.multiple"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-click="handleCurrentChange"
|
||||||
|
>
|
||||||
|
<el-table-column v-if="_elProps.tableConfig.multiple" fixed type="selection" reserve-selection width="55"/>
|
||||||
|
<el-table-column fixed type="index" label="#" width="50"/>
|
||||||
|
<el-table-column :prop="item.prop" :label="item.label" :width="item.width"
|
||||||
|
v-for="(item,index) in _elProps.tableConfig.columns" :key="index"/>
|
||||||
|
</el-table>
|
||||||
|
<el-pagination style="margin-top: 10px;max-width: 200px" background
|
||||||
|
small
|
||||||
|
:current-page="pageConfig.page"
|
||||||
|
:page-size="pageConfig.limit"
|
||||||
|
layout="prev, pager, next"
|
||||||
|
:total="pageConfig.total"
|
||||||
|
@current-change="handlePageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div slot="reference" ref="divRef" :style="{'pointerEvents': disabled?'none':''}">
|
||||||
|
<div v-if="currentValue" class="div-input el-input__inner" :class="disabled?'div-disabled':''">
|
||||||
|
<div>
|
||||||
|
<el-tag
|
||||||
|
style="margin-right: 5px"
|
||||||
|
v-for="(item,index) in currentValue"
|
||||||
|
:key="index"
|
||||||
|
:closable="disabled"
|
||||||
|
size="small"
|
||||||
|
:hit="false"
|
||||||
|
type="info"
|
||||||
|
@close="itemClosed(item,index)"
|
||||||
|
disable-transitions
|
||||||
|
>
|
||||||
|
<span>{{ item[dict.label] }}</span>
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-input v-else placeholder="请选择" slot:reference :disabled="disabled"></el-input>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { request } from '@/api/service'
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
import { d2CrudPlus } from 'd2-crud-plus'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'selector-table-input',
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: ['change', 'input']
|
||||||
|
},
|
||||||
|
mixins: [d2CrudPlus.input, d2CrudPlus.inputDict],
|
||||||
|
props: {
|
||||||
|
// 值
|
||||||
|
value: {
|
||||||
|
type: [String, Number, Array],
|
||||||
|
required: false,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 数据字典配置
|
||||||
|
dict: {
|
||||||
|
type: Object,
|
||||||
|
require: false
|
||||||
|
},
|
||||||
|
// 其他配置
|
||||||
|
elProps: {
|
||||||
|
type: Object,
|
||||||
|
require: false,
|
||||||
|
default () {
|
||||||
|
return {
|
||||||
|
tableConfig: {
|
||||||
|
multiple: false,
|
||||||
|
columns: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 你可以定义一些参数,通过component.props传进来
|
||||||
|
color: {
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
styleName: {
|
||||||
|
type: [Object, String],
|
||||||
|
required: false,
|
||||||
|
default () {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
// 由于value值是props参数,是不允许修改的,需要用别的值存起来
|
||||||
|
currentValue: [],
|
||||||
|
pageConfig: {
|
||||||
|
page: 1,
|
||||||
|
limit: 5,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
search: null,
|
||||||
|
tableData: [],
|
||||||
|
multipleSelection: [],
|
||||||
|
collapseTags: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 你也可以通过computed来监听value的变化,跟watch作用类似,根据实际情况选用
|
||||||
|
_elProps () {
|
||||||
|
return this.elProps
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: {
|
||||||
|
handler (value, oldVal) {
|
||||||
|
// 父组件收到input事件后会通过v-model改变value参数的值
|
||||||
|
// 然后此处会watch到value的改变,发出change事件
|
||||||
|
// change事件放在此处发射的好处是,当外部修改value值时,也能够触发form-data-change事件
|
||||||
|
this.$emit('change', value)
|
||||||
|
this.$emit('input', value)
|
||||||
|
// 如果值是被外部改变的,则修改本组件的currentValue
|
||||||
|
if (Array.isArray(value) && value.length === 0) {
|
||||||
|
this.currentValue = null
|
||||||
|
this.multipleSelection = null
|
||||||
|
} else {
|
||||||
|
if (value && this.dispatch) {
|
||||||
|
this.dispatch('ElFormItem', 'el.form.blur')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
multipleSelection: {
|
||||||
|
handler (newValue, oldVal) {
|
||||||
|
const { tableConfig } = this._elProps
|
||||||
|
// 是否多选
|
||||||
|
if (!tableConfig.multiple) {
|
||||||
|
this.currentValue = [newValue]
|
||||||
|
} else {
|
||||||
|
this.currentValue = newValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
// currentValue (newValue, oldVal) {
|
||||||
|
// const { tableConfig } = this._elProps
|
||||||
|
// const { value } = this.dict
|
||||||
|
// if (newValue) {
|
||||||
|
// if (!tableConfig.multiple) {
|
||||||
|
// if (newValue[0]) {
|
||||||
|
// this.$emit('input', newValue[0][value])
|
||||||
|
// this.$emit('change', newValue[0][value])
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// console.log(newValue)
|
||||||
|
// const result = newValue.map((item) => {
|
||||||
|
// return item[value]
|
||||||
|
// })
|
||||||
|
// this.$emit('input', result)
|
||||||
|
// this.$emit('change', result)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
// 给currentValue设置初始值
|
||||||
|
this.setCurrentValue(this.value)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 设置显示值
|
||||||
|
setCurrentValue (val) {
|
||||||
|
if (val.toString().length > 0) {
|
||||||
|
// 在这里对 传入的value值做处理
|
||||||
|
const { url, value, label } = this.dict
|
||||||
|
const params = {}
|
||||||
|
params[value] = val
|
||||||
|
return request({
|
||||||
|
url: url,
|
||||||
|
params: params,
|
||||||
|
method: 'get'
|
||||||
|
}).then(res => {
|
||||||
|
const { data } = res.data
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
this.currentValue = data
|
||||||
|
} else {
|
||||||
|
this.currentValue = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.currentValue = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取数据
|
||||||
|
getDict () {
|
||||||
|
const that = this
|
||||||
|
let url
|
||||||
|
if (typeof that.dict.url === 'function') {
|
||||||
|
const form = that.d2CrudContext.getForm()
|
||||||
|
url = that.dict.url(that.dict, { form })
|
||||||
|
} else {
|
||||||
|
url = that.dict.url
|
||||||
|
}
|
||||||
|
let dictParams = {}
|
||||||
|
if (that.dict.params) {
|
||||||
|
dictParams = { ...that.dict.params }
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
page: that.pageConfig.page,
|
||||||
|
limit: that.pageConfig.limit
|
||||||
|
}
|
||||||
|
if (that.search) {
|
||||||
|
params.search = that.search
|
||||||
|
params.page = 1
|
||||||
|
}
|
||||||
|
if (that._elProps.tableConfig.data === undefined || that._elProps.tableConfig.data.length === 0) {
|
||||||
|
request({
|
||||||
|
url: url,
|
||||||
|
method: 'get',
|
||||||
|
params: { ...params, ...dictParams }
|
||||||
|
}).then(res => {
|
||||||
|
const { data, page, limit, total } = res.data
|
||||||
|
that.pageConfig.page = page
|
||||||
|
that.pageConfig.limit = limit
|
||||||
|
that.pageConfig.total = total
|
||||||
|
if (that._elProps.tableConfig.isTree) {
|
||||||
|
that.tableData = XEUtils.toArrayTree(data, { parentKey: 'parent', key: 'id', children: 'children' })
|
||||||
|
} else {
|
||||||
|
that.tableData = data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
that.tableData = that._elProps.tableConfig.data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 下拉框展开/关闭
|
||||||
|
* @param bool
|
||||||
|
*/
|
||||||
|
visibleChange () {
|
||||||
|
const that = this
|
||||||
|
that.getDict()
|
||||||
|
const { tableConfig } = that._elProps
|
||||||
|
if (tableConfig.multiple) {
|
||||||
|
that.$refs.tableRef.clearSelection() // 先清空选择,再赋值选择
|
||||||
|
that.currentValue ? that.currentValue.forEach(item => {
|
||||||
|
that.$refs.tableRef.toggleRowSelection(item, true)
|
||||||
|
}) : null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 分页
|
||||||
|
* @param page
|
||||||
|
*/
|
||||||
|
handlePageChange (page) {
|
||||||
|
this.pageConfig.page = page
|
||||||
|
this.getDict()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 表格多选
|
||||||
|
* @param val:Array
|
||||||
|
*/
|
||||||
|
handleSelectionChange (val) {
|
||||||
|
this.multipleSelection = val
|
||||||
|
this.$emit('checkChange', val)
|
||||||
|
const result = val.map((item) => {
|
||||||
|
return item[this.dict.value]
|
||||||
|
})
|
||||||
|
this.$emit('input', result)
|
||||||
|
this.$emit('change', result)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 表格单选
|
||||||
|
* @param val:Object
|
||||||
|
*/
|
||||||
|
handleCurrentChange (val) {
|
||||||
|
const { tableConfig } = this._elProps
|
||||||
|
if (!tableConfig.multiple) {
|
||||||
|
this.multipleSelection = val
|
||||||
|
this.$emit('radioChange', val)
|
||||||
|
this.$emit('input', val[this.dict.value])
|
||||||
|
this.$emit('change', val[this.dict.value])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/***
|
||||||
|
* 清空
|
||||||
|
*/
|
||||||
|
onClear () {
|
||||||
|
const { tableConfig } = this._elProps
|
||||||
|
if (!tableConfig.multiple) {
|
||||||
|
this.$emit('input', '')
|
||||||
|
this.$emit('change', '')
|
||||||
|
} else {
|
||||||
|
this.$emit('input', [])
|
||||||
|
this.$emit('change', [])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* tag删除事件
|
||||||
|
* @param obj
|
||||||
|
*/
|
||||||
|
itemClosed (obj, index) {
|
||||||
|
const { tableConfig } = this._elProps
|
||||||
|
XEUtils.remove(this.multipleSelection, index)
|
||||||
|
XEUtils.remove(this.currentValue, index)
|
||||||
|
if (!tableConfig.multiple) {
|
||||||
|
this.$emit('input', '')
|
||||||
|
this.$emit('change', '')
|
||||||
|
} else {
|
||||||
|
this.$refs.tableRef?.toggleRowSelection(obj, false)
|
||||||
|
// const { value } = this.dict
|
||||||
|
// const result = this.currentValue.map((item) => {
|
||||||
|
// return item[value]
|
||||||
|
// })
|
||||||
|
// this.$emit('input', result)
|
||||||
|
// this.$emit('change', result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.option {
|
||||||
|
height: auto;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
.popperClass {
|
||||||
|
height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select-dropdown__wrap {
|
||||||
|
max-height: 310px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableSelector {
|
||||||
|
.el-icon, .el-tag__close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.div-input {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-color: #FFF;
|
||||||
|
background-image: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #DCDFE6;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #606266;
|
||||||
|
display: inline-block;
|
||||||
|
min-height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
outline: 0;
|
||||||
|
padding: 0 15px;
|
||||||
|
-webkit-transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
|
||||||
|
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
.div-disabled{
|
||||||
|
background-color: #F5F7FA;
|
||||||
|
border-color: #E4E7ED;
|
||||||
|
color: #C0C4CC;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,12 @@
|
||||||
|
export default {
|
||||||
|
// 字段类型配置,注册之后即可在crud.js中使用了
|
||||||
|
'table-progress': {
|
||||||
|
// 表单组件配置
|
||||||
|
form: { component: { name: 'form-input', props: { color: 'danger' } } },
|
||||||
|
// 行组件配置
|
||||||
|
component: { name: 'table-progress', props: {} },
|
||||||
|
// 行展示时居中
|
||||||
|
align: 'center'
|
||||||
|
// 您还可以写更多默认配置
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { d2CrudPlus } from 'd2-crud-plus'
|
||||||
|
import group from './group'
|
||||||
|
function install (Vue) {
|
||||||
|
Vue.component('table-progress', () => import('./lib/table-progress'))
|
||||||
|
if (d2CrudPlus != null) {
|
||||||
|
// 注册字段类型`demo-extend`
|
||||||
|
d2CrudPlus.util.columnResolve.addTypes(group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-progress :percentage="currentValue" :color="setColor" :format="format"></el-progress>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 行展示组件进阶版
|
||||||
|
// 本示例演示要对传入的值做一些改变,然后再展示
|
||||||
|
export default {
|
||||||
|
name: 'table-progress',
|
||||||
|
props: {
|
||||||
|
// 接收row.xxx的值
|
||||||
|
value: {
|
||||||
|
type: String || Number,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
currentValue: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value (value) {
|
||||||
|
// this.$emit('change', value)
|
||||||
|
if (this.currentValue === value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setValue(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.setValue(this.value)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setValue (value) {
|
||||||
|
// 在这里对 传入的value值做处理
|
||||||
|
this.currentValue = Number(this.value)
|
||||||
|
// 根据值的key 递归获取对应的名称
|
||||||
|
},
|
||||||
|
setColor () {
|
||||||
|
if (this.value <= 50) {
|
||||||
|
return '#F56C6C'
|
||||||
|
} else if (this.value > 50 && this.value < 80) {
|
||||||
|
return '#E6A23C'
|
||||||
|
} else {
|
||||||
|
return '#67C23A'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
format (percentage) {
|
||||||
|
return `${percentage}%`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -104,7 +104,7 @@ export default {
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 40,
|
top: 40,
|
||||||
left: 40,
|
left: 50,
|
||||||
right: 65,
|
right: 65,
|
||||||
bottom: 75
|
bottom: 75
|
||||||
},
|
},
|
||||||
|
|
|
@ -95,7 +95,7 @@ export default {
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 40,
|
top: 40,
|
||||||
left: 40,
|
left: 50,
|
||||||
right: 65,
|
right: 65,
|
||||||
bottom: 60
|
bottom: 60
|
||||||
},
|
},
|
||||||
|
|
|
@ -98,7 +98,7 @@ export default {
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 40,
|
top: 40,
|
||||||
left: 40,
|
left: 50,
|
||||||
right: 65,
|
right: 65,
|
||||||
bottom: 60
|
bottom: 60
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue