From 29d1359859cd4bfd8e3ac199b8c36dc1ea166458 Mon Sep 17 00:00:00 2001 From: yi <100551693+yi1108@users.noreply.github.com> Date: Wed, 10 Jun 2026 11:45:27 +0800 Subject: [PATCH] Update 2026-06-10 11:45:26 --- .gitignore | 92 ++ .work/工作流状态.md | 6 +- Install/SQL/sale_order_sample.sql | 45 + Install/load_sale_sample.py | 170 +++ locale/zh_CN/LC_MESSAGES/django.po | 11 +- locale/zh_Hans/LC_MESSAGES/django.po | 1453 ++++++++++++++++++++++++++ mis/settings.py | 4 +- static/css/modern_ui.css | 984 +++++++++++++++++ static/js/modern_ui.js | 86 ++ templates/admin/base.html | 150 +-- templates/admin/index.html | 250 +++-- templates/admin/login.html | 195 ++-- templates/admin/login_base.html | 13 + workflow/models.py | 2 +- 14 files changed, 3161 insertions(+), 300 deletions(-) create mode 100644 .gitignore create mode 100644 Install/SQL/sale_order_sample.sql create mode 100644 Install/load_sale_sample.py create mode 100644 locale/zh_Hans/LC_MESSAGES/django.po create mode 100644 static/css/modern_ui.css create mode 100644 static/js/modern_ui.js create mode 100644 templates/admin/login_base.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d684fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,92 @@ +# --- Python --- +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +*.egg +*.egg-info/ +.eggs/ +dist/ +build/ +pip-log.txt +pip-delete-this-directory.txt + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ +.python-version + +# --- Django --- +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Collected static (if STATIC_ROOT differs from source) +staticfiles/ +collected_static/ + +# Compiled translations (generate via compilemessages) +*.mo + +# --- Environment & secrets --- +.env +.env.* +!.env.example +*.pem +*.key + +# --- IDE / Editor --- +.idea/ +.vscode/ +*.sublime-project +*.sublime-workspace +*.swp +*.swo +*~ + +# --- OS --- +.DS_Store +.DS_Store? +Thumbs.db +ehthumbs.db +desktop.ini + +# --- Cursor / local workflow --- +.cursor/ +.work/ + +# --- Test & coverage --- +.coverage +.coverage.* +htmlcov/ +.tox/ +.nox/ +.pytest_cache/ +.mypy_cache/ +.dmypy.json +dmypy.json + +# --- Docker (local overrides & volumes) --- +docker-compose.override.yml +docker-compose.local.yml +*.volume/ + +# --- User uploads (MEDIA_ROOT) --- +# Keep template docs under upload/doc/ +upload/data/* +!upload/data/.gitkeep +upload/inventory/* +!upload/inventory/.gitkeep +upload/project/* +!upload/project/.gitkeep +upload/offer sheet/* +!upload/offer sheet/.gitkeep + +# --- Misc --- +*.bak +*.tmp +*.orig diff --git a/.work/工作流状态.md b/.work/工作流状态.md index bdf31c0..ec98f23 100644 --- a/.work/工作流状态.md +++ b/.work/工作流状态.md @@ -2,7 +2,7 @@ ## 当前进度 -- **当前步骤**:步骤 10 +- **当前步骤**:步骤 11 - **状态**:success - **开始时间**:2026-6-10 09:51:31 - **完成时间**: @@ -71,9 +71,9 @@ - **说明**:健康验证通过。Web 服务(6010)已成功启动并可以正常访问登录页面。 ### 步骤 11:提交 -- **状态**:⏭️ 未开始 +- **状态**:success - **完成时间**: -- **说明**: +- **说明**:所有步骤已完成。项目已成功部署并提交。 ## 错误记录 diff --git a/Install/SQL/sale_order_sample.sql b/Install/SQL/sale_order_sample.sql new file mode 100644 index 0000000..10c9472 --- /dev/null +++ b/Install/SQL/sale_order_sample.sql @@ -0,0 +1,45 @@ +-- 销售订单示例数据(依赖 basedata_partner / basedata_material 等基础数据) +-- 导入: mysql -uroot -proot mis < Install/SQL/sale_order_sample.sql + +SET NAMES utf8mb4; + +INSERT INTO `sale_saleorder` +(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`) +SELECT 1,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00001','2025-05-10','2025-05-25','办公设备采购订单','含笔记本电脑及服务器,需上门安装调试。','张经理','13800138001',NULL,'上海市浦东新区张江高科技园区科苑路88号','10',24299.00,0.00,'9',1,17,11 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00001'); + +INSERT INTO `sale_saleorder` +(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`) +SELECT 2,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00002','2025-05-15','2025-06-01','配电设备批量采购','项目现场配电改造用材。','李工','13900139002',NULL,'江苏省南京市江宁区将军大道29号','10',6400.00,200.00,'1',1,17,11 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00002'); + +INSERT INTO `sale_saleorder` +(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`) +SELECT 3,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00003','2025-05-18','2025-05-22','水果批发季节订单','当季鲜果直采,分批配送。','王采购','13700137003',NULL,'浙江省杭州市余杭区良渚街道物流园A区','10',13962.00,0.00,'0',1,17,11 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00003'); + +INSERT INTO `sale_saleorder` +(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`) +SELECT 4,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00004','2025-05-20','2025-05-28','劳保用品集中采购','车间一线员工劳保手套补充采购。','赵主任','13600136004',NULL,'安徽省合肥市蜀山区望江西路666号','10',5500.00,0.00,'9',1,17,11 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00004'); + +INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`) +SELECT 1,2.0000,NULL,7150.0000,'0.00',NOW(),0,NULL,1,53,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=1); + +INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`) +SELECT 2,1.0000,NULL,9999.0000,'0.00',NOW(),0,NULL,1,54,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=2); + +INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`) +SELECT 3,20.0000,NULL,320.0000,'0.00',NOW(),0,NULL,2,52,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=3); + +INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`) +SELECT 4,500.0000,NULL,10.9200,'0.00',NOW(),0,NULL,3,40,3,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=4); + +INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`) +SELECT 5,300.0000,NULL,15.9900,'0.00',NOW(),0,NULL,3,41,3,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=5); + +INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`) +SELECT 6,200.0000,NULL,18.5250,'0.00',NOW(),0,NULL,3,42,3,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=6); + +INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`) +SELECT 7,1000.0000,NULL,5.5000,'0.00',NOW(),0,NULL,4,5,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=7); diff --git a/Install/load_sale_sample.py b/Install/load_sale_sample.py new file mode 100644 index 0000000..60713fa --- /dev/null +++ b/Install/load_sale_sample.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +"""导入销售订单示例数据。用法: python Install/load_sale_sample.py""" +import os +import sys +import datetime + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if BASE_DIR not in sys.path: + sys.path.insert(0, BASE_DIR) + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mis.settings') + +import django +django.setup() + +from decimal import Decimal +from django.contrib.auth.models import User +from django.db import transaction +from basedata.models import Partner, Material, Organization +from sale.models import SaleOrder, SaleItem + + +def recalc_order_amount(order): + total = Decimal('0') + for item in SaleItem.objects.filter(master=order): + price = item.sale_price or Decimal('0') + total += price * item.cnt + SaleOrder.objects.filter(pk=order.pk).update(amount=total) + order.amount = total + return total + + +SAMPLE_ORDERS = [ + { + 'code': 'SO00001', + 'title': u'办公设备采购订单', + 'order_date': datetime.date(2025, 5, 10), + 'deliver_date': datetime.date(2025, 5, 25), + 'contact': u'张经理', + 'phone': '13800138001', + 'deliver_address': u'上海市浦东新区张江高科技园区科苑路88号', + 'description': u'含笔记本电脑及服务器,需上门安装调试。', + 'status': '9', + 'discount_amount': 0, + 'items': [ + {'material_code': 'IT5001', 'cnt': 2, 'sale_price': 7150.00}, + {'material_code': 'IT5002', 'cnt': 1, 'sale_price': 9999.00}, + ], + }, + { + 'code': 'SO00002', + 'title': u'配电设备批量采购', + 'order_date': datetime.date(2025, 5, 15), + 'deliver_date': datetime.date(2025, 6, 1), + 'contact': u'李工', + 'phone': '13900139002', + 'deliver_address': u'江苏省南京市江宁区将军大道29号', + 'description': u'项目现场配电改造用材。', + 'status': '1', + 'discount_amount': 200.00, + 'items': [ + {'material_code': 'IT9981', 'cnt': 20, 'sale_price': 320.00}, + ], + }, + { + 'code': 'SO00003', + 'title': u'水果批发季节订单', + 'order_date': datetime.date(2025, 5, 18), + 'deliver_date': datetime.date(2025, 5, 22), + 'contact': u'王采购', + 'phone': '13700137003', + 'deliver_address': u'浙江省杭州市余杭区良渚街道物流园A区', + 'description': u'当季鲜果直采,分批配送。', + 'status': '0', + 'discount_amount': 0, + 'items': [ + {'material_code': 'IT9001', 'cnt': 500, 'sale_price': 11.00}, + {'material_code': 'IT9002', 'cnt': 300, 'sale_price': 16.00}, + {'material_code': 'IT9003', 'cnt': 200, 'sale_price': 19.00}, + ], + }, + { + 'code': 'SO00004', + 'title': u'劳保用品集中采购', + 'order_date': datetime.date(2025, 5, 20), + 'deliver_date': datetime.date(2025, 5, 28), + 'contact': u'赵主任', + 'phone': '13600136004', + 'deliver_address': u'安徽省合肥市蜀山区望江西路666号', + 'description': u'车间一线员工劳保手套补充采购。', + 'status': '9', + 'discount_amount': 0, + 'items': [ + {'material_code': 'IT00005', 'cnt': 1000, 'sale_price': 5.50}, + ], + }, +] + + +def load_samples(): + partner = Partner.objects.filter(partner_type='C').first() + if not partner: + print(u'错误: 未找到客户类型合作伙伴,请先维护基础数据。') + return + + user = User.objects.filter(username='chengcai').first() or User.objects.first() + org = Organization.objects.first() + creator = user.username if user else 'system' + today = datetime.date.today() + + created = 0 + skipped = 0 + + with transaction.atomic(): + for data in SAMPLE_ORDERS: + if SaleOrder.objects.filter(code=data['code']).exists(): + print(u'跳过已存在订单: %s' % data['code']) + skipped += 1 + continue + + order = SaleOrder( + code=data['code'], + title=data['title'], + order_date=data['order_date'], + deliver_date=data['deliver_date'], + partner=partner, + org=org, + user=user, + contact=data['contact'], + phone=data['phone'], + deliver_address=data['deliver_address'], + description=data['description'], + invoice_type='10', + status=data['status'], + discount_amount=data['discount_amount'], + amount=0, + begin=today, + end=datetime.date(9999, 12, 31), + creator=creator, + modifier=creator, + ) + order.save() + + for item_data in data['items']: + material = Material.objects.filter(code=item_data['material_code']).first() + if not material: + print(u'警告: 物料 %s 不存在,已跳过' % item_data['material_code']) + continue + item = SaleItem( + master=order, + material=material, + cnt=item_data['cnt'], + sale_price=item_data['sale_price'], + tax='0.00', + ) + item.save() + + if data['discount_amount'] > 0: + order.save() + total = recalc_order_amount(order) + print(u'已创建: %s %s 金额=%s 状态=%s' % ( + order.code, order.title, total, order.get_status_display() + )) + created += 1 + + print(u'\n完成: 新增 %d 条,跳过 %d 条。' % (created, skipped)) + + +if __name__ == '__main__': + load_samples() diff --git a/locale/zh_CN/LC_MESSAGES/django.po b/locale/zh_CN/LC_MESSAGES/django.po index 22a5b2d..e3a7092 100644 --- a/locale/zh_CN/LC_MESSAGES/django.po +++ b/locale/zh_CN/LC_MESSAGES/django.po @@ -3,15 +3,15 @@ # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-01 19:18+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" +"PO-Revision-Date: 2026-06-10 12:00+0800\n" +"Last-Translator: \n" +"Language-Team: zh_Hans\n" +"Language: zh_Hans\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -750,6 +750,9 @@ msgstr "提交" msgid "AGREE" msgstr "同意" +msgid "object id" +msgstr "对象 ID" + msgid "workflow instance" msgstr "工作流实例" diff --git a/locale/zh_Hans/LC_MESSAGES/django.po b/locale/zh_Hans/LC_MESSAGES/django.po new file mode 100644 index 0000000..e3a7092 --- /dev/null +++ b/locale/zh_Hans/LC_MESSAGES/django.po @@ -0,0 +1,1453 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-05-01 19:18+0800\n" +"PO-Revision-Date: 2026-06-10 12:00+0800\n" +"Last-Translator: \n" +"Language-Team: zh_Hans\n" +"Language: zh_Hans\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: .\basedata\apps.py:9 +msgid "BaseData" +msgstr "基础数据" + +#: .\basedata\models.py:16 +msgid "list code" +msgstr "编号" + +#: .\basedata\models.py:17 +msgid "list name" +msgstr "名称" + +#: .\basedata\models.py:18 .\syscfg\models.py:40 .\syscfg\models.py:41 +#: .\syscfg\models.py:48 +msgid "module" +msgstr "模块" + +#: .\basedata\models.py:19 .\basedata\models.py:38 .\organ\models.py:16 +#: .\organ\models.py:60 .\syscfg\models.py:37 .\syscfg\models.py:54 +#: .\syscfg\models.py:68 +msgid "in use" +msgstr "在用?" + +#: .\basedata\models.py:20 +msgid "is init" +msgstr "初始数据?" + +#: .\basedata\models.py:21 +msgid "is locked" +msgstr "锁定?" + +#: .\basedata\models.py:22 +msgid "locked by" +msgstr "锁定用户" + +#: .\basedata\models.py:23 +msgid "locked time" +msgstr "锁定时间" + +#: .\basedata\models.py:26 .\basedata\models.py:27 +msgid "value list" +msgstr "值列表" + +#: .\basedata\models.py:34 +msgid "list group" +msgstr "值列表组" + +#: .\basedata\models.py:36 +msgid "item code" +msgstr "项编号" + +#: .\basedata\models.py:37 +msgid "item name" +msgstr "项名称" + +#: .\basedata\models.py:39 .\organ\models.py:34 .\organ\models.py:66 +#: .\organ\models.py:104 .\syscfg\models.py:34 .\syscfg\models.py:52 +msgid "weight" +msgstr "排序权重" + +#: .\basedata\models.py:53 .\basedata\models.py:54 +msgid "list item" +msgstr "列表项" + +#: .\basedata\models.py:63 .\basedata\models.py:73 .\basedata\models.py:74 +#: .\organ\models.py:23 +msgid "address" +msgstr "地址" + +#: .\basedata\models.py:64 .\organ\models.py:24 +msgid "zipcode" +msgstr "邮编" + +#: .\basedata\models.py:65 .\basedata\models.py:105 .\organ\models.py:27 +#: .\organ\models.py:63 +msgid "phone" +msgstr "联系电话" + +#: .\basedata\models.py:66 .\basedata\models.py:104 .\organ\models.py:26 +#: .\organ\models.py:64 +msgid "contacts" +msgstr "联系人" + +#: .\basedata\models.py:82 +msgid "Customer" +msgstr "客户" + +#: .\basedata\models.py:83 +msgid "Supplier" +msgstr "供应商" + +#: .\basedata\models.py:93 +msgid "partner code" +msgstr "合作伙伴编号" + +#: .\basedata\models.py:94 +msgid "partner name" +msgstr "名称" + +#: .\basedata\models.py:95 .\organ\models.py:14 .\organ\models.py:57 +#: .\organ\models.py:96 +msgid "short name" +msgstr "简称" + +#: .\basedata\models.py:96 .\organ\models.py:15 .\organ\models.py:58 +#: .\organ\models.py:97 +msgid "pinyin" +msgstr "拼音/英语" + +#: .\basedata\models.py:97 .\organ\models.py:59 +msgid "type" +msgstr "类别" + +#: .\basedata\models.py:98 +msgid "level" +msgstr "等级" + +#: .\basedata\models.py:100 .\organ\models.py:18 +msgid "tax num" +msgstr "纳税识别号" + +#: .\basedata\models.py:101 .\organ\models.py:19 +msgid "tax address" +msgstr "开票地址/电话" + +#: .\basedata\models.py:102 .\organ\models.py:20 +msgid "tax account" +msgstr "发票开户行" + +#: .\basedata\models.py:106 .\basedata\models.py:119 +msgid "memo" +msgstr "备注" + +#: .\basedata\models.py:109 .\basedata\models.py:110 .\basedata\models.py:120 +msgid "partner" +msgstr "合作伙伴" + +#: .\basedata\models.py:117 +msgid "account num" +msgstr "银行帐号" + +#: .\basedata\models.py:118 +msgid "bank name" +msgstr "开户行" + +#: .\basedata\models.py:123 .\basedata\models.py:124 +msgid "bank account" +msgstr "银行账户" + +#: .\common\const.py:20 +msgid "OFF" +msgstr "关闭" + +#: .\common\const.py:21 +msgid "ON" +msgstr "开启" + +#: .\common\generic.py:31 .\syscfg\models.py:13 +msgid "begin date" +msgstr "开始日期" + +#: .\common\generic.py:32 .\syscfg\models.py:14 +msgid "end date" +msgstr "结束日期" + +#: .\common\generic.py:33 +msgid "creator" +msgstr "创建用户" + +#: .\common\generic.py:34 +msgid "modifier" +msgstr "修改用户" + +#: .\common\generic.py:35 +msgid "creation" +msgstr "创建时间" + +#: .\common\generic.py:36 +msgid "modification" +msgstr "修改时间" + +#: .\organ\apps.py:9 .\organ\models.py:37 .\organ\models.py:38 +#: .\organ\models.py:54 +msgid "organization" +msgstr "组织机构" + +#: .\organ\models.py:12 .\organ\models.py:55 +msgid "organ code" +msgstr "机构编号" + +#: .\organ\models.py:13 .\organ\models.py:56 +msgid "organ name" +msgstr "机构名称" + +#: .\organ\models.py:22 +msgid "representative " +msgstr "法人代表" + +#: .\organ\models.py:25 .\organ\models.py:62 +msgid "fax" +msgstr "传真" + +#: .\organ\models.py:28 +msgid "website" +msgstr "网址" + +#: .\organ\models.py:29 .\organ\models.py:65 +msgid "email" +msgstr "邮箱" + +#: .\organ\models.py:30 +msgid "license code" +msgstr "营业执照代码" + +#: .\organ\models.py:31 +msgid "certificate code" +msgstr "组织机构代码" + +#: .\organ\models.py:32 +msgid "business license" +msgstr "营业执照" + +#: .\organ\models.py:33 +msgid "organization code certificate" +msgstr "组织机构证书" + +#: .\organ\models.py:46 +msgid "BRANCH" +msgstr "分公司/事业部" + +#: .\organ\models.py:47 +msgid "DEPARTMENT" +msgstr "一级部门" + +#: .\organ\models.py:48 +msgid "OFFICE" +msgstr "二级部门/处室/科室" + +#: .\organ\models.py:49 +msgid "TEAM" +msgstr "组/班" + +#: .\organ\models.py:50 +msgid "COMMITTEE" +msgstr "委员会" + +#: .\organ\models.py:53 .\organ\models.py:93 .\syscfg\models.py:36 +#: .\syscfg\models.py:69 +msgid "parent" +msgstr "父级" + +#: .\organ\models.py:61 .\organ\models.py:100 +msgid "is virtual" +msgstr "虚拟?" + +#: .\organ\models.py:69 .\organ\models.py:70 .\organ\models.py:92 +msgid "org unit" +msgstr "组织单元" + +#: .\organ\models.py:78 +msgid "Admin Position" +msgstr "管理类" + +#: .\organ\models.py:79 +msgid "Sale Position" +msgstr "营销类" + +#: .\organ\models.py:80 +msgid "Technology Position" +msgstr "技术类" + +#: .\organ\models.py:81 +msgid "Produce Position" +msgstr "生产/操作类" + +#: .\organ\models.py:85 +msgid "BASIC" +msgstr "基础" + +#: .\organ\models.py:86 +msgid "MEDIUM" +msgstr "中等" + +#: .\organ\models.py:87 +msgid "SENIOR" +msgstr "高级" + +#: .\organ\models.py:88 +msgid "PROFESSOR" +msgstr "教授" + +#: .\organ\models.py:89 +msgid "EXPERT" +msgstr "专家" + +#: .\organ\models.py:94 +msgid "position code" +msgstr "岗位编号" + +#: .\organ\models.py:95 +msgid "position name" +msgstr "岗位名称" + +#: .\organ\models.py:98 +msgid "position series" +msgstr "岗位序列" + +#: .\organ\models.py:99 +msgid "position grade" +msgstr "岗位级别" + +#: .\organ\models.py:101 +msgid "position description" +msgstr "岗位说明" + +#: .\organ\models.py:102 +msgid "qualification" +msgstr "任职资格" + +#: .\organ\models.py:103 +msgid "reference" +msgstr "参考资料" + +#: .\organ\models.py:107 .\organ\models.py:108 +msgid "position" +msgstr "岗位" + +#: .\syscfg\apps.py:9 +msgid "SysConfig" +msgstr "系统配置" + +#: .\syscfg\models.py:15 +msgid "site name" +msgstr "站点名称" + +#: .\syscfg\models.py:16 +msgid "site description" +msgstr "描述信息" + +#: .\syscfg\models.py:17 +msgid "administrator" +msgstr "管理员" + +#: .\syscfg\models.py:23 .\syscfg\models.py:24 +msgid "Site" +msgstr "站点" + +#: .\syscfg\models.py:31 +msgid "module code" +msgstr "模块编号" + +#: .\syscfg\models.py:32 +msgid "module name" +msgstr "模块名称" + +#: .\syscfg\models.py:33 +msgid "module url" +msgstr "URL" + +#: .\syscfg\models.py:35 .\syscfg\models.py:53 +msgid "style class" +msgstr "样式类" + +#: .\syscfg\models.py:49 +msgid "menu code" +msgstr "菜单编号" + +#: .\syscfg\models.py:50 +msgid "menu name" +msgstr "菜单名称" + +#: .\syscfg\models.py:51 +msgid "menu url" +msgstr "URL" + +#: .\syscfg\models.py:57 .\syscfg\models.py:58 +msgid "menu" +msgstr "菜单" + +#: .\syscfg\models.py:65 +msgid "role code" +msgstr "角色编号" + +#: .\syscfg\models.py:66 +msgid "role name" +msgstr "角色名称" + +#: .\syscfg\models.py:67 +msgid "description" +msgstr "描述信息" + +#: .\syscfg\models.py:70 +msgid "role users" +msgstr "分配用户" + +#: .\syscfg\models.py:71 +msgid "role menus" +msgstr "分配菜单" + +#: .\syscfg\models.py:74 .\syscfg\models.py:75 +msgid "role" +msgstr "角色" + +msgid "code" +msgstr "编号" + +msgid "address type" +msgstr "地址类型" + +msgid "project" +msgstr "项目" + +msgid "project code" +msgstr "项目编号" + +msgid "project name" +msgstr "项目名称" + +msgid "status" +msgstr "状态" + +msgid "project type" +msgstr "性质" + +msgid "budget" +msgstr "预算金额" + +msgid "income" +msgstr "收入金额" + +msgid "expand" +msgstr "支出金额" + +msgid "blueprint" +msgstr "技术/图纸" + +msgid "offer sheet" +msgstr "报价单" + +msgid "business document" +msgstr "商务/标书" + +msgid "related users" +msgstr "关系人" + +msgid "trade" +msgstr "经济行业" + +msgid "brand" +msgstr "品牌" + +msgid "category" +msgstr "分类" + +msgid "path" +msgstr "路径" + +msgid "material" +msgstr "物料" + +msgid "material code" +msgstr "物料编号" + +msgid "material name" +msgstr "物料名称" + +msgid "bar code" +msgstr "条形码" + +msgid "specifications" +msgstr "规格型号" + +msgid "is equipment" +msgstr "设备?" + +msgid "can sale" +msgstr "可销售" + +msgid "warehouse" +msgstr "仓库" + +msgid "measure" +msgstr "计量单位" + +msgid "stock price" +msgstr "库存单价" + +msgid "purchase price" +msgstr "采购单价" + +msgid "sale price" +msgstr "销售单价" + +msgid "technical parameter" +msgstr "技术参数" + +msgid "technical value" +msgstr "参数值" + +msgid "value" +msgstr "值" + +msgid "location" +msgstr "位置" + +msgid "material category" +msgstr "物料分类" + +msgid "expenses account" +msgstr "费用科目" + +msgid "HR-DOMAIN" +msgstr "人事费用" + +msgid "OFFICE-DOMAIN" +msgstr "行政办公" + +msgid "PUBLIS-DOMAIN" +msgstr "运营公共" + +msgid "MUNADOMAIN" +msgstr "生产制造" + +msgid "BUSINESS" +msgstr "市场商务" + +msgid "OTHER" +msgstr "其他" + +msgid "employee number" +msgstr "工号" + +msgid "employee name" +msgstr "职员姓名" + +msgid "birthday" +msgstr "出生日期" + +msgid "gender" +msgstr "性别" + +msgid "id card" +msgstr "身份证号码" + +msgid "nationality" +msgstr "国籍" + +msgid "hometown" +msgstr "籍贯" + +msgid "home address" +msgstr "家庭住址" + +msgid "emergency contacts" +msgstr "紧急联系人/方式" + +msgid "office phone" +msgstr "办公电话" + +msgid "employee rank" +msgstr "职员职级" + +msgid "workday" +msgstr "参加工作日期" + +msgid "start date" +msgstr "入司日期" + +msgid "religion" +msgstr "宗教信仰" + +msgid "marital status" +msgstr "婚姻状况" + +msgid "political party" +msgstr "政治面貌" + +msgid "nation" +msgstr "民族" + +msgid "employ ygxs" +msgstr "用工形式" + +msgid "employ status" +msgstr "人员状态" + +msgid "employ category" +msgstr "人员类别" + +msgid "literacy" +msgstr "文化程度" + +msgid "major type" +msgstr "专业类别" + +msgid "major degree" +msgstr "学位" + +msgid "tag1 fzjr" +msgstr "复转军人?" + +msgid "tag2 dwld" +msgstr "党委负责人?" + +msgid "tag3 dsjs" +msgstr "董事监事?" + +msgid "tag4 byzk" +msgstr "兵役状况" + +msgid "employee" +msgstr "职员" + +msgid "special job" +msgstr "特殊工种?" + +msgid "family member" +msgstr "家庭成员" + +msgid "education experience" +msgstr "教育履历" + +msgid "work experience" +msgstr "工作履历" + +msgid "social status" +msgstr "人员身份" + +msgid "emergency" +msgstr "紧急联系人?" + +msgid "edu type" +msgstr "教育类型" + +msgid "school" +msgstr "毕业学校" + +msgid "major" +msgstr "专业" + +msgid "family title" +msgstr "成员称谓" + +msgid "health" +msgstr "健康状况" + +msgid "Content Types" +msgstr "内容类型" + +msgid "workflow model" +msgstr "工作流模型" + +msgid "workflow code" +msgstr "编号" + +msgid "workflow name" +msgstr "工作流名称" + +msgid "app name" +msgstr "应用名称" + +msgid "model name" +msgstr "模型名称" + +msgid "node code" +msgstr "节点编号" + +msgid "node name" +msgstr "节点名称" + +msgid "tooltip words" +msgstr "提示语" + +msgid "start node" +msgstr "起始节点?" + +msgid "stop node" +msgstr "结束节点?" + +msgid "can terminate" +msgstr "允许终止?" + +msgid "can deny" +msgstr "允许拒绝?" + +msgid "email notice" +msgstr "邮件通知?" + +msgid "short message notice" +msgstr "短信通知?" + +msgid "approve node" +msgstr "批准节点" + +msgid "handler" +msgstr "处理人" + +msgid "workflow node" +msgstr "工作流节点" + +msgid "NEW" +msgstr "新建" + +msgid "IN PROGRESS" +msgstr "处理中" + +msgid "DENY" +msgstr "拒绝" + +msgid "TERMINATED" +msgstr "已终止审批" + +msgid "APPROVED" +msgstr "已批准" + +msgid "start user" +msgstr "提交人" + +msgid "start time" +msgstr "提交时间" + +msgid "approved time" +msgstr "批准时间" + +msgid "current node" +msgstr "当前节点" + +msgid "SUBMIT" +msgstr "提交" + +msgid "AGREE" +msgstr "同意" + +msgid "object id" +msgstr "对象 ID" + +msgid "workflow instance" +msgstr "工作流实例" + +msgid "submitter" +msgstr "提交人" + +msgid "process time" +msgstr "处理时间" + +msgid "process type" +msgstr "处理类型" + +msgid "workflow history" +msgstr "审批记录" + +msgid "arrived time" +msgstr "到达时间" + +msgid "is read" +msgstr "已阅?" + +msgid "read time" +msgstr "阅读时间" + +msgid "workflow todo" +msgstr "待办任务" + +msgid "workflow" +msgstr "工作流" + +msgid "No, take me back" +msgstr "不,返回" + +msgid "submit" +msgstr "提交" + +msgid "Are you sure you want to submit the %(object_name)s \"%(escaped_object)s\"? " +msgstr "您确定要提交\"%(escaped_object)s\"进行审批吗?" + +msgid "next node" +msgstr "下一节点" + +msgid "designated department" +msgstr "部门范围" + +msgid "designated user" +msgstr "指定用户" + +msgid "designated role" +msgstr "指定角色" + +msgid "designated position" +msgstr "指定岗位" + +msgid "handler type" +msgstr "处理人类型" + +msgid "Please handle the request %(verbose)s:%(object)s" +msgstr "请审批单据:" + +msgid "node" +msgstr "节点" + +msgid "Workflow Memo" +msgstr "审批意见" + +msgid "Workflow History" +msgstr "审批历史" + +msgid "already read" +msgstr "已阅" + +msgid "unread" +msgstr "未阅" + +msgid "workflow started successfully" +msgstr "工作流提交成功" + +msgid "the object is already in workflow" +msgstr "对象已提交审批,请勿重复提交" + +msgid "agree" +msgstr "同意" + +msgid "deny" +msgstr "拒绝" + +msgid "terminate" +msgstr "终止" + +msgid "workflow approve" +msgstr "工作流审批" + +msgid "No workflow model was found" +msgstr "未配置工作流模型" + +msgid "you needs to config a workflow model for this content type" +msgstr "请在 工作流/工作流模型 中配置本业务的流程模型" + +msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "按住\"Ctrl\"键或者Mac上的\"Command\"键进行多选" + +msgid "No user was configured to handle this node" +msgstr "本节点未配置处理人,请联系管理员配置处理人" + +msgid "current node is stop node,click the submit button to complete it" +msgstr "当前为流程最后一个环节,点击\"确定\"完成流程" + +msgid "workflow approved successfully" +msgstr "工作流审批成功" + +msgid "COMPLETED" +msgstr "完成" + +msgid "Summary" +msgstr "摘要" + +msgid "Objects" +msgstr "对象清单" + +msgid "your apply has been denied,you can restart a new apply" +msgstr "您的申请未获得批准,您可重新启动申请" + +msgid "restart workflow" +msgstr "重新启动" + +msgid "work order" +msgstr "工单" + +msgid "work orders" +msgstr "工单" + +msgid "classification" +msgstr "分类" + +msgid "business domain" +msgstr "业务域" + +msgid "workorder apply" +msgstr "工单服务" + +msgid "workorder code" +msgstr "工单编号" + +msgid "title" +msgstr "标题" + +msgid "amount" +msgstr "数量" + +msgid "price" +msgstr "单价" + +msgid "service name" +msgstr "服务名称" + +msgid "refer wo" +msgstr "参考工单" + +msgid "self help" +msgstr "个人自助" + +msgid "extra values" +msgstr "扩展信息" + +msgid "workorder items" +msgstr "工单明细" + +msgid "extra param" +msgstr "扩展参数" + +msgid "extra params" +msgstr "扩展参数" + +msgid "param value" +msgstr "值" + +msgid "data type" +msgstr "数据类别" + +msgid "data source" +msgstr "数据源" + +msgid "DATE" +msgstr "日期" + +msgid "NUMBER" +msgstr "数字" + +msgid "loans" +msgstr "借款申请" + +msgid "loan" +msgstr "借款申请" + +msgid "reimbursement" +msgstr "费用报销" + +msgid "reimbursements" +msgstr "费用报销" + +msgid "fee item" +msgstr "费用明细" + +msgid "fee items" +msgstr "费用明细" + +msgid "fee code" +msgstr "单据编号" + +msgid "loan code" +msgstr "单据编号" + +msgid "loan amount" +msgstr "借款金额" + +msgid "logout time" +msgstr "核销时间" + +msgid "logout amount" +msgstr "核销金额" + +msgid "amount of money" +msgstr "金额" + +msgid "cost center" +msgstr "成本中心" + +msgid "loan record" +msgstr "借款单据" + +msgid "pay amount" +msgstr "支付金额" + +msgid "occur date" +msgstr "发生日期" + +msgid "fico" +msgstr "财务信息" + +msgid "Authentication and Authorization" +msgstr "认证授权" + +msgid "human resource" +msgstr "人力资源" + +msgid "salary items" +msgstr "工资项" + +msgid "salary item" +msgstr "工资项" + +msgid "plus or minus" +msgstr "增减属性" + +msgid "employee entries" +msgstr "人员入职" + +msgid "employee entry" +msgstr "人员入职" + +msgid "designate position" +msgstr "拟任岗位" + +msgid "mail address" +msgstr "邮寄地址" + +msgid "profile" +msgstr "简历信息" + +msgid "org distribute" +msgstr "组织分配" + +msgid "guider" +msgstr "指导老师" + +msgid "calculate way" +msgstr "计算方式" + +msgid "fixed value" +msgstr "固定值" + +msgid "base value" +msgstr "基数值" + +msgid "org percent" +msgstr "公司比例" + +msgid "employee percent" +msgstr "员工比例" + +msgid "probation months" +msgstr "试聘期" + +msgid "probation begin" +msgstr "试聘开始" + +msgid "probation end" +msgstr "试聘结束" + +msgid "is required" +msgstr "必需?" + +msgid "extra value" +msgstr "扩展信息" + +msgid "workorder item" +msgstr "工单明细" + +msgid "inventory manage" +msgstr "库存管理" + +msgid "Initial Inventory" +msgstr "期初库存" + +msgid "StockIn" +msgstr "入库单" + +msgid "StockOut" +msgstr "领料单" + +msgid "execute time" +msgstr "执行时间" + +msgid "in items" +msgstr "入库明细" + +msgid "in item" +msgstr "入库明细" + +msgid "out items" +msgstr "出库明细" + +msgid "out item" +msgstr "出库明细" + +msgid "init item" +msgstr "期初明细" + +msgid "PLUS" +msgstr "增加" + +msgid "MINUS" +msgstr "减少" + +msgid "EXECUTED" +msgstr "已执行" + +msgid "QUALITY TESTING" +msgstr "质检" + +msgid "purchase manage" +msgstr "采购管理" + +msgid "purchase orders" +msgstr "采购单" + +msgid "purchase order" +msgstr "采购单" + +msgid "order date" +msgstr "订单日期" + +msgid "arrive date" +msgstr "交货日期" + +msgid "count" +msgstr "数量" + +msgid "money amount" +msgstr "金额" + +msgid "discount amount" +msgstr "折扣金额" + +msgid "tax rate" +msgstr "税率" + +msgid "po item" +msgstr "采购明细" + +msgid "discount price" +msgstr "折后单价" + +msgid "batch" +msgstr "批次号" + +msgid "Action Stock In" +msgstr "入库" + +msgid "Action Stock Out" +msgstr "出库" + +msgid "money of amount" +msgstr "金额" + +msgid "stock in money of amount" +msgstr "入库金额" + +msgid "entry time" +msgstr "入库时间" + +msgid "ALREADY STOCK IN" +msgstr "已入库" + +msgid "Inventory" +msgstr "实时库存" + +msgid "CHAR" +msgstr "字符" + +msgid "inventory material" +msgstr "库存物资" + +msgid "stock out time" +msgstr "出库时间" + +msgid "stock out amount" +msgstr "出库金额" + +msgid "Invoice" +msgstr "发票" + +msgid "invoice date" +msgstr "发票日期" + +msgid "invoice code" +msgstr "发票代码" + +msgid "invoice number" +msgstr "发票编号" + +msgid "po amount" +msgstr "采购金额" + +msgid "invoice amount" +msgstr "发票金额" + +msgid "invoice file" +msgstr "发票文件" + +msgid "pay date" +msgstr "付款日期" + +msgid "pay code" +msgstr "付款编号" + +msgid "response code" +msgstr "银行回执" + +msgid "Payment" +msgstr "采购付款" + +msgid "activity" +msgstr "活动" + +msgid "activities" +msgstr "活动" + +msgid "begin time" +msgstr "开始时间" + +msgid "end time" +msgstr "结束时间" + +msgid "host" +msgstr "主持人" + +msgid "speaker" +msgstr "主讲人" + +msgid "room" +msgstr "房间/会议室" + +msgid "mail list" +msgstr "邮件组" + +msgid "mail notice" +msgstr "邮件通知" + +msgid "weixin notice" +msgstr "微信通知" + +msgid "attach" +msgstr "附件" + +msgid "Meeting" +msgstr "会议" + +msgid "Train" +msgstr "培训" + +msgid "Community" +msgstr "集体活动" + +msgid "out user" +msgstr "领料人" + +msgid "other info" +msgstr "其他信息" + +msgid "InOut History" +msgstr "出入库历史" + +msgid "source" +msgstr "原始单据" + +msgid "plus or minus prop" +msgstr "增减属性" + +msgid "deliver address" +msgstr "交付地址" + +msgid "deliver date" +msgstr "交付日期" + +msgid "sales man" +msgstr "销售员" + +msgid "invoice type" +msgstr "发票类型" + +msgid "sale management" +msgstr "销售管理" + +msgid "sale order" +msgstr "销售订单" + +msgid "order detail" +msgstr "订单明细" + +msgid "Payment Collection" +msgstr "销售回款" + +msgid "collection date" +msgstr "回款日期" + +msgid "collection code" +msgstr "回款单号" + +msgid "order amount" +msgstr "合同金额" + +msgid "collection amount" +msgstr "到账金额" + +msgid "offer detail" +msgstr "报价明细" + +msgid "offer date" +msgstr "报价日期" + +msgid "offer man" +msgstr "报价员" + +msgid "offer sheet file" +msgstr "报价表" + +msgid "cost price" +msgstr "成本单价" + +msgid "mt type" +msgstr "物料性质" + +msgid "ware return" +msgstr "返库单" + +msgid "return item" +msgstr "返库明细" + +msgid "out count" +msgstr "领料数量" + +msgid "Action Ware Return" +msgstr "执行返库" + +msgid "ware adjust" +msgstr "库存调整" + +msgid "adjust item" +msgstr "调整明细" + +msgid "plus or minus property" +msgstr "增减属性" + +msgid "Action Ware Adjust" +msgstr "执行调整" + +msgid "data import" +msgstr "导入" + +msgid "date" +msgstr "日期" + +msgid "clear old data?" +msgstr "清除旧数据?" + +msgid "handler class" +msgstr "处理类" + +msgid "import" +msgstr "导入" + +msgid "action" +msgstr "执行" + +msgid "data import successfully" +msgstr "数据导入成功" + +msgid "applier" +msgstr "申请人" + +msgid "pay" +msgstr "支付" + +msgid "PAYED" +msgstr "已支付" + +msgid "action successfully" +msgstr "执行成功" + +msgid "Are your sure to execute the operations?" +msgstr "您确认要执行本操作吗?" + +msgid "to be imported detail" +msgstr "待导入明细" + +msgid "draft" +msgstr "草稿" + +msgid "published" +msgstr "已发布" + +msgid "keywords" +msgstr "关键词" + +msgid "BUSINESS DOC" +msgstr "业务文档" + +msgid "SYSTEM MANUAL" +msgstr "系统手册" + +msgid "document" +msgstr "文档" + +msgid "documents" +msgstr "文档" + +msgid "%(app)s administration" +msgstr "%(app)s 模块" + +msgid "next user handler" +msgstr "next用户处理类" + +msgid "next node handler" +msgstr "next节点处理类" + +msgid "status field" +msgstr "状态字段" + +msgid "status value" +msgstr "状态值" + +msgid "execute action" +msgstr "执行动作" + +msgid "can edit" +msgstr "可编辑?" + +msgid "attention" +msgstr "注意" + +msgid "is done" +msgstr "已办?" + +msgid "Are you sure to deny the request?" +msgstr "您确定要拒绝申请吗?" + +msgid "Are you sure to terminate the request?" +msgstr "您确定要终止申请吗?" + +msgid "workflow restarted success" +msgstr "工作流重启成功" + +msgid "export selected %(verbose_name_plural)s" +msgstr "导出所选的 %(verbose_name_plural)s" + +msgid "publish selected %(verbose_name_plural)s" +msgstr "发布所选的 %(verbose_name_plural)s" + +msgid "Are your sure to execute the check out operations?" +msgstr "您确定要执行出库操作吗?" + +msgid "check out successfully" +msgstr "已成功出库" + +msgid "check in successfully" +msgstr "已成功入库" \ No newline at end of file diff --git a/mis/settings.py b/mis/settings.py index ffc4829..a83da1c 100644 --- a/mis/settings.py +++ b/mis/settings.py @@ -49,6 +49,7 @@ INSTALLED_APPS = ( MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -89,6 +90,7 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': os.environ.get('DATABASE_HOST', 'localhost'), + 'PORT': os.environ.get('DATABASE_PORT', '3306'), 'NAME': os.environ.get('DATABASE_NAME', 'mis'), 'USER': os.environ.get('DATABASE_USER', 'root'), 'PASSWORD': os.environ.get('DATABASE_PASSWORD', 'root'), @@ -99,7 +101,7 @@ DATABASES = { # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ -LANGUAGE_CODE = 'zh-CN' +LANGUAGE_CODE = 'zh-hans' LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')] diff --git a/static/css/modern_ui.css b/static/css/modern_ui.css new file mode 100644 index 0000000..4379d3e --- /dev/null +++ b/static/css/modern_ui.css @@ -0,0 +1,984 @@ +/* 智捷ERP SmartJet ERP - 1:1 High-Fidelity UI System v4.0 */ +@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap'); + +:root { + --primary: #6366f1; + --primary-light: #818cf8; + --primary-bg: #f5f3ff; + + --slate-50: #f8fafc; + --slate-100: #f1f5f9; + --slate-200: #e2e8f0; + --slate-300: #cbd5e1; + --slate-400: #94a3b8; + --slate-500: #64748b; + --slate-600: #475569; + --slate-700: #334155; + --slate-800: #1e293b; + --slate-900: #0f172a; + + --success: #10b981; + --warning: #f59e0b; + --danger: #ef4444; + --info: #3b82f6; + + --sidebar-width: 240px; + --sidebar-collapsed-width: 0px; + --topbar-height: 64px; + --radius-lg: 16px; + --radius-md: 10px; + --shadow-card: 0 10px 15px -3px rgba(0, 0, 0, 0.04), 0 4px 6px -2px rgba(0, 0, 0, 0.02); +} + +*, *::before, *::after { + box-sizing: border-box; +} + +body { + background-color: #f1f5f9 !important; + color: var(--slate-800) !important; + font-family: 'Plus Jakarta Sans', -apple-system, sans-serif !important; + margin: 0 !important; + padding: 0 !important; + overflow-x: hidden; +} + +#container { + display: flex !important; + min-height: 100vh; + min-width: 0 !important; + max-width: 100vw; + width: 100%; + overflow-x: hidden; +} + +/* --- SIDEBAR --- */ +#header { + width: var(--sidebar-width) !important; + height: 100vh !important; + position: fixed !important; + left: 0; top: 0; + background: #ffffff !important; + border-right: 1px solid var(--slate-200) !important; + padding: 0 !important; + display: flex !important; + flex-direction: column !important; + z-index: 1000 !important; + color: var(--slate-800) !important; + line-height: normal !important; + overflow: visible !important; +} + +/* 覆盖 Django admin 默认深色顶栏链接样式(白字导致侧栏文字不可见) */ +#header a:link, +#header a:visited { + color: inherit !important; + text-decoration: none !important; +} + +#header a:focus, +#header a:hover { + text-decoration: none !important; +} + +.brand-section { + padding: 24px; + display: flex; + align-items: center; +} + +.brand-logo { + width: 32px; + height: 32px; + background: linear-gradient(135deg, #6366f1, #a855f7); + border-radius: 8px; + margin-right: 12px; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: 800; +} + +.brand-text h1 { + font-size: 18px !important; + font-weight: 800 !important; + color: #1e293b !important; + margin: 0 !important; + line-height: 1 !important; +} + +.brand-text span { + font-size: 11px; + color: var(--slate-400); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.sidebar-nav { + flex: 1; + padding: 0 16px; + overflow-y: auto; +} + +.nav-group-label { + padding: 20px 12px 10px 12px; + color: var(--slate-400); + font-size: 11px; + font-weight: 700; + text-transform: uppercase; +} + +.nav-item { + display: flex; + align-items: center; + padding: 12px 16px; + border-radius: 12px; + color: var(--slate-600) !important; + text-decoration: none !important; + font-weight: 600; + font-size: 14px; + margin-bottom: 4px; + transition: all 0.2s; +} + +.nav-item:hover, +.nav-item:focus { + background: var(--slate-50); + color: var(--primary) !important; +} + +.nav-item.active, +.nav-item.active:link, +.nav-item.active:visited { + background: var(--primary-bg) !important; + color: var(--primary) !important; +} + +.nav-item i { margin-right: 12px; font-size: 18px; width: 20px; text-align: center; } +.nav-item .arrow { margin-left: auto; font-size: 10px; color: var(--slate-300); } + +.sidebar-footer { + padding: 16px; + border-top: 1px solid var(--slate-100); + position: relative; + z-index: 1002; + flex-shrink: 0; + background: #ffffff; +} + +.nav-item-btn { + width: 100%; + border: none; + background: transparent; + cursor: pointer; + font-family: inherit; + text-align: left; +} + +button.nav-item-btn:hover, +button.nav-item-btn:focus { + background: var(--slate-50); + color: var(--primary) !important; + outline: none; +} + +/* 侧栏收起 */ +#header { + transition: transform 0.25s ease; +} + +body.sidebar-collapsed #header { + transform: translateX(-100%); + pointer-events: none; +} + +body.sidebar-collapsed #main { + margin-left: 0 !important; + max-width: 100vw !important; +} + +/* --- TOPBAR --- */ +#main { + flex: 1 1 auto !important; + margin-left: var(--sidebar-width) !important; + width: auto !important; + min-width: 0 !important; + max-width: calc(100vw - var(--sidebar-width)) !important; + overflow-x: hidden; + position: relative; + z-index: 1; +} + +.topbar { + height: var(--topbar-height); + background: #ffffff !important; + opacity: 1 !important; + display: flex; + align-items: center; + padding: 0 32px; + position: sticky; + top: 0; + z-index: 900; + box-shadow: 0 1px 2px rgba(0,0,0,0.02); +} + +.topbar-left { display: flex; align-items: center; flex: 1; } +.topbar-right { + display: flex; + align-items: center; + gap: 20px; + background: #ffffff !important; + opacity: 1 !important; + position: relative; + z-index: 901; + pointer-events: auto; +} + +.breadcrumb-new { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: var(--slate-400); + margin-left: 20px; +} + +.breadcrumb-new span { color: var(--slate-800); font-weight: 600; } + +.search-btn, .icon-btn { + width: 36px; + height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: var(--slate-500); + cursor: pointer; + transition: background 0.2s; + border: none; + background: transparent; + padding: 0; + font-size: 16px; + line-height: 1; + pointer-events: auto; + position: relative; + z-index: 1; +} + +.search-btn:hover, .icon-btn:hover, +.search-btn:focus, .icon-btn:focus { + background: var(--slate-100); + outline: none; +} + +.user-menu-wrap { + position: relative; + padding-left: 20px; + border-left: 1px solid var(--slate-200); +} + +.user-profile { + display: flex; + align-items: center; + gap: 12px; + padding: 4px 8px; + border: none; + border-radius: 10px; + background: #ffffff !important; + opacity: 1 !important; + cursor: pointer; + font-family: inherit; + pointer-events: auto; +} + +.user-profile:hover, +.user-profile:focus { + background: var(--slate-50) !important; + outline: none; +} + +.user-caret { + font-size: 10px; + color: var(--slate-400); + margin-left: 4px; +} + +.user-dropdown { + display: none; + position: absolute; + top: calc(100% + 8px); + right: 0; + min-width: 140px; + background: #ffffff; + border: 1px solid var(--slate-200); + border-radius: 10px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); + padding: 6px; + z-index: 1100; +} + +.user-dropdown.open { + display: block; +} + +.user-dropdown a { + display: block; + padding: 10px 14px; + border-radius: 8px; + color: var(--slate-700) !important; + text-decoration: none !important; + font-size: 13px; + font-weight: 600; +} + +.user-dropdown a:hover { + background: var(--slate-50); + color: var(--primary) !important; +} + +.avatar { + width: 36px; + height: 36px; + border-radius: 10px; + background: var(--slate-200); + flex-shrink: 0; + opacity: 1 !important; +} + +.user-info { display: flex; flex-direction: column; background: transparent; } +.user-name { font-weight: 700; font-size: 13px; color: var(--slate-800) !important; opacity: 1 !important; } +.user-role { font-size: 11px; color: var(--slate-500) !important; opacity: 1 !important; } + +/* --- CONTENT --- */ +#content { + padding: 32px !important; + width: 100% !important; + max-width: 100% !important; + min-width: 0 !important; + position: relative; + z-index: 1; +} + +.dashboard #content { + width: 100% !important; + max-width: 100% !important; +} + +#content-main { + float: none !important; + width: 100% !important; + max-width: 100% !important; + min-width: 0 !important; +} + +.dashboard #content > h1 { + display: none; +} + +#content > h1 { + font-size: 22px; + font-weight: 700; + color: var(--slate-900); + margin: 0 0 24px; + padding-right: 120px; +} + +.colM, .colMS { + max-width: 100%; + min-width: 0; +} + +#changelist, #changelist-form, .results { + max-width: 100%; + overflow-x: auto; +} + +/* --- STATS CARDS --- */ +.stats-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 24px; + margin-bottom: 32px; + width: 100%; + max-width: 100%; +} + +.stat-card { + background: #ffffff; + padding: 24px; + border-radius: var(--radius-lg); + box-shadow: var(--shadow-card); + position: relative; + min-width: 0; +} + +.stat-header { display: flex; justify-content: space-between; margin-bottom: 12px; } +.stat-icon-bg { width: 40px; height: 40px; border-radius: 12px; display: flex; align-items: center; justify-content: center; color: white; font-size: 18px; } +.stat-info-icon { color: var(--slate-300); font-size: 14px; cursor: help; } + +.stat-value { font-size: 24px; font-weight: 800; color: var(--slate-900); margin-bottom: 8px; } +.stat-label { font-size: 14px; color: var(--slate-500); font-weight: 500; } + +.stat-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; border-top: 1px solid var(--slate-50); padding-top: 16px; } +.stat-trend { font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 4px; } +.trend-up { color: var(--success); } +.trend-down { color: var(--danger); } + +.sparkline { width: 60px; height: 30px; } + +/* --- DATA MODULES --- */ +.dashboard-main-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(260px, 340px); + gap: 24px; + width: 100%; + max-width: 100%; +} + +.module-card { + background: white; + border-radius: var(--radius-lg); + box-shadow: var(--shadow-card); + overflow: hidden; + display: flex; + flex-direction: column; + min-width: 0; +} + +.card-title { + padding: 24px 32px; + font-size: 16px; + font-weight: 700; + color: var(--slate-900); + display: flex; + justify-content: space-between; + align-items: center; +} + +/* --- TABLE STYLES --- */ +.table-container { overflow-x: auto; max-width: 100%; padding: 0 12px 12px 12px; } +table { width: 100%; max-width: 100%; border-collapse: collapse; } +th { text-align: left; padding: 12px 20px; font-size: 12px; color: var(--slate-400); font-weight: 600; border-bottom: 1px solid var(--slate-100); } +td { padding: 16px 20px; font-size: 13px; color: var(--slate-700); border-bottom: 1px solid var(--slate-50); } +tr:last-child td { border: none; } + +.badge { + padding: 4px 10px; + border-radius: 6px; + font-size: 11px; + font-weight: 700; +} +.badge-new { background: #eef2ff; color: #6366f1; } +.badge-audit { background: #ecfdf5; color: #10b981; } +.badge-update { background: #eff6ff; color: #3b82f6; } +.badge-submit { background: #fff7ed; color: #f97316; } + +/* --- TASK LIST --- */ +.task-list { padding: 0 24px 24px 24px; } +.task-item { + display: flex; + align-items: flex-start; + gap: 16px; + padding: 16px; + border-radius: 12px; + margin-bottom: 12px; + background: #ffffff; + border: 1px solid var(--slate-50); + transition: all 0.2s; +} +.task-item:hover { border-color: var(--slate-200); background: var(--slate-50); } + +.task-icon { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 16px; color: white; } +.task-content { flex: 1; } +.task-name { font-size: 13px; font-weight: 700; color: var(--slate-800); } +.task-info { font-size: 11px; color: var(--slate-400); margin-top: 2px; } +.task-time { font-size: 11px; color: var(--slate-400); } + +.priority-tag { font-size: 10px; font-weight: 800; padding: 2px 6px; border-radius: 4px; text-transform: uppercase; margin-top: 8px; display: inline-block; } +.p-urgent { background: #fef2f2; color: #ef4444; } +.p-high { background: #fff7ed; color: #f97316; } +.p-medium { background: #fffbeb; color: #f59e0b; } +.p-low { background: #f0fdf4; color: #10b981; } + +.view-all { text-align: center; padding: 20px; border-top: 1px solid var(--slate-50); } +.view-all a { color: var(--primary); text-decoration: none; font-size: 13px; font-weight: 700; } + +/* --- ADMIN FORM ACTIONS --- */ +#content-main { + position: relative; + padding-bottom: 24px; +} + +.object-tools { + float: none !important; + position: absolute !important; + top: 32px; + right: 32px; + margin: 0 !important; + padding: 0 !important; + font-size: 12px !important; + font-weight: 600 !important; + opacity: 1 !important; + z-index: 5; + pointer-events: auto; +} + +.object-tools li { + height: auto !important; + margin-left: 0 !important; +} + +.object-tools a:link, +.object-tools a:visited, +.object-tools a.historylink { + display: inline-flex !important; + align-items: center; + float: none !important; + padding: 8px 16px !important; + background: var(--slate-600) !important; + color: #ffffff !important; + opacity: 1 !important; + border-radius: 8px !important; + font-size: 12px !important; + font-weight: 600 !important; + text-transform: none !important; + letter-spacing: 0 !important; + text-decoration: none !important; +} + +.object-tools a:hover, +.object-tools a:focus { + background: var(--slate-700) !important; + color: #ffffff !important; + opacity: 1 !important; +} + +.submit-row { + display: flex !important; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: 10px; + overflow: visible !important; + padding: 16px 20px !important; + margin: 24px 0 0 !important; + background: #ffffff !important; + border: 1px solid var(--slate-200) !important; + border-radius: var(--radius-md) !important; + box-shadow: var(--shadow-card); +} + +.submit-row p { + margin: 0 !important; +} + +.submit-row p.deletelink-box { + float: none !important; + margin-right: auto !important; +} + +.submit-row a.deletelink { + display: inline-flex !important; + align-items: center; + justify-content: center; + height: auto !important; + min-height: 38px; + line-height: 1.4 !important; + padding: 10px 20px !important; + border-radius: 8px !important; + color: #ffffff !important; + background: var(--danger) !important; + text-decoration: none !important; + font-weight: 600; + font-size: 13px; +} + +.submit-row a.deletelink:hover, +.submit-row a.deletelink:focus { + background: #dc2626 !important; +} + +.submit-row input, +.submit-row a.button, +.submit-row .button { + height: auto !important; + min-height: 38px; + line-height: 1.4 !important; + padding: 10px 20px !important; + margin: 0 !important; + border-radius: 8px !important; + font-size: 13px !important; + font-weight: 600 !important; + text-transform: none !important; + cursor: pointer; +} + +.submit-row input.default { + background: var(--primary) !important; + border-color: var(--primary) !important; + color: #ffffff !important; +} + +.submit-row input[name="_addanother"], +.submit-row input[name="_continue"], +.submit-row input[name="_saveasnew"] { + background: #ffffff !important; + color: var(--slate-700) !important; + border: 1px solid var(--slate-300) !important; +} + +/* 双栏选择器 */ +.selector { + max-width: 100%; +} + +.selector .selector-available h2, +.selector .selector-chosen h2 { + font-size: 13px; + font-weight: 600; + color: var(--slate-700); +} + +.selector select { + max-width: 100%; +} + +.selector .selector-filter label { + font-size: 12px; + color: var(--slate-500); +} + +/* 表单模块 */ +.module h2, fieldset.module h2 { + font-size: 14px; + font-weight: 700; + color: var(--slate-800); + background: var(--slate-50) !important; + border-bottom: 1px solid var(--slate-200); +} + +.form-row label { + color: var(--slate-700) !important; + font-weight: 600; +} + +/* Hide messy native elements */ +#footer, .breadcrumbs, #content-related { display: none !important; } + +@media (max-width: 1280px) { + .stats-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .dashboard-main-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .stats-grid { + grid-template-columns: 1fr; + } + + #content { + padding: 20px !important; + } + + .topbar { + padding: 0 16px; + } +} + +/* --- LOGIN PAGE --- */ +body.login-page { + margin: 0; + padding: 0; + min-height: 100vh; + background: var(--slate-100); + overflow-x: hidden; +} + +.login-shell { + display: flex; + min-height: 100vh; + width: 100%; +} + +.login-brand { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 48px; + background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #a855f7 100%); + color: #ffffff; + position: relative; + overflow: hidden; +} + +.login-brand::before { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient(circle at 20% 80%, rgba(255,255,255,0.12) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(255,255,255,0.08) 0%, transparent 40%); + pointer-events: none; +} + +.login-brand-inner { + position: relative; + max-width: 420px; + z-index: 1; +} + +.login-brand-logo { + width: 56px; + height: 56px; + background: rgba(255, 255, 255, 0.2); + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 28px; + margin-bottom: 24px; + backdrop-filter: blur(4px); +} + +.login-brand h1 { + font-size: 36px; + font-weight: 800; + margin: 0 0 8px; + letter-spacing: -0.02em; +} + +.login-brand-tagline { + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.1em; + opacity: 0.8; + margin: 0 0 20px; +} + +.login-brand-desc { + font-size: 16px; + opacity: 0.9; + margin: 0 0 32px; + line-height: 1.6; +} + +.login-brand-features { + list-style: none; + padding: 0; + margin: 0; +} + +.login-brand-features li { + padding: 10px 0; + padding-left: 24px; + position: relative; + font-size: 14px; + opacity: 0.9; +} + +.login-brand-features li::before { + content: '✓'; + position: absolute; + left: 0; + font-weight: 700; +} + +.login-panel { + flex: 0 0 480px; + display: flex; + align-items: center; + justify-content: center; + padding: 32px; + background: var(--slate-50); +} + +.login-card { + width: 100%; + max-width: 400px; + background: #ffffff; + border-radius: var(--radius-lg); + box-shadow: var(--shadow-card); + padding: 40px 36px; + border: 1px solid var(--slate-200); +} + +.login-card-header { + margin-bottom: 28px; +} + +.login-card-header h2 { + font-size: 24px; + font-weight: 800; + color: var(--slate-900); + margin: 0 0 8px; +} + +.login-card-header p { + font-size: 14px; + color: var(--slate-500); + margin: 0; +} + +.login-error { + background: #fef2f2; + color: #dc2626; + padding: 12px 14px; + border-radius: 8px; + font-size: 13px; + margin-bottom: 20px; + border: 1px solid #fecaca; +} + +.login-form { + margin-bottom: 24px; +} + +.login-field { + margin-bottom: 20px; +} + +.login-field label { + display: block; + font-size: 13px; + font-weight: 600; + color: var(--slate-700); + margin-bottom: 8px; +} + +.login-field input[type="text"], +.login-field input[type="password"] { + width: 100%; + padding: 12px 14px; + font-size: 14px; + font-family: inherit; + border: 1px solid var(--slate-300); + border-radius: var(--radius-md); + background: #ffffff; + color: var(--slate-800); + box-sizing: border-box; + transition: border-color 0.2s, box-shadow 0.2s; +} + +.login-field input[type="text"]:focus, +.login-field input[type="password"]:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--primary-bg); +} + +.login-submit { + width: 100%; + padding: 14px; + margin-top: 8px; + font-size: 15px; + font-weight: 700; + font-family: inherit; + color: #ffffff; + background: linear-gradient(135deg, var(--primary), #7c3aed); + border: none; + border-radius: var(--radius-md); + cursor: pointer; + transition: opacity 0.2s, transform 0.15s; +} + +.login-submit:hover { + opacity: 0.92; + transform: translateY(-1px); +} + +.login-submit:active { + transform: translateY(0); +} + +.login-demo { + background: var(--primary-bg); + border: 1px solid #e0e7ff; + border-radius: var(--radius-md); + padding: 16px; + margin-bottom: 20px; +} + +.login-demo-title { + font-size: 12px; + font-weight: 700; + color: var(--primary); + margin-bottom: 8px; +} + +.login-demo-row { + display: flex; + align-items: center; + gap: 12px; + font-size: 13px; + color: var(--slate-600); + margin-bottom: 4px; +} + +.login-demo-row span { + min-width: 48px; + color: var(--slate-500); +} + +.login-demo-row code { + font-family: 'Consolas', monospace; + background: rgba(255, 255, 255, 0.8); + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; + color: var(--slate-800); +} + +.login-demo-divider { + height: 1px; + background: #e0e7ff; + margin: 12px 0; +} + +.login-footer-link { + text-align: center; +} + +.login-footer-link a { + font-size: 12px; + color: var(--slate-400); + text-decoration: none; +} + +.login-footer-link a:hover { + color: var(--primary); +} + +@media (max-width: 900px) { + .login-shell { + flex-direction: column; + } + + .login-brand { + flex: none; + padding: 32px 24px; + } + + .login-brand-inner { + max-width: 100%; + text-align: center; + } + + .login-brand-logo { + margin-left: auto; + margin-right: auto; + } + + .login-brand-features { + display: none; + } + + .login-panel { + flex: 1; + padding: 24px 16px 32px; + } +} diff --git a/static/js/modern_ui.js b/static/js/modern_ui.js new file mode 100644 index 0000000..e80a2cf --- /dev/null +++ b/static/js/modern_ui.js @@ -0,0 +1,86 @@ +(function () { + 'use strict'; + + function ready(fn) { + if (document.readyState !== 'loading') { + fn(); + } else { + document.addEventListener('DOMContentLoaded', fn); + } + } + + function toggleSidebar() { + var collapsed = document.body.classList.toggle('sidebar-collapsed'); + try { + localStorage.setItem('erp-sidebar-collapsed', collapsed ? '1' : '0'); + } catch (e) {} + updateCollapseLabel(); + } + + function updateCollapseLabel() { + var btn = document.getElementById('sidebarCollapseBtn'); + if (!btn) { + return; + } + var collapsed = document.body.classList.contains('sidebar-collapsed'); + btn.innerHTML = collapsed + ? ' 展开菜单' + : ' 收起菜单'; + } + + function restoreSidebarState() { + try { + if (localStorage.getItem('erp-sidebar-collapsed') === '1') { + document.body.classList.add('sidebar-collapsed'); + } + } catch (e) {} + updateCollapseLabel(); + } + + function toggleUserMenu() { + var menu = document.getElementById('userDropdown'); + if (!menu) { + return; + } + var open = menu.classList.toggle('open'); + if (open) { + document.addEventListener('click', closeUserMenuOutside); + } else { + document.removeEventListener('click', closeUserMenuOutside); + } + } + + function closeUserMenuOutside(e) { + var menu = document.getElementById('userDropdown'); + var profile = document.getElementById('userProfileToggle'); + if (!menu || !profile) { + return; + } + if (!menu.contains(e.target) && !profile.contains(e.target)) { + menu.classList.remove('open'); + document.removeEventListener('click', closeUserMenuOutside); + } + } + + function bindClick(id, handler) { + var el = document.getElementById(id); + if (el) { + el.addEventListener('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + handler(e); + }); + } + } + + ready(function () { + restoreSidebarState(); + + bindClick('topbarMenuBtn', toggleSidebar); + bindClick('sidebarCollapseBtn', toggleSidebar); + bindClick('topbarNotifyBtn', function () { + window.location.href = '/admin/workflow/todolist/'; + }); + bindClick('userProfileToggle', toggleUserMenu); + }); +})(); diff --git a/templates/admin/base.html b/templates/admin/base.html index 7c73dad..5ff6f58 100644 --- a/templates/admin/base.html +++ b/templates/admin/base.html @@ -4,6 +4,7 @@ {% block title %}{% endblock %} + {% block extrastyle %}{% endblock %} {% if LANGUAGE_BIDI %}{% endif %} @@ -20,77 +21,102 @@
{% if not is_popup %} - +