2025-03-18 08:46:50 +08:00

559 lines
28 KiB
Python
Raw Permalink 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.3
# +-------------------------------------------------------------------
# | django-vue-lyadmin 专业版
# +-------------------------------------------------------------------
# | Author: lybbn
# +-------------------------------------------------------------------
# | QQ: 1042594286
# +-------------------------------------------------------------------
# | Date: 2023/10/17
# +-------------------------------------------------------------------
# ------------------------------
# 表单构建-- index.vue 前端页面
# ------------------------------
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">
<el-table :height="tableHeight" border :data="tableData" ref="tableref" v-loading="loadingPage" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column type="index" width="60" align="center" label="序号">
<template #default="scope">
<span v-text="getIndex(scope.$index)"></span>
</template>
</el-table-column>{% for c in object.widgetList %}{% if c.options.crudField.list_display_custom %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="scope">
{{c.options.crudField.list_display_custom | safe}}
</template>
</el-table-column>{% elif c.options.crudField.field_type == 'ForeignKey' or c.options.crudField.field_type == 'OneToOneField' %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="scope">
<span>{% templatetag openvariable %} scope.row.{{c.options.crudField.field_name}}_lyformbuilder_name {% templatetag closevariable %}</span>
</template>
</el-table-column>{% elif c.type == 'picture-single' %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="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>
</el-table-column>{% elif c.type == 'picture-upload' %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="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>
</el-table-column>{% elif c.type == 'file-upload' %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="scope">
{% templatetag openvariable %} scope.row.{{c.options.crudField.field_name}}.split(',').length + "" {% templatetag closevariable %}
</template>
</el-table-column>{% elif c.type == 'checkbox' %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="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>
</el-table-column>{% elif c.type == 'cascader' %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="scope">
{% templatetag openvariable %} scope.row.{{c.options.crudField.field_name}} {% templatetag closevariable %}
</template>
</el-table-column>{% elif c.options.crudField.field_type == 'BooleanField' and c.options.optionItems %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="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>
</el-table-column>{% elif c.options.optionItems %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}">
<template #default="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>
</el-table-column>{% else %}
<el-table-column min-width="{{c.options.crudField.width}}" prop="{{c.options.crudField.field_name}}" label="{{c.options.crudField.verbose_name}}" v-if="{{c.options.crudField.can_list | lower}}"></el-table-column>{% endif %}{% endfor %}
<el-table-column min-width="150" prop="create_datetime" label="创建时间"></el-table-column>
<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>
</el-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>
<Pagination v-bind:child-msg="pageparm" @callFather="callFather"></Pagination>
</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 Pagination from "@/components/Pagination";
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({
page: 1,
limit: 10,
})
let defaultImg = ref(require('../../assets/img/avatar.jpg'))
let pageparm = ref({
page: 1,
limit: 10,
total: 0
})
let tableData = ref([])
let timers = ref([]){% for d in date_range_filter_list %}
let {{d.search_data}} = ref([]){% endfor %}
// 选项框选中数组
let ids = ref([])
// 选项框非单个禁用
let single = ref(true)
// 非多个禁用
let multiple = ref(true)
//多选项框被选中数据
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 getIndex($index) {
// (当前页 - 1) * 当前显示数据条数 + 当前行数据的索引 + 1
return (pageparm.value.page-1)*pageparm.value.limit + $index +1
}
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 callFather(parm) {
formInline.value.page = parm.page
formInline.value.limit = parm.limit
getData()
}
function search() {
formInline.value.page = 1
formInline.value.limit = 10
getData()
}
/** 批量删除按钮操作 */
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 = {
page:1,
limit: 10
}
pageparm.value={
page: 1,
limit: 10,
total: 0
}
timers.value = []{% for d in date_range_filter_list %}
{{d.search_data}}.value=[]{% endfor %}
search()
}
}
//获取列表
async function getData(){
loadingPage.value = true
lyformbuilderList(formInline.value).then(res => {
loadingPage.value = false
if(res.code ==2000) {
tableData.value = res.data.data
pageparm.value.page = res.data.page;
pageparm.value.limit = res.data.limit;
pageparm.value.total = res.data.total;
}
})
}
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,
}
}
loadingPage.value = true
lyformbuilderExport(params).then(res => {
loadingPage.value = 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)
})
}
onMounted(()=>{
getData()
})
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)