2025-03-17 18:06:54 +08:00

510 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/python
#coding: utf-8
# +-------------------------------------------------------------------
# | version1.0
# +-------------------------------------------------------------------
# | django-vue-lyadmin 专业版
# +-------------------------------------------------------------------
# | Author: lybbn
# +-------------------------------------------------------------------
# | QQ: 1042594286
# +-------------------------------------------------------------------
# | Date: 2023/11/05
# +-------------------------------------------------------------------
# ------------------------------
# 表单构建-- index.vue 前端页面lytable版
# ------------------------------
from django.template import Template,Context
def lyGenerateIndexVue(objects,object,VueIndexName):
#导出
canExport = False
#外键筛选处理
ForeignKeyList = []
#日期范围过滤处理
date_range_filter_list = []
for c in object['widgetList']:
field_name = c['options']['crudField']['field_name']
verbose_name = c['options']['crudField']['verbose_name']
field_type = c['options']['crudField'].get('field_type','')
foreign_key = c['options']['crudField']['foreign_key']
form_type = c.get('type','')
if c['options']['crudField'].get('can_search',False) and c['options']['crudField']['search_type'] == 'range' and form_type in ['date','time']:
search_data = '%sTimers'%field_name
search_data_beginAt = '%s_beginAt'%field_name
search_data_endAt = '%s_endAt'%field_name
search_data_funcion = '%sTimeChange'%field_name
search_range_type = form_type
if form_type == 'date':
search_format = 'yyyy-MM-dd'
search_item = f"""<el-date-picker style="width:350px" v-model="{search_data}" type="daterange" @change="{search_data_funcion}" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>"""
else:
search_format = 'yyyy-MM-dd hh:mm:ss'
search_item = f"""<el-date-picker style="width:350px" v-model="{search_data}" type="datetimerange" @change="{search_data_funcion}" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>"""
d_f_data = {
'search_data':search_data,
'search_data_beginAt':search_data_beginAt,
'search_data_endAt':search_data_endAt,
'search_item':search_item,
'search_range_type':search_range_type,
'search_data_funcion':search_data_funcion,
'search_format':search_format
}
date_range_filter_list.append(d_f_data)
c['date_range_filter_list'] = d_f_data
if c['options']['crudField'].get('can_export',False):
canExport = True
if field_type in ['ForeignKey','ManyToManyField','OneToOneField']:
f_class_name = foreign_key['class_name']
f_value = foreign_key['value']
f_label = foreign_key['label']
f_api_path = foreign_key['api_path']
if f_class_name and f_value and f_label and f_api_path:
const_cl = 'params'
gname_data = f"{field_name}LyFormBuilderAPIList"
gname_data_let = f"let {gname_data} = ref([])"
gname = "%sLyFormBuilderAPI"%field_name
gname_function = "%sLyFormBuilderAPIFunction"%field_name
gname_params = '{url: `%s`,%s}'%(f_api_path,const_cl)
import_data = f"""const {gname} = {const_cl} => ajaxGet({gname_params})"""
s_data = {
'field_name':field_name,
'name':gname,
'data':gname_data,
'data_let':gname_data_let,
'import':import_data,
'function':gname_function,
'value':f_value,
'label':f_label
}
ForeignKeyList.append(s_data)
c['ForeignKeyList'] = s_data
apps_code = """<template>
<div :class="{'ly-is-full':isFull}">
<div class="tableSelect" ref="tableSelect">
<el-form :model="formInline">{% for c in object.widgetList %}{% if c.options.isFormField and c.options.crudField.can_search and c.options.optionItems %}
<el-form-item label="{{c.options.crudField.verbose_name}}">
<el-select v-model="formInline.{{c.options.crudField.field_name}}" clearable filterable placeholder="请选择{{c.options.crudField.verbose_name}}" style="width:200px;" @change="search">
<el-option
v-for="item in {{c.options.optionItems|safe}}"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>{% elif c.options.isFormField and c.options.crudField.can_search and c.options.crudField.search_type == 'range' and c.type == 'date'%}
<el-form-item label="{{c.options.crudField.verbose_name}}">
{{c.date_range_filter_list.search_item|safe}}
</el-form-item>{% elif c.options.isFormField and c.options.crudField.can_search and c.options.crudField.search_type == 'range' and c.type == 'time'%}
<el-form-item label="{{c.options.crudField.verbose_name}}">
{{c.date_range_filter_list.search_item|safe}}
</el-form-item>{% elif c.options.isFormField and c.options.crudField.can_search %}
<el-form-item label="{{c.options.crudField.verbose_name}}">
<el-input v-model="formInline.{{c.options.crudField.field_name}}" maxlength="100" clearable placeholder="请输入{{c.options.crudField.verbose_name}}" @change="search" style="width:200px"></el-input>
</el-form-item>{% endif %}{% endfor %}
<el-form-item label="创建时间:" v-if="{{object.formConfig.other_config.create_datetime_filter|lower}}">
<el-date-picker
style="width:350px"
v-model="timers"
type="datetimerange"
@change="timeChange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item label="" v-if="hasPermission(route.name,'Search')"><el-button @click="search" type="primary" icon="Search">查询</el-button></el-form-item>
<el-form-item label=""><el-button @click="handleEdit('','reset')" icon="Refresh">重置</el-button></el-form-item>
<el-form-item label="" v-if="hasPermission(route.name,'Create')"><el-button type="primary" icon="Plus" @click="handleEdit('','add')" >新增</el-button></el-form-item>
<el-form-item label="" v-if="hasPermission(route.name,'Delete') && {{object.formConfig.other_config.mutiple_delete|lower}}"><el-button @click="handleDelete" type="danger" :disabled="multiple" icon="Delete" >删除</el-button></el-form-item>
<el-form-item label="" v-if="hasPermission(route.name,'Export') && {{canExport|lower}}"><el-button type="primary" @click="exportDataBackend" :disabled="loadingPage" icon="Download">导出</el-button></el-form-item>
</el-form>
</div>
<div class="table">
<ly-table tableName="{{VueIndexName}}Table" :height="tableHeight+51" :pageSize="10" :apiObj="lyformbuilderList" :params="formInline" ref="tableref" :column="column" showSelectable showSequence @selection-change="handleSelectionChange">{% for c in object.widgetList %}{% if c.options.crudField.list_display_custom %}
<template #{{c.options.crudField.field_name}}="scope">
{{c.options.crudField.list_display_custom | safe}}
</template>{% elif c.options.crudField.field_type == 'ForeignKey' or c.options.crudField.field_type == 'OneToOneField' %}
<template #{{c.options.crudField.field_name}}="scope">
<span>{% templatetag openvariable %} scope.row.{{c.options.crudField.field_name}}_lyformbuilder_name {% templatetag closevariable %}</span>
</template>{% elif c.type == 'picture-single' %}
<template #{{c.options.crudField.field_name}}="scope">
<el-image preview-teleported :src="scope.row.{{c.options.crudField.field_name}}" style="width: 30px;height:30px" :preview-src-list="[scope.row.{{c.options.crudField.field_name}}]" v-if="scope.row.{{c.options.crudField.field_name}}"></el-image>
</template>{% elif c.type == 'picture-upload' %}
<template #{{c.options.crudField.field_name}}="scope">
<el-image v-for="(item,index) in scope.row.{{c.options.crudField.field_name}}.split(',')" v-bind:key="index" preview-teleported :src="item" style="width: 30px;height:30px;margin-right: 5px;" :preview-src-list="[item]" v-if="scope.row.{{c.options.crudField.field_name}}"></el-image>
</template>{% elif c.type == 'file-upload' %}
<template #{{c.options.crudField.field_name}}="scope">
{% templatetag openvariable %} scope.row.{{c.options.crudField.field_name}}.split(',').length + "" {% templatetag closevariable %}
</template>{% elif c.type == 'checkbox' %}
<template #{{c.options.crudField.field_name}}="scope">
<span v-for="(item1,index1) in scope.row.{{c.options.crudField.field_name}}">
<span v-for="(item,index) in {{c.options.optionItems|safe}}">
<span v-if="item1 == item.value">{% templatetag openvariable %} item.label {% templatetag closevariable %}<span v-if="index1<scope.row.{{c.options.crudField.field_name}}.length-1">、</span></span>
</span>
</span>
</template>{% elif c.type == 'cascader' %}
<template #{{c.options.crudField.field_name}}="scope">
{% templatetag openvariable %} scope.row.{{c.options.crudField.field_name}} {% templatetag closevariable %}
</template>{% elif c.options.crudField.field_type == 'BooleanField' and c.options.optionItems %}
<template #{{c.options.crudField.field_name}}="scope">
<span v-for="(item,index) in c.options.optionItems">
<el-tag v-if="scope.row.{{c.options.crudField.field_name}} && !!JSON.parse(item.value)" type="success">{% templatetag openvariable %} item.label {% templatetag closevariable %}</el-tag>
<el-tag v-else-if="!scope.row.{{c.options.crudField.field_name}} && !(!!JSON.parse(item.value))" type="danger">{% templatetag openvariable %} item.label {% templatetag closevariable %}</el-tag>
</span>
</template>{% elif c.options.optionItems %}
<template #{{c.options.crudField.field_name}}="scope">
<span v-for="(item,index) in {{c.options.optionItems|safe}}">
<span v-if="scope.row.{{c.options.crudField.field_name}} == item.value" >{% templatetag openvariable %} item.label {% templatetag closevariable %}</span>
</span>
</template>{% endif %}{% endfor %}
<el-table-column label="操作" fixed="right" width="180">
<template #header>
<div style="display: flex;justify-content: space-between;align-items: center;">
<div>操作</div>
<div @click="setFull">
<el-tooltip content="全屏" placement="bottom">
<el-icon ><full-screen /></el-icon>
</el-tooltip>
</div>
</div>
</template>
<template #default="scope">
<span class="table-operate-btn" @click="handleEdit(scope.row,'detail')" v-show="hasPermission(route.name,'Retrieve')">详情</span>
<span class="table-operate-btn" @click="handleEdit(scope.row,'edit')" v-if="hasPermission(route.name,'Update')">编辑</span>
<span class="table-operate-btn" @click="handleEdit(scope.row,'delete')" v-if="hasPermission(route.name,'Delete')">删除</span>
</template>
</el-table-column>
</ly-table>
</div>
<lyDialog v-model="dialogVisible" v-if="dialogVisible" :title="dialogTitleMap[currentMode]" width="60%" :before-close="handleClose" :fullscreen="fullscreen" :loading="dialogLoading">
<lyFormRender :form-json="formJson" :form-data="formData" ref="lyFormBuilderRef"></lyFormRender>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="submitForm" :loading="loadingSave" v-if="currentMode!='detail'">确定</el-button>
</template>
</lyDialog>
</div>
</template>
<script setup>
import {ajaxGet,ajaxPost,ajaxDelete,ajaxPut,ajaxPatch,ajaxDownloadExcel} from '@/api/request'
// 列表
const lyformbuilderList = params => ajaxGet({url: `lyformbuilder/{{object.formConfig.modelClassName}}/`,params})
// 新增
const lyformbuilderAdd = params => ajaxPost({url: `lyformbuilder/{{object.formConfig.modelClassName}}/`,params})
// 编辑
const lyformbuilderEdit = params => ajaxPut({url: `lyformbuilder/{{object.formConfig.modelClassName}}/`,params})
// 删除
const lyformbuilderDelete = params => ajaxDelete({url: `lyformbuilder/{{object.formConfig.modelClassName}}/`,params})
// 导出
const lyformbuilderExport = params => ajaxDownloadExcel({url: `lyformbuilder/{{object.formConfig.modelClassName}}/export/`,params}){% for f in ForeignKeyList %}
{{f.import|safe}}{% endfor %}
import {ref,reactive,onMounted,onBeforeUnmount,nextTick} from 'vue'
import { ElMessage,ElMessageBox } from 'element-plus';
import {hasPermission,dateFormats,getStorage,deepClone} from "@/utils/util";
import LyDialog from "@/components/dialog/dialog";
import useTableHight from '@/mixins/useTableHight';
import lyFormRender from '@/components/lyform-builder/lyform-render/index'
import { useRoute } from 'vue-router'
const route = useRoute()
let isFull = ref(false)
let orderStatic = ref(null)
let tableSelect = ref(null)
let tableHeight = useTableHight(orderStatic,tableSelect,isFull.value)
let loadingPage = ref(false)
let formInline = ref({})
let defaultImg = ref(require('../../assets/img/avatar.jpg'))
let tableData = ref([])
let timers = ref([]){% for d in date_range_filter_list %}
let {{d.search_data}} = ref([]){% endfor %}
let column = ref([{% for c in object.widgetList %}
{
label: "{{c.options.crudField.verbose_name}}",
prop: "{{c.options.crudField.field_name}}",
minWidth:"{{c.options.crudField.width}}",
hide: !{{c.options.crudField.can_list | lower}}
},{% endfor %}
{
label: "创建时间",
prop: "create_datetime",
minWidth:"180"
}
])
// 选项框选中数组
let ids = ref([])
// 选项框非单个禁用
let single = ref(true)
// 非多个禁用
let multiple = ref(true)
let tableref = ref(null)
//多选项框被选中数据
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
}
function isobject(item){
if(typeof(item) == "object"){
return true
}else{
return false
}
}
function setFull(){
isFull.value=!isFull.value
window.dispatchEvent(new Event('resize'))
}
function setLoadingPage(bools){
loadingPage.value = bools
tableref.value.loadingPage(bools)
}
function convertStrNum(model,value){
if(model === "" || model === null || model === undefined){
return value
}
else if(Object.prototype.toString.call(model)=== "[object String]"){
return value.toString()
}
else if(Object.prototype.toString.call(model) === "[object Number]"){
return value*1
}else{
return value
}
}
function search() {
tableref.value.reload(formInline.value)
}
/** 批量删除按钮操作 */
function handleDelete(row) {
ElMessageBox.confirm('是否确认删除选中的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return lyformbuilderDelete({id:ids.value}).then(res=>{
if(res.code == 2000) {
ElMessage.success(res.msg)
getData()
} else {
ElMessage.warning(res.msg)
}
})
})
}
{% for f in ForeignKeyList %}
{{f.data_let}}{% endfor %}
{% for f in ForeignKeyList %}
function {{f.function}}() {
{{f.name}}({page:1,limit:999}).then(res => {
if(res.code ==2000) {
{{f.data}}.value = res.data.data
}
lyFormBuilderRef.value.setFormJsonSelectOption('{{f.field_name}}',{{f.data}}.value)
})
}{% endfor %}
function handleEdit(row,flag) {
if(flag=='add') {
currentMode.value = 'add';
disableForm(false)
dialogVisible.value=true{% for f in ForeignKeyList %}
{{f.function}}(){% endfor %}
}
else if(flag=='edit') {
currentMode.value = 'edit';
disableForm(false)
formData.value = deepClone(row)
dialogVisible.value=true{% for f in ForeignKeyList %}
{{f.function}}(){% endfor %}
}
else if(flag=='detail') {
currentMode.value = 'detail';
if(currentMode.value == "detail"){
disableForm()
}
formData.value = deepClone(row)
dialogVisible.value=true{% for f in ForeignKeyList %}
{{f.function}}(){% endfor %}
}
else if(flag=='delete') {
ElMessageBox.confirm('您确定要删除选中的数据吗?', "警告",{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return lyformbuilderDelete({id:row.id}).then(res=>{
if(res.code == 2000) {
ElMessage.success(res.msg)
getData()
} else {
ElMessage.warning(res.msg)
}
})
})
}
else if(flag=="reset"){
formInline.value = {}
timers.value = []{% for d in date_range_filter_list %}
{{d.search_data}}.value=[]{% endfor %}
search()
}
}
//获取列表
async function getData(){
tableref.value.getData()
}
function timeChange(val){
if (val) {
formInline.value.beginAt=dateFormats(val[0],'yyyy-MM-dd hh:mm:ss');
formInline.value.endAt=dateFormats(val[1],'yyyy-MM-dd hh:mm:ss');
} else {
formInline.value.beginAt = null
formInline.value.endAt = null
}
search()
}{% for d in date_range_filter_list %}
function {{d.search_data_funcion}}(val) {
if (val) {
formInline.value.{{d.search_data_beginAt}}=dateFormats(val[0],'{{d.search_format}}');
formInline.value.{{d.search_data_endAt}}=dateFormats(val[1],'{{d.search_format}}');
} else {
formInline.value.{{d.search_data_beginAt}} = null
formInline.value.{{d.search_data_endAt}} = null
}
search()
}{% endfor %}
//弹窗
let dialogVisible = ref(false)
let dialogLoading = ref(false)
let loadingSave = ref(false)
let dialogTitle = ref('')
let currentMode = ref('add')
let dialogTitleMap = {
add: '新增',
edit: '编辑',
detail: '详情'
}
let fullscreen = ref(false)
let formJson = ref({{objects|safe}})
let formData = ref({})
let lyFormBuilderRef = ref(null)
function handleClose() {
dialogVisible.value = false
dialogLoading.value = false
loadingSave.value = false
currentMode.value = 'add'
formData.value = {}
}
function disableForm(flag=true){
if(formJson.value.formConfig){
formJson.value.formConfig.disabled = flag
}
}
//excel文件流下载
function downloadExcelBlob(res) {
let fileName = new Date().getTime() +".xlsx"
let dispositionStr = res.headers["content-disposition"];
if (dispositionStr == null || dispositionStr === "") {
}else{
// 获取文件名
let dispositionArr = dispositionStr.split(";");
fileName = decodeURIComponent(dispositionArr[1]);
fileName = fileName.split("=")[1];
}
const blob = new Blob([res.data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
let href = window.URL.createObjectURL(blob); //下载链接
let link = document.createElement("a")
link.href = href
link.download = fileName //下载后文件名
document.body.appendChild(link);
link.click(); //点击下载
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(href); //释放blob对象
ElMessage.success('导出成功')
}
//导出
function exportDataBackend() {
let params = {}
if(ids.value.length>0){
params = {
ids:ids.value,
}
}
setLoadingPage(true)
lyformbuilderExport(params).then(res => {
setLoadingPage(false)
downloadExcelBlob(res)
})
}
const submitForm = () => {
lyFormBuilderRef.value.getFormData().then(formData => {
if(formData){
loadingSave.value=true
let param = {
...formData
}{% for c in object.widgetList %}{% if c.type == 'checkbox' or c.type == 'cascader' %}
param['{{c.options.crudField.field_name}}'] = JSON.stringify(param['{{c.options.crudField.field_name}}']){% endif %}{% endfor %}
if(currentMode.value == 'add'){
lyformbuilderAdd(param).then(res=>{
loadingSave.value=false
if(res.code ==2000) {
ElMessage.success(res.msg)
handleClose()
getData()
} else {
ElMessage.warning(res.msg)
}
})
}else{
lyformbuilderEdit(param).then(res=>{
loadingSave.value=false
if(res.code ==2000) {
ElMessage.success(res.msg)
handleClose()
getData()
} else {
ElMessage.warning(res.msg)
}
})
}
}
}).catch(error => {
ElMessage.error(error)
})
}
defineExpose({
setFull
})
</script>
<style lang="scss" scoped>
</style>
"""
context = Context({'objects':objects,'object': object,'VueIndexName':VueIndexName,'ForeignKeyList':ForeignKeyList,'canExport':canExport,'date_range_filter_list':date_range_filter_list})
return Template(apps_code).render(context)