效果图↓
需要配置↓
index.html↓
<!DOCTYPE html>
{% load i18n static %}
{% load simpletags %}
{% load customtags %}
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<!--
The project use: django-simpleui
source code:
https://github.com/newpanjing/simpleui
-->
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"/>
{% block title %}
<title>{{ site_title }}</title>
{% endblock %}
{% block icon %}
{% endblock %}
{% block css %}
{% include 'admin/includes/css-part.html' %}
<link rel="stylesheet" href="{% static 'admin/simpleui-x/css/index.css' %}?_=3.2">
<link rel="stylesheet" href="{% static 'admin/simpleui-x/waves/waves.min.css' %}?_=2.1">
{% endblock %}
{% block head %}
{% endblock %}
</head>
<body>
<style type="text/css">
.el-tabs__nav .el-tabs__item:nth-child(1) .el-icon-close {
display: none;
}
* {
-webkit-overflow-scrolling: touch;
}
[v-block] {
display: none;
}
</style>
{% verbatim dynamicCss %}
{% endverbatim dynamicCss %}
{% block menus %}
{% autoescape off %}
{% custom_menus %}
{% endautoescape %}
{% endblock %}
<div id="main" @click="mainClick($event)" v-block>
{% block theme_css %}
<link v-if="theme && theme!=''" rel="stylesheet" :href="theme">
{% if "SIMPLEUI_DEFAULT_THEME"|get_config %}
<link v-else rel="stylesheet"
href="{% static 'admin/simpleui-x/theme/' %}{{ "SIMPLEUI_DEFAULT_THEME"|get_config }}">
{% endif %}
{% endblock %}
<!-- mobile -->
<el-drawer
class="lite-menus"
title="{{ site_header }}"
:visible.sync="drawer"
:show-close="false"
size="50%"
direction="ltr">
<el-menu unique-opened="true" :default-active="menuActive">
<div v-for="(item,i) in menus" :key="item">
<el-menu-item v-if="!item.models" :index="item.eid+''" @click="openTab(item,item.eid)">
<i :class="item.icon"></i>
<span slot="title" v-text="item.name"></span>
</el-menu-item>
<el-submenu v-else :index="item.eid+''">
<template slot="title">
<i :class="item.icon"></i>
<span slot="title" v-text="item.name"></span>
</template>
<el-menu-item-group v-for="(sub,j) in item.models" :title="sub.name" :key="sub.name">
<el-menu-item :index="sub.eid+''" @click="openTab(sub,item.eid)">
<i :class="sub.icon"></i>
<span slot="title" v-text="sub.name"></span>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</div>
</el-menu>
</el-drawer>
<el-container :style="{height: height+'px'}">
<el-aside v-show="!mobile" width="auto" class="menu" {% block menu_style %}{% endblock %}>
{% block logo %}
<div class="logo-wrap" v-if="!fold">
<div class="float-wrap">
<div class="left">
{% if "SIMPLEUI_LOGO"|get_config %}
<img src="{{ "SIMPLEUI_LOGO"|get_config |safe }}">
{% else %}
<img src="{% static 'admin/simpleui-x/img/logo.png' %}">
{% endif %}
</div>
<div class="left">
<span>{{ site_header }}</span>
</div>
</div>
</div>
{% endblock %}
<!-- menu -->
<transition name="el-zoom-in-center">
<multiple-menu
:menus="menus"
:menu-active="menuActive"
:fold="fold"
></multiple-menu>
</transition>
</el-aside>
<el-container>
{% block header %}
<el-header class="navbar" style="font-size: 12px;padding: 10px;height: auto">
<div class="float-wrap">
<div class="left">
<el-button v-waves circle icon="fas fa-bars"
style="margin-right: 10px;border: none" @click="foldClick()"></el-button>
{% block breadcrumb %}
<el-breadcrumb v-if="!mobile" style="display: inline-block;" separator="/">
{% verbatim myclode %}
<el-breadcrumb-item><i :class="menus[0].icon"
:key="menus[0].name"></i>{{ menus[0].name }}
</el-breadcrumb-item>
<el-breadcrumb-item v-for="item in breadcrumbs"
:key="item">
<span :class="getIcon(item.name,item.icon)"></span>
<span v-text="item.name"></span>
</el-breadcrumb-item>
{% endverbatim myclode %}
</el-breadcrumb>
{% endblock %}
</div>
<div class="right">
<el-button icon="fas fa-font" circle v-waves @click="fontClick()"></el-button>
<el-button
:icon="this.zoom?'fas fa-compress-arrows-alt':'fas fa-expand-arrows-alt'"
@click="goZoom()" circle></el-button>
<el-button icon="fas fa-home" @click="goIndex('{{ 'SIMPLEUI_INDEX'|get_config }}')" circle
v-waves></el-button>
{% verbatim mycode %}
<el-button @click="themeDialogVisible=true" v-waves>
<i class="fas fa-palette"></i>
<span v-text="getLanuage('Change theme')"></span><i
class="el-icon-arrow-down el-icon--right"></i>
</el-button>
{% endverbatim mycode %}
<el-dropdown>
<el-button v-waves>
<i class="fas fa-user"></i>
{{ user }}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
{% verbatim mycode %}
<el-dropdown-item v-waves icon="far fa-edit"
@click.native="changePassword()">{{ language.change_password }}
</el-dropdown-item>
{% endverbatim mycode %}
{% has_enable_admindoc as has_admindoc %}
{% if has_admindoc %}
<el-dropdown-item icon="el-icon-document"
@click.native="openTab({eid:100000,name:'{% trans 'Documentation' %}',icon:'el-icon-document',url:'{% url 'django-admindocs-docroot' %}'})"
divided>{% trans 'Documentation' %}</el-dropdown-item>
{% endif %}
{% verbatim mycode %}
<el-dropdown-item icon="el-icon-close"
@click.native="logout()"
divided>{{ language.logout }}</el-dropdown-item>
{% endverbatim mycode %}
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</el-header>
{% endblock %}
<el-main>
<el-tabs v-model="tabModel" {% block tabs_type %}type="border-card" {% endblock %} editable
:style="isResize?'height:100%':'height: calc(100% - 97px)'" @edit="handleTabsEdit"
@tab-click="tabClick">
<el-tab-pane v-for="(item,index) in tabs" :closable="index!=0" :label="item.name" :name="item.id"
:key="item.id" lazy="true">
{% home_page %}
{% if home %}
<span v-if="index==0" slot="label"><i
class="{{ icon }}"></i><span>{{ title }}</span>
</span>
<span v-else slot="label" @contextmenu.prevent="contextmenu(item,$event)">
{% else %}
<span slot="label" @contextmenu.prevent="contextmenu(item,$event)">
{% endif %}
{% block tab-item %}
<i :class="item.loading?'el-icon-loading':item.icon"></i>
<span v-text="item.name"></span>
{% endblock %}
</span>
<div v-if="index==0" style="height:100%">
{% block home_content %}
{% if home %}
<iframe :src="'{{ home }}'"></iframe>
{% else %}
{% include './home.html' %}
{% endif %}
{% endblock %}
</div>
<div v-else class="iframe-wrap">
<iframe :src="item.url" :id="item.id" @load="iframeLoad(item,$event)"></iframe>
{% if "SIMPLEUI_LOADING"|get_config != False %}
<div v-if="loading" class="loading" @dblclick="loading=false">
<div class="center">
<span class="el-icon-loading"></span>
<span>loading...</span>
</div>
</div>
{% endif %}
</div>
</el-tab-pane>
</el-tabs>
</el-main>
</el-container>
</el-container>
<ul v-if="popup.show" class="el-dropdown-menu el-popper" ref="popupmenu"
:style="{position: 'absolute',top: popup.top+'px',left: popup.left+'px'}" x-placement="top-end">
<li v-for="(item,index) in popup.menus" tabindex="-1" class="el-dropdown-menu__item"
@click="item.handler(popup.tab,item)"><i :class="item.icon"></i><span
v-text="item.text"></span>
</li>
</ul>
<el-dialog title="{% trans 'Change password' %}" :visible.sync="pwdDialog.show">
<iframe frameborder="0" :src="pwdDialog.url" width="100%" height="500"></iframe>
</el-dialog>
{% block theme_dialog %}
<el-dialog
:title="getLanuage('Change theme')"
:visible.sync="themeDialogVisible"
:width="small?'90%':'50%'">
{% block theme_body %}
<div class="change-theme clearfix">
<div v-waves :class="{'theme-item':true,active:themeName==item.text}" v-for="(item,i) in themes"
:key="item.text"
:title="getLanuage(item.text)" @click="setTheme(item)">
<div class="theme-menu" :style="{background:item.menu}">
<div class="theme-logo" :style="{background: item.logo}"></div>
</div>
<div class="theme-top" :style="{background: item.top}"></div>
</div>
</div>
{% endblock %}
</el-dialog>
{% endblock %}
{% block font_dialog %}
<el-dialog
:title="getLanuage('Set font size')"
:visible.sync="fontDialogVisible"
:width="small?'90%':'50%'">
<el-slider v-model="fontSlider" :min="12" :max="100" show-input @change="fontSlideChange"></el-slider>
<div style="text-align: right;padding-top: 20px">
<el-button type="primary" @click="reset()" v-text="getLanuage('Reset')"></el-button>
</div>
</el-dialog>
{% endblock %}
</div>
{% block base_script %}
<script type="text/javascript">
{% if home %}
var home = {
id: '0',
index: '1',
name: '{{ title }}',
icon: '{{ icon }}',
active: true,
eid: '1'
}
{% else %}
var home = {
id: '0',
index: '1',
eid: '1',
name: "{% trans 'Home' %}",
icon: 'fas fa-home'
};
{% endif %}
menus.unshift(home);
window.language = {
change_password: '{% trans 'Change password' %}',
logout: '{% trans 'Log out' %}',
yes: '{% trans 'Yes' %}',
no: '{% trans 'No' %}',
confirm: '{% trans 'Are you sure?' %}'
}
window.themeUrl = '{% static 'admin/simpleui-x/theme/' %}';
window.urls = {
changePassword: "{% url 'admin:password_change' %}",
logout: "{% url 'admin:logout' %}"
}
var lanuageCode = '{% get_language %}';
function localeError(e) {
console.warn("Please add the {% get_language %}.js language file to the '/simpleui-x/theme' directory.")
console.log("See simpleui i18n:https://github.com/newpanjing/simpleui/blob/master/i18n.md")
console.log("或者加入QQ群:786576510 (Join QQ Group.)")
}
</script>
{% include 'admin/includes/js-part.html' %}
<script type="text/javascript">
__simpleui_version = '{% simple_version %}';
</script>
<script type="text/javascript"
src="{% static 'admin/simpleui-x/automatic/dicts.js' %}?_={% get_version %}"></script>
<script type="text/javascript"
src="{% static 'admin/simpleui-x/automatic/segment.js' %}?_={% get_version %}"></script>
<script type="text/javascript" src="{% static 'admin/simpleui-x/locale/en-us.js' %}?_={% get_version %}"></script>
<script type="text/javascript"
src="{% static 'admin/simpleui-x/locale/' %}{% get_language %}.js?_={% get_version %}"
onerror="localeError()"></script>
<script type="text/javascript" src="{% static 'admin/simpleui-x/js/cookie.js' %}?_={% get_version %}"></script>
<script type="text/javascript" src="{% static 'admin/simpleui-x/theme/theme.js' %}?_={% get_version %}"></script>
<script type="text/javascript"
src="{% static 'admin/simpleui-x/waves/waves.min.js' %}?_={% get_version %}"></script>
{% block index_js %}
<script type="text/javascript" src="{% static 'admin/simpleui-x/js/menu.js' %}?_={% get_version %}"></script>
<script type="text/javascript" src="{% static 'admin/simpleui-x/js/index.js' %}?_={% get_version %}"></script>
{% endblock %}
{% block autoupdate %}
{% endblock %}
{% endblock %}
{% load_analysis %}
{% block script %}
{# 预留脚本block 可以实现基础该页面,添加自定义脚本#}
{% endblock %}
</body>
</html>
customtags↓
# -*- coding: utf-8 -*-
import base64
import json
import os
import sys
from django import template
from django.core.serializers.json import DjangoJSONEncoder
from django.utils.functional import Promise
try:
from django.utils.encoding import force_text
except:
from django.utils.encoding import force_str as force_text
register = template.Library()
PY_VER = sys.version[0] # 2 or 3
if PY_VER != '2':
from importlib import reload
from urllib.parse import parse_qsl
else:
from urlparse import parse_qsl
class LazyEncoder(DjangoJSONEncoder):
"""
解决json __proxy__ 问题
"""
def default(self, obj):
if isinstance(obj, Promise):
return force_text(obj)
return super(LazyEncoder, self).default(obj)
def unicode_to_str(u, encoding='utf-8'):
if PY_VER != '2':
return u
return u.encode(encoding)
@register.filter
def to_str(obj):
return str(obj)
def __get_config(name):
from django.conf import settings
value = os.environ.get(name, getattr(settings, name, None))
return value
@register.filter
def get_config(key):
return __get_config(key)
@register.simple_tag(takes_context=True)
def custom_menus(context, _get_config=None):
data = []
# print(context.request.user.has_perm("%s.%s" % (opts.app_label, codename))
if not _get_config:
_get_config = get_config
config = _get_config('SIMPLEUI_CONFIG')
if not config:
config = {}
if config.get('dynamic', False) is True:
config = _import_reload(_get_config('DJANGO_SETTINGS_MODULE')).SIMPLEUI_CONFIG
app_list = context.get('app_list')
for app in app_list:
_models = [
{
'name': m.get('name'),
'icon': get_icon(m.get('object_name'), unicode_to_str(m.get('name'))),
'url': m.get('admin_url'),
'addUrl': m.get('add_url'),
'breadcrumbs': [{
'name': app.get('name'),
'icon': get_icon(app.get('app_label'), app.get('name'))
}, {
'name': m.get('name'),
'icon': get_icon(m.get('object_name'), unicode_to_str(m.get('name')))
}]
}
for m in app.get('models')
] if app.get('models') else []
module = {
'name': app.get('name'),
'icon': get_icon(app.get('app_label'), app.get('name')),
'models': _models
}
data.append(module)
# 如果有menu 就读取,没有就调用系统的
key = 'system_keep'
if config and 'menus' in config:
if config.get(key, None):
temp = config.get('menus')
_temp = temp[:]
for i in _temp:
if 'models' not in i:
if context.request.user.has_perm("%s.%s" % (i.get("name"), i.get("codename"))):
i['breadcrumbs'] = [{
'name': i.get('name'),
'icon': i.get('icon')
}]
else:
temp.remove(i)
else:
_i = i.get('models')[:]
for k in _i:
if context.request.user.has_perm("%s.%s" % (k.get("name"), k.get("codename"))):
k['breadcrumbs'] = [{
'name': i.get('name'),
'icon': i.get('icon')
}, {
'name': k.get('name'),
'icon': k.get('icon')
}]
else:
i.get("models").remove(k)
if not i.get("models"):
temp.remove(i)
for i in temp:
data.append(i)
else:
data = config.get('menus')
# 获取侧边栏排序, 如果设置了就按照设置的内容排序, 留空则表示默认排序以及全部显示
if config.get('menu_display') is not None:
display_data = list()
for _app in data:
if _app['name'] not in config.get('menu_display'):
continue
_app['_weight'] = config.get('menu_display').index(_app['name'])
display_data.append(_app)
display_data.sort(key=lambda x: x['_weight'])
data = display_data
# 给每个菜单增加一个唯一标识,用于tab页判断
eid = 1000
handler_eid(data, eid)
menus_string = json.dumps(data, cls=LazyEncoder)
# 把data放入session中,其他地方可以调用
if not isinstance(context, dict) and context.request:
context.request.session['_menus'] = menus_string
return '<script type="text/javascript">var menus={}</script>'.format(menus_string)
def handler_eid(data, eid):
for i in data:
eid += 1
i['eid'] = eid
if 'models' in i:
eid = handler_eid(i.get('models'), eid)
return eid
def get_icon(obj, name=None):
temp = get_config_icon(name)
if temp != '':
return temp
_dict = {
'auth': 'fas fa-shield-alt',
'User': 'far fa-user',
'Group': 'fas fa-users-cog'
}
temp = _dict.get(obj)
if not temp:
return 'far fa-circle'
return temp
# 从配置中读取图标
def get_config_icon(name):
_config_icon = __get_config('SIMPLEUI_ICON')
if _config_icon is None:
return ''
if name in _config_icon:
return _config_icon.get(name)
return ''
def _import_reload(_modules):
_obj = __import__(_modules, fromlist=_modules.split('.'))
reload(_obj)
return _obj
index.html路径一定要按照第三张截图所示配置,customtags.py文件可以放在任意APP目录下的templatetags目录下