112 Commits

Author SHA1 Message Date
wolfcode
3a2ee69d0f feat(curd): 关联表优化支持 add relation support and optimize model associations
- Add support for handling relations in CURD operations
- Optimize model associations to use hasOne instead of belongsTo
- Implement field selection for relations
- Update controller to handle AJAX requests and return JSON data Modify model to include relation methods
2025-04-16 15:28:41 +08:00
wolfcode
d513177c74 feat(easy-admin): improve AI suggestion display and typing effect
- Adjust the width of AI suggestion popup for mobile devices
- Implement a more realistic typing effect for AI-generated content
- Remove the 'fluid' class from the 'AI optimization' button in goods edit page
2025-04-11 15:23:15 +08:00
wolfcode
0705b9a38d Update public.css 2025-04-10 18:30:57 +08:00
wolfcode
feb26660e8 style(admin): adjust table cell height and line height
- Set table cell height to 47px instead of100%
- Set table cell line-height to 40px
2025-04-10 16:51:57 +08:00
wolfcode
b8ccf1542b feat(curd):支持CURD自动生成回收站功能 add recycle functionality for soft deleted items
- Add recycle method to Curd trait for restoring or permanently deleting items
- Implement recycle view and update index view to include recycle button-Add recycle URL and functionality to JavaScript table initialization
- Create new recycle template files for view and JavaScript
2025-04-08 10:43:27 +08:00
wolfcode
74122885f1 feat(mall): 新增回收站功能 add recycle functionality for goods
- Add recycle feature to goods management
- Implement recycle button in toolbar
- Create recycle page for deleted goods
- Add functionality to restore or permanently delete goods
2025-04-07 13:44:12 +08:00
wolfcode
f5813dec99 perf(cache): update system configuration and version caching logic
- Add cache update logic for system configurations in Config.php
- Modify version retrieval and caching logic in ConfigService.php
2025-04-02 11:18:42 +08:00
wolfcode
db0ac015f0 feat(easy-admin): add filtering functionality when sorting tables
- Implement filter logic in the table sorting event
- Iterate through columns to check for filter conditions-Construct filter and operator objects based on selected values
- Update the defaultWhere object with filter and op parameters
- Reload the table with the combined sorting and filtering conditions
2025-04-01 17:35:49 +08:00
wolfcode
bdabac7cff Update curd_generate.js 2025-03-31 17:11:12 +08:00
wolfcode
bd9cb6a3af Update Menu.php 2025-03-29 20:39:25 +08:00
wolfcode
3aaf030b89 feat(export): replace php-excel with PhpSpreadsheet for Excel export-Remove php-excel package and related usage
- Add PhpSpreadsheet package and update to latest version
- Implement new exportExcel function using PhpSpreadsheet
- Update admin controller to use new exportExcel function
- Remove redundant code and improve error handling
2025-03-28 12:22:05 +08:00
wolfcode
16975c4ee8 Update log.md 2025-03-28 10:18:00 +08:00
wolfcode
666598cd30 Update log.md 2025-03-27 18:43:31 +08:00
wolfcode
b9f764e4d0 refactor(admin): 重构控制器和模型的使用方式
- https://github.com/top-think/think-orm/issues/704
2025-03-27 18:38:27 +08:00
wolfcode
bc03616e43 refactor(admin): refactor model generation and improve array string handling
- Update CommonTool to fix array string formatting issue- Refactor model generation template to use getOptions method
2025-03-27 14:58:32 +08:00
wolfcode
8aba56c8c2 🚀 Layui v2.10.1 2025-03-27 11:22:43 +08:00
wolfcode
150e0ecd23 Update composer.json 2025-03-26 16:09:01 +08:00
wolfcode
e9ed0cd8f6 Merge branch 'main' of https://github.com/easyadmin8/EasyAdmin8 2025-03-26 14:03:22 +08:00
wolfcode
187d4343b3 refactor(admin): remove unused 'where' option in system module
- Remove unnecessary 'where' option from various models in system module
- Simplify query methods by removing redundant 'where' calls
- Affected models: Config, Node, SystemAdmin, SystemMenu, SystemNode
2025-03-26 14:00:58 +08:00
wolfcode
9bc0185b6b refactor(admin): improve error handling in Admin controller
- Enhance error messages by appending exception details
- Remove unnecessary password field handling in update scenario
2025-03-26 11:20:06 +08:00
wolfcode
652b17d6a6 refactor(admin): move auth_ids explode logic to model
- Remove auth_ids explode logic from Admin controller
- Add getAuthIdsAttr method to SystemAdmin model for auth_ids parsing
- This change improves code organization and reusability
2025-03-26 10:03:59 +08:00
wolfcode
ed8c3d545b refactor(admin):适配新版 think-orm remove console.log and refactor MallGoods model
- Remove unnecessary console.log statement from log.js
- Refactor MallGoods model to use getOptions method for deleteTime
2025-03-25 18:38:07 +08:00
wolfcode
12b38c7bf5 refactor(admin): optimize log data processing and display
- Update log data serialization and deserialization method
- Improve log data display format in the admin interface- Refactor log model initialization and table suffix handling
- Optimize time model configuration for better timestamp management
2025-03-25 18:08:35 +08:00
wolfcode
fc202be987 build(deps): update topthink/think-orm to 4.0.3
- Specify version 4.0.3 for topthink/think-orm in composer.json
- This change pins the ORM package to a specific version for stability and compatibility
2025-03-25 15:02:33 +08:00
wolfcode
71e069712a style(easy-admin): adjust image height in admin table
- Change image height from 40 to 30 in the admin table
2025-03-19 18:32:26 +08:00
wolfcode
ca4080d5e6 feat(config-admin): integrate lazyload for image optimization
- Add lazyload plugin to config-admin.js
- Implement lazyload functionality in easy-admin.js
- Add lazyload.min.js file to project
2025-03-18 17:10:46 +08:00
wolfcode
4bbe287626 refactor(system): upgrade tabs functionality and improve error handling
- Replace element.on with tabs.on for better tab management Add success and error handling for form submission
- Update HTML structure to use layui-tabs for improved UI
- Remove unnecessary Vue import
2025-03-17 18:02:06 +08:00
wolfcode
e316cd40e0 🚀 Layui v2.10.0 2025-03-17 18:01:02 +08:00
wolfcode
a7a3ddef8b refactor(easy-admin): improve AI optimization feature and enhance user experience
- Remove line-height style from AI optimization content div
- Add smooth scrolling to the end of the content when streaming output Move input validation to the beginning of the aiOptimization function
2025-03-10 11:54:41 +08:00
wolfcode
51f2cbc0f4 feat(mall): 新增AI对话支持 add AI optimization function for product titles
- Integrate AI functionality to optimize product titles
- Add AI optimization button in product add and edit pages
- Implement AI chat service for generating optimized titles
- Update backend to support AI optimization requests
- Add error handling and loading state for AI optimization process
2025-03-07 17:58:11 +08:00
wolfcode
8acf9f3f6c Update .example.env 2025-03-06 11:02:49 +08:00
wolfcode
d7b23f305d Update RateLimiting.php 2025-03-05 18:31:45 +08:00
wolfcode
fdfe2a542a Update RateLimiting.php 2025-03-05 15:25:04 +08:00
wolfcode
3d412d9ec6 Update index.html 2025-03-05 09:18:41 +08:00
wolfcode
f75ebffa5d feat(admin): 新增限流器 add rate limiting middleware for backend
- Add rate limiting functionality to the admin panel
- Implement rate limiting middleware to control API request frequency
- Update login controller to use rate limiting Add helper functions for getting IP and admin UID
- Update route configuration to include rate limiting middleware
- Add Redis configuration to .env file
- Update composer.json to include rate limiting package dependency
2025-03-04 17:26:24 +08:00
wolfcode
77881a27ed fix(curd): resolve select option value binding issue
- Update buildOptionView method to use correct field parameter
- Add conditional check for templateValue before appending to indexCols
2025-03-03 17:58:35 +08:00
wolfcode
07e11a1c45 fix(curd): improve CURD generation logic
- Update relation handling in BuildCurd.php:
  - Modify how foreign key relationships are constructed
  - Remove unnecessary template generation for non-editable fields

- Enhance command processing in CurdGenerate.php:
  - Filter and re-index command arguments to improve flexibility
2025-03-03 17:42:52 +08:00
wolfcode
8531ec89ad fix(admin): handle exceptions when reflecting on controller methods
- Add try-catch block around ReflectionMethod usage
- Catch and ignore any Throwable that occurs during reflection
- Improve error handling and prevent potential fatal errors
2025-02-25 10:50:18 +08:00
wolfcode
ff2e842c24 🚀 Layui v2.9.23 2025-02-21 11:11:24 +08:00
wolfcode
3d767643c8 Update BuildCurd.php 2025-02-20 15:40:47 +08:00
wolfcode
08fb6ef4d0 feat(log): 新增支持删除部分日志 add function to delete logs older than specified months
- Add deleteMonthLog functionality to log system
- Create new route and controller method for handling log deletion
- Implement frontend UI and logic for selecting and confirming log deletion-Add necessary database queries to safely delete logs
2025-02-19 11:23:46 +08:00
wolfcode
32d94bb3e0 feat(log): 新增支持删除部分日志 add function to delete logs older than specified months
- Add deleteMonthLog functionality to log system
- Create new route and controller method for handling log deletion
- Implement frontend UI and logic for selecting and confirming log deletion-Add necessary database queries to safely delete logs
2025-02-19 11:07:06 +08:00
wolfcode
d93483d9bf Update README.md 2025-02-18 13:44:24 +08:00
wolfcode
973e9cb24c feat(orm): upgrade think-orm to version 4.0 and add BaseEntity class
- Upgrade topthink/think-orm from ^3.0 to ^4.0 in composer.json
- Add new BaseEntity class in app/common/entity to handle common entity options
2025-02-14 16:12:12 +08:00
wolfcode
b510042323 Update Goods.php 2025-02-14 11:42:40 +08:00
wolfcode
b55dd8f67a feat(auth): add support for ignoring node authentication via annotation
- Add checkNodeAnnotationAttrAuth method to AuthService for annotation-based auth control
- Update checkAuth method to use the new annotation check-Modify Goods controller to use NodeAnnotation for specifying auth requirements
- Remove unused library imports in config-admin.js
2025-02-14 11:36:07 +08:00
wolfcode
8e488fb46c 🚀 Layui v2.9.21 2025-02-10 10:40:14 +08:00
wolfcode
d99e168583 Update Install.php 2025-01-15 09:54:11 +08:00
wolfcode
d6bb1456fa build(dependencies): update composer dependencies and adjust view rendering
- Update topthink/think-view dependency to version ^2.0
- Modify view rendering in Install controller to use empty template path
2025-01-15 09:45:07 +08:00
wolfcode
91eac36371 refactor(view): remove redundant template path generation
- Remove unnecessary code that generates a template path based on the request pathinfo
- Simplify the render function by removing the conditional block that sets the template path
2025-01-15 09:28:15 +08:00
wolfcode
1e4486989a refactor(admin): improve login check middleware
- Rename $ignoreAuth to $ignoreLogin for better clarity
- Update comments for better code readability
- Modify Login controller to use $ignoreLogin instead of $ignoreAuth
2025-01-14 10:09:58 +08:00
wolfcode
f9f25b76dd Merge branch 'main' of https://github.com/easyadmin8/EasyAdmin8 2025-01-12 18:13:17 +08:00
wolfcode
7603cdfa7e feat(admin): add middleware annotation for login exemption
- Add MiddlewareAnnotation to CheckLogin middleware for login exemption
- Implement IGNORE_LOGIN constant in MiddlewareAnnotation
- Use MiddlewareAnnotation in Goods controller for login exemption

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2025-01-12 18:12:28 +08:00
wolfcode
2e0cc85966 build(deps): update topthink/think-view to 2.0.0
- Change topthink/think-view dependency from ^2.0 to 2.0.0
- This update specifies an exact version to ensure compatibility and stability
2025-01-09 11:48:38 +08:00
wolfcode
5814fed0da Update Install.php 2025-01-09 11:32:25 +08:00
wolfcode
40f7ee82cd refactor(install): update view path delimiter
- Change the view path delimiter from '/' to '@' for the installation view
- This modification ensures consistency with the naming convention used in other parts of the application
2025-01-09 11:21:02 +08:00
wolfcode
936cb56c7f Update public.css 2025-01-08 21:38:48 +08:00
wolfcode
2d1940522c fix(View): automatically determine the template path if it is empty
- Add logic to automatically set the template path based on the controller and action if not explicitly defined
- Improve consistency in the 'iframeOpenTop' configuration by adding missing commas
2025-01-08 14:35:54 +08:00
wolfcode
264cf56ae4 fix(View): automatically determine the template path if it is empty
- Add logic to automatically set the template path based on the controller and action if not explicitly defined
- Improve consistency in the 'iframeOpenTop' configuration by adding missing commas
2025-01-08 14:25:54 +08:00
wolfcode
57ea9a3f47 feat(install): optimize installation page and add .env configuration support
- Update installation page layout and styling
- Add support for reading database configuration from .env file
- Improve error handling and user feedback during installation process
- Refactor JavaScript code for better readability and performance
2025-01-08 13:53:24 +08:00
wolfcode
31c06cff69 feat: use localStorage instead of sessionStorage and adjust styles for dark mode
- Replace sessionStorage with localStorage for saving background color ID
- Update CSS to improve dark mode styling:
  - Remove background color from layuimini-main
  - Add background color and other styles for tableSearch-xmSelect in dark mode
2025-01-08 10:47:37 +08:00
wolfcode
4c8d21fccf Merge pull request #21 from 1rmb/main
xmselect搜索独立样式
2025-01-07 20:41:16 +08:00
淘青松
817582bed5 xmselect搜索独立样式
表单item中可以自定义最小高度适配layui了
2025-01-07 18:13:51 +08:00
wolfcode
3a0c0c1624 Update public.css 2025-01-07 15:27:42 +08:00
wolfcode
30c7615e53 feat(auth): use attribute for ignore node
- Add NodeAnnotation attribute to filter ignore nodes
- Update Goods controller to use NodeAnnotation for ignore nodes
- Modify Node service to handle new attribute-Enhance AdminController with ignoreNode property
2025-01-07 11:12:16 +08:00
wolfcode
9d58da3cc3 Merge pull request #19 from QiuYiBin/main
feta(admin):数字输入框添加动态点缀,样式更加好看
2025-01-07 09:52:46 +08:00
wolfcode
1f5ca39226 style(admin): update login page background styling
- Set body background color to #333333
- Move background image from container to body element
- Remove background color from container element

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2025-01-06 20:25:37 +08:00
wolfcode
82f17301a7 style(layuimini): adjust header and menu colors
- Darken header right colors for better legibility-Adjust left menu colors for consistency- Update font family in public.css for improved typography
2025-01-06 18:30:59 +08:00
QiuYiBin
4ef7879ae3 feta(admin):数字输入框添加动态点缀,样式更加好看
feta(admin):CURL 生成排序使用数字输入框
2025-01-06 18:01:25 +08:00
wolfcode
0c9b369e2e build: integrate xmSelect plugin into admin static files
- Add xmSelect plugin to config-admin.js
- Remove separate script tag for xmSelect in default.html
- Update easy-admin.js to include xmSelect as a dependency
- Modify xm-select.js to support different module systems
2025-01-06 16:55:24 +08:00
wolfcode
e981852422 feat(admin): add custom favicon support Add favicon link to the head section of the default layout
- Use site_ico configuration or fallback to default favicon.ico
2025-01-06 16:29:18 +08:00
wolfcode
1d258621c8 feat(theme): 支持日夜模式切换 add dark mode support and enhance theme switching functionality
- Add dark mode support to the admin panel
- Implement smooth theme switching animation
- Update theme settings to use local storage
- Refactor theme initialization and rendering logic-Improve styling for dark mode
2025-01-06 14:26:12 +08:00
wolfcode
9534f2c2fe style(admin): update welcome module layout-Add max-height and overflow properties to welcome-module class
- Improve layout consistency for the welcome module in the admin panel
2025-01-03 11:24:53 +08:00
wolfcode
9fb8dd022a refactor(admin): improve system log middleware
- Remove unused imports and annotations- Simplify annotation reading process
- Use ReflectionClass and ReflectionMethod directly-Handle annotations using getAttributes() method
- Improve code readability and performance
2025-01-02 17:23:13 +08:00
wolfcode
fc264630ec refactor(admin):重构注解获取 upgrade annotations
- Replace Doctrine annotations with PHP 8.1 attributes
- Update annotation classes to use Attribute interface
- Modify attribute usage across multiple controller files
- Update composer.json to use doctrine/annotations ^2.0.0
2025-01-02 17:11:28 +08:00
wolfcode
3d19c8d337 refactor(admin):重构注解获取 upgrade annotations
- Replace Doctrine annotations with PHP 8.1 attributes
- Update annotation classes to use Attribute interface
- Modify attribute usage across multiple controller files
- Update composer.json to use doctrine/annotations ^2.0.0
2025-01-02 17:04:10 +08:00
wolfcode
4baa4e185d feat(admin): add system log switch and optimize log middleware
- Add APP_ADMIN_SYSTEM_LOG environment variable to control admin system logs
- Implement log switch check in SystemLog middleware
- Update .example.env file with new environment variable
2025-01-02 09:58:08 +08:00
wolfcode
88bc6441e5 Update README.md 2025-01-01 11:37:24 +08:00
wolfcode
0efb70b3b7 build: update minimum PHP version to8.1
- Update composer.json to require PHP >= 8.1.0
- Add log entry for PHP version update in log.md

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2025-01-01 11:31:43 +08:00
wolfcode
c22be8ac76 Merge pull request #17 from 1rmb/main
隐藏搜索框提示
2024-12-31 11:06:00 +08:00
淘青松
ebce7f3b0e 隐藏搜索框提示
将注释属性添加到table标签上即可
2024-12-31 10:56:13 +08:00
wolfcode
f8b6d847d5 fix(admin): 阿里云对象存储签名版本 V4, upgrade OSS SDK and implement signature version 4
- Upgrade aliyuncs/oss-sdk-php to v2.7.2 or above
- Implement signature version 4 for OSS client
- Add region and environment variable credentials provider
- Update error handling and code formatting
2024-12-27 11:25:02 +08:00
wolfcode
55a9b13e69 refactor(curd): optimize relation binding and code generation
- Move relation binding logic from model constructor to controller
- Generate controller code for binding select fields-Remove unnecessary code and templates
- Improve code readability and maintainability
2024-12-20 17:09:04 +08:00
wolfcode
5d58248cf4 feat(curd): add relation data binding and optimize model construction
- Add relation data binding in model construction
- Update model template to include new relation construction logic
- Remove unused index method template
2024-12-20 16:26:56 +08:00
wolfcode
bbd0320a35 Update README.md 2024-12-19 10:40:01 +08:00
wolfcode
d62a67aafe refactor(admin): use param instead of post to get request parameters
- In both Admin and Menu controllers, change $request->post('id') to $request->param('id')
- This change improves code consistency and allows for more flexible parameter retrieval
2024-12-17 11:22:01 +08:00
wolfcode
c6cc9d4164 feat(common): add support for absolute URLs in __url function
- Implement a check for absolute URLs using filter_var with FILTER_VALIDATE_URL
- If the provided URL is an absolute URL, it is returned as is
- This enhancement allows the __url function to handle both relative and absolute URLs
2024-12-16 13:51:27 +08:00
wolfcode
add8c5144c fix(easymde): update font-awesome css link
- Replace external font-awesome link with local static resource
- Update font-awesome CSS path to local version
2024-12-13 10:44:24 +08:00
wolfcode
62f591045e refactor(admin): enhance delete method to support custom primary keys
- Update delete method in Curd trait to accept Request parameter
- Add support for deleting multiple records using 'id' parameter
- Improve error handling for non-existent data
2024-12-11 17:00:34 +08:00
wolfcode
7470790657 refactor(admin): optimize system log middleware
- Adjust class name construction logic in SystemLog middleware
- Simplify condition for checking controller and action existence
2024-12-09 13:55:24 +08:00
wolfcode
11fa69afaf Merge pull request #13 from Rodots/main
路由配置新增操作方法的参数绑定方式
2024-12-02 16:04:23 +08:00
Rodots
8983705ce4 feat: Parameter Binding Method of New Operation Method for Routing Configuration 2024-12-02 16:01:49 +08:00
wolfcode
95ccd4071b feat(easy-admin): add support for custom template functions in operat column-Implement the ability to use custom template functions in the operat column of easy-admin tables
- Add a new button in the goods list page to demonstrate the usage of custom templates
2024-12-02 11:03:26 +08:00
wolfcode
09f3ea7e54 feat(search): add date type search support
- Implement search functionality for date type fields
- Add new case for 'date' in the search HTML generation logic
- Include date-specific input field with appropriate attributes
2024-11-29 18:50:02 +08:00
wolfcode
9cadb27c9e 🚀Layui v2.9.20 2024-11-29 16:38:46 +08:00
wolfcode
2772034a93 fix(admin): add type attribute to login button
- Add 'type="button"' attribute to the login button in admin login page
- This change prevents the button from being submitted as a form Reset button
2024-11-28 18:19:12 +08:00
wolfcode
08ea79033c feat(admin): add ignoreNode property to skip unnecessary node generation
- Add $ignoreNode property to Goods controller to specify methods to ignore
- Update Node service to check and skip ignored methods during node generation
- This change helps to filter out unnecessary nodes, improving system performance and readability
2024-11-28 10:54:15 +08:00
wolfcode
e7f09d9c68 refactor(install):使用PDO初始化安装 use PDO instead of mysqli for database operations
- Replace mysqli with PDO for database connection and queries
- Create a separate method for generating PDO DSN
- Update error handling to use PDOException
- Remove unnecessary mysqli_set_charset and mysqli_select_db calls
2024-11-27 12:33:04 +08:00
wolfcode
e2effb762c build(deps): update dependency wolfcode/authenticator to v0.0.6
- Updated wolfcode/authenticator from version 0.0.5 to 0.0.6 in composer.json
2024-11-22 16:59:07 +08:00
wolfcode
0e18825808 fix(authenticator): update dependency and adjust QR code generation
- Update wolfcode/authenticator from 0.0.3 to 0.0.5
- Modify QR code generation method in Index controller
2024-11-22 16:51:36 +08:00
wolfcode
c031b09422 build(deps): update topthink/think-multi-app to ^1.1.0
- Replace dev-master with stable version ^1.1.0 for topthink/think-multi-app- This change ensures compatibility and stability in dependency management
2024-11-22 15:16:44 +08:00
wolfcode
231fd48e2f build(deps): update topthink/think-multi-app to dev-master
- Change the version of topthink/think-multi-app from ^1.0 to dev-master in composer.json
2024-11-22 09:32:06 +08:00
wolfcode
53772badd4 Merge pull request #12 from Rodots/main
fix: 修正命名空间,移除无用类的引用
2024-11-14 16:45:09 +08:00
wolfcode
3e329a4ea3 fix(install): update ea_system_admin insert statement
- Add missing column for 'status' in ea_system_admin insert statement
- Set 'status' column to 1 for the admin user
2024-11-13 16:30:15 +08:00
wolfcode
f015a90b89 style(layuimini): remove redundant CSS rules for nav more icons- Removed specific margin-top rules for .layuimini-menu-left and .layuimini-menu-left-zoom classes
- Kept the general rule for .layuimini-menu-left .layui-nav .layui-nav-mored and .layuimini-menu-left .layui-nav-itemed > a .layui-nav-more
2024-11-12 17:21:04 +08:00
wolfcode
75c668b966 feat(auth): 新增谷歌验证码登录 implement Google Authenticator support for two-factor authentication
- Add Google Authenticator integration for enhanced login security-Update admin edit page to include login type selection
- Modify login process to support two-factor authentication
- Add new database fields for login type and GA secret
- Update client-side JavaScript to handle GA code input and validation
2024-11-12 10:51:48 +08:00
wolfcode
f3e5a041d1 feat(ueditor): add custom upload service and other enhancements
- Add custom upload service support
- Implement server response preparation function
- Enable loading config from server
- Add content import button to toolbar
- Update image alignment buttons order
- Add tip error function for better error handling
- Remove unused comments and simplify configuration options
2024-11-11 11:22:47 +08:00
Rodots
34354837d5 fix: 修正命名空间,移除无用类的引用 2024-11-06 14:51:15 +08:00
wolfcode
3b219cfe7f feat(install): add official website tutorial and common questions links
- Add links to the official website tutorial and common questions in the installation page
- Update the logo image style for better visual presentation
2024-11-04 11:53:39 +08:00
wolfcode
ed99fe79fd feat(admin): 快捷操作支持翻页滚动显示 and optimize UI for better user experience
- Add swiper plugin support for carousel functionality
- Optimize UI for better user experience by using swiper
- Update index.js to support swiper initialization
- Modify welcome.html to include swiper HTML structure
- Adjust welcome.css for better styling of swiper elements
2024-10-29 17:24:54 +08:00
wolfcode
846b8ffafc feat(easy-admin): update window open URL for new tab button
- Change the URL for the "New tab open" button to use the iframe's content URL
- Add fallback to current window URL if iframe content URL cannot be accessed
- Improve user experience by opening the correct URL in a new tab

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-28 18:26:26 +08:00
wolfcode
47ef5d9f10 feat(auth): 改用 zTree 替换 layui 的 tree 组件 use zTree for node authorization
- Replace layui tree with zTree for better performance and flexibility
- Update auth.js to use zTree for node rendering and checking
- Add zTree CSS and JS files to project

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-28 16:35:06 +08:00
160 changed files with 63590 additions and 32777 deletions

View File

@@ -1,5 +1,8 @@
APP_DEBUG=true
# 后台系统日志开关
APP_ADMIN_SYSTEM_LOG=true
DEFAULT_TIMEZONE=Asia/Shanghai
DB_TYPE=mysql
@@ -11,6 +14,16 @@ DB_PORT=3306
DB_CHARSET=utf8mb4
DB_PREFIX=ea8_
# 限流器开关 若启动需要配置 Redis 服务
RATE_LIMITING_STATUS=false
# Redis配置
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_PREFIX=
REDIS_DATABASE=0
# 后台配置项组
[EASYADMIN]

View File

@@ -1,27 +1,31 @@
<div align="center" dir="auto">
<img alt="log" src="/public/static/common/images/logo-8.png" />
<img alt="log" src="public/static/common/images/logo-8.png" />
<span><img src="https://img.shields.io/badge/php-%3E=8.0.0-brightgreen.svg?style=for-the-badge&logo=php&colorB=ff69b4" alt="php"></span>
<span><img src="https://img.shields.io/badge/mysql-%3E=5.7-brightgreen.svg?style=for-the-badge&logo=mysql&colorB=blue" alt="MySQL"></span>
<span><img src="https://img.shields.io/badge/thinkphp-%3E=8.0.0-brightgreen.svg?style=for-the-badge&logo=thinkphp" alt="ThinkPHP"></span>
<span><img src="https://img.shields.io/badge/layui-%3E=2.9.0-brightgreen.svg?style=for-the-badge&logo=layui&colorB=orange" alt="layui"></span>
<span><img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge&logo=license&colorB=purple" alt="License"></span>
<p>
<img src="https://img.shields.io/badge/php-%3E=8.1.0-brightgreen.svg?style=for-the-badge&logo=php&colorB=ff69b4" alt="php">
<img src="https://img.shields.io/badge/mysql-%3E=5.7-brightgreen.svg?style=for-the-badge&logo=mysql&colorB=blue" alt="MySQL">
<img src="https://img.shields.io/badge/thinkphp-%3E=8.0.0-brightgreen.svg?style=for-the-badge&logo=thinkphp" alt="ThinkPHP">
<img src="https://img.shields.io/badge/layui-%3E=2.9.0-brightgreen.svg?style=for-the-badge&logo=layui&colorB=orange" alt="layui">
<img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge&logo=license&colorB=purple" alt="License">
</p>
</div>
## `EasyAdmin8`所有版本 (当前项目为`ThinkPHP`版本)
| | Github | Gitee |
|----------|:--------------------------------------------------------------------:|:-----------------------------------------------------------------:|
| ThinkPHP | [EasyAdmin8](https://github.com/wolf-leo/EasyAdmin8) | [EasyAdmin8](https://gitee.com/wolf18/EasyAdmin8) |
| Laravel | [EasyAdmin8-Laravel](https://github.com/wolf-leo/EasyAdmin8-Laravel) | [EasyAdmin8-Laravel](https://gitee.com/wolf18/EasyAdmin8-Laravel) |
| webman | [EasyAdmin8-webman](https://github.com/wolf-leo/EasyAdmin8-webman) | [EasyAdmin8-webman](https://gitee.com/wolf18/EasyAdmin8-webman) |
| | Github | Gitee |
|----------|:----------------------------------------------------------------------:|:---------------------------------------------------------------------:|
| ThinkPHP | [EasyAdmin8](https://github.com/EasyAdmin8/EasyAdmin8) | [EasyAdmin8](https://gitee.com/EasyAdmin8/EasyAdmin8) |
| Laravel | [EasyAdmin8-Laravel](https://github.com/EasyAdmin8/EasyAdmin8-Laravel) | [EasyAdmin8-Laravel](https://gitee.com/EasyAdmin8/EasyAdmin8-Laravel) |
| webman | [EasyAdmin8-webman](https://github.com/EasyAdmin8/EasyAdmin8-webman) | [EasyAdmin8-webman](https://gitee.com/EasyAdmin8/EasyAdmin8-webman) |
## 项目介绍
> `EasyAdmin8` 在 [`EasyAdmin`](https://gitee.com/zhongshaofa/easyadmin) 的基础上更新 ThinkPHP 框架到 8.0 PHP 最低版本要求不低于 8.0
> `EasyAdmin8` 在 [`EasyAdmin`](https://gitee.com/zhongshaofa/easyadmin) 的基础上更新 ThinkPHP 框架到 8.1+ PHP 最低版本要求不低于 8.1
>
> 2025年起 `PHP` 版本要求提升到 `8.1+`, 如果需要 `8.0` 到分支 `v8.0` 中下载
>
> ThinkPHP v8.0 和 Layui v2.9.x 的快速开发的后台管理系统。
> ThinkPHP v8.1+ 和 Layui v2.9.x 的快速开发的后台管理系统。
>
> 项目地址:[http://easyadmin8.top](http://easyadmin8.top)
>
@@ -50,13 +54,13 @@ if [ -f /usr/bin/curl ];then curl -sSO https://easyadmin8.top/auto-install-EasyA
```
1.下载安装包
git clone https://github.com/wolf-leo/EasyAdmin8
git clone https://github.com/EasyAdmin8/EasyAdmin8
或者
git clone https://gitee.com/wolf18/EasyAdmin8
git clone https://gitee.com/EasyAdmin8/EasyAdmin8
2.安装依赖包(确保 PHP 版本 >= 8.0
2.安装依赖包(确保 PHP 版本 >= 8.1
在根目录下 composer install ,如果有报错信息可以使用命令 composer install --ignore-platform-reqs
@@ -75,7 +79,7 @@ if [ -f /usr/bin/curl ];then curl -sSO https://easyadmin8.top/auto-install-EasyA
## CURD命令大全
> 参考 [CURD命令大全](CURD.md)
> 参考 [CURD命令大全](https://edocs.easyadmin8.top/curd/command.html)
## 常见问题
@@ -97,7 +101,7 @@ if [ -f /usr/bin/curl ];then curl -sSO https://easyadmin8.top/auto-install-EasyA
## 相关文档
* [ThinkPHP 8.0](https://doc.thinkphp.cn/v8_0)
* [ThinkPHP 8.1](https://doc.thinkphp.cn)
* [EasyAdmin](http://easyadmin.99php.cn/docs)

View File

@@ -4,6 +4,7 @@ use app\admin\middleware\CheckInstall;
use app\admin\middleware\CheckLogin;
use app\admin\middleware\CheckAuth;
use app\admin\middleware\SystemLog;
use app\admin\middleware\RateLimiting;
// 你可以在这里继续写你需要的路由
@@ -16,6 +17,8 @@ use app\admin\middleware\SystemLog;
return [
'middleware' => [
// 限流中间件
RateLimiting::class,
// 判断是否已经安装后台系统
CheckInstall::class,
// 检测是否登录

View File

@@ -109,24 +109,21 @@ class Ajax extends AdminController
*/
public function getUploadFiles(Request $request): Json
{
$get = $request->get();
$page = !empty($get['page']) ? $get['page'] : 1;
$limit = !empty($get['limit']) ? $get['limit'] : 10;
$title = !empty($get['title']) ? $get['title'] : null;
$this->model = new SystemUploadfile();
$count = $this->model
->where(function (Query $query) use ($title) {
!empty($title) && $query->where('original_name', 'like', "%{$title}%");
})
$get = $request->get();
$page = !empty($get['page']) ? $get['page'] : 1;
$limit = !empty($get['limit']) ? $get['limit'] : 10;
$title = !empty($get['title']) ? $get['title'] : null;
$count = SystemUploadfile::where(function(Query $query) use ($title) {
!empty($title) && $query->where('original_name', 'like', "%{$title}%");
})
->count();
$list = $this->model
->where(function (Query $query) use ($title) {
!empty($title) && $query->where('original_name', 'like', "%{$title}%");
})
$list = SystemUploadfile::where(function(Query $query) use ($title) {
!empty($title) && $query->where('original_name', 'like', "%{$title}%");
})
->page($page, $limit)
->order($this->sort)
->select()->toArray();
$data = [
$data = [
'code' => 0,
'msg' => '',
'count' => $count,
@@ -149,7 +146,7 @@ class Ajax extends AdminController
$upload_allow_size = $uploadConfig['upload_allow_size'];
$_upload_allow_ext = explode(',', $uploadConfig['upload_allow_ext']);
$upload_allow_ext = [];
array_map(function ($value) use (&$upload_allow_ext) {
array_map(function($value) use (&$upload_allow_ext) {
$upload_allow_ext[] = '.' . $value;
}, $_upload_allow_ext);
$config = [
@@ -213,4 +210,23 @@ class Ajax extends AdminController
return json($config);
}
}
public function composerInfo(): Json
{
$lockFilePath = root_path() . '/composer.lock';
$list = [];
if (file_exists($lockFilePath)) {
$lockFileContent = file_get_contents($lockFilePath);
if ($lockFileContent !== false) {
$lockData = json_decode($lockFileContent, true);
if (!empty($lockData['packages'])) {
foreach ($lockData['packages'] as $package) {
$list[] = ['name' => $package['name'], 'version' => $package['version']];
}
}
}
}
$this->success('success', $list);
}
}

View File

@@ -36,11 +36,9 @@ class Index extends AdminController
$mysqlVersion = Db::query("select version() as version")[0]['version'] ?? '未知';
$phpVersion = phpversion();
$versions = compact('tpVersion', 'mysqlVersion', 'phpVersion');
$quicks = SystemQuick::field('id,title,icon,href')
->where(['status' => 1])
->order('sort', 'desc')
->limit(8)
->select();
$quick_list = SystemQuick::field('id,title,icon,href')
->where(['status' => 1])->order('sort', 'desc')->limit(50)->select()->toArray();
$quicks = array_chunk($quick_list, 8);
$this->assign(compact('quicks', 'versions'));
return $this->fetch();
}
@@ -55,7 +53,7 @@ class Index extends AdminController
*/
public function editAdmin(Request $request): string
{
$id = $this->adminUid;
$id = $this->adminUid;
$row = (new SystemAdmin())
->withoutField('password')
->find($id);
@@ -66,15 +64,20 @@ class Index extends AdminController
$rule = [];
$this->validate($post, $rule);
try {
$save = $row
->allowField(['head_img', 'phone', 'remark', 'update_time'])
->save($post);
}catch (Exception $e) {
$login_type = $post['login_type'] ?? 1;
if ($login_type == 2) {
$ga_secret = (new SystemAdmin())->where('id', $id)->value('ga_secret');
if (empty($ga_secret)) $this->error('请先绑定谷歌验证器');
}
$save = $row->allowField(['head_img', 'phone', 'remark', 'update_time', 'login_type'])->save($post);
}catch (\PDOException $e) {
$this->error('保存失败');
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$this->assign('row', $row);
$notes = (new SystemAdmin())->notes;
$this->assign('notes', $notes);
return $this->fetch();
}
@@ -82,13 +85,10 @@ class Index extends AdminController
* 修改密码
* @param Request $request
* @return string
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function editPassword(Request $request): string
{
$id = $this->adminUid;
$id = $this->adminUid;
$row = (new SystemAdmin())
->withoutField('password')
->find($id);
@@ -124,4 +124,37 @@ class Index extends AdminController
return $this->fetch();
}
/**
* 设置谷歌验证码
* @param Request $request
* @return string
* @throws Exception
*/
public function set2fa(Request $request): string
{
$id = $this->adminUid;
$row = (new SystemAdmin())->withoutField('password')->find($id);
if (!$row) $this->error('用户信息不存在');
// You can see: https://gitee.com/wolf-code/authenticator
$ga = new \Wolfcode\Authenticator\google\PHPGangstaGoogleAuthenticator();
if (!$request->isAjax()) {
$old_secret = $row->ga_secret;
$secret = $ga->createSecret(32);
$ga_title = $this->isDemo ? 'EasyAdmin8演示环境' : '可自定义修改显示标题';
$dataUri = $ga->getQRCode($ga_title, $secret);
$this->assign(compact('row', 'dataUri', 'old_secret', 'secret'));
return $this->fetch();
}
$this->isDemo && $this->error('演示环境下不允许修改');
$post = $request->post();
$ga_secret = $post['ga_secret'] ?? '';
$ga_code = $post['ga_code'] ?? '';
if (empty($ga_code)) $this->error('请输入验证码');
if (!$ga->verifyCode($ga_secret, $ga_code)) $this->error('验证码错误');
$row->ga_secret = $ga_secret;
$row->login_type = 2;
$row->save();
$this->success('操作成功');
}
}

View File

@@ -4,17 +4,19 @@ namespace app\admin\controller;
use app\admin\model\SystemAdmin;
use app\common\controller\AdminController;
use app\common\utils\Helper;
use think\captcha\facade\Captcha;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use app\Request;
use think\Response;
use Wolfcode\RateLimiting\Attributes\RateLimitingMiddleware;
class Login extends AdminController
{
protected bool $ignoreAuth = true;
protected bool $ignoreLogin = true;
public function initialize(): void
{
@@ -34,6 +36,7 @@ class Login extends AdminController
* @throws DbException
* @throws ModelNotFoundException
*/
#[RateLimitingMiddleware(key: [Helper::class, 'getIp'], seconds: 1, limit: 1, message: '请求过于频繁')]
public function index(Request $request): string
{
$captcha = env('EASYADMIN.CAPTCHA', 1);
@@ -56,6 +59,11 @@ class Login extends AdminController
if ($admin->status == 0) {
$this->error('账号已被禁用');
}
if ($admin->login_type == 2) {
if (empty($post['ga_code'])) $this->error('请输入谷歌验证码', ['is_ga_code' => true]);
$ga = new \Wolfcode\Authenticator\google\PHPGangstaGoogleAuthenticator();
if (!$ga->verifyCode($admin->ga_secret, $post['ga_code'])) $this->error('谷歌验证码错误');;
}
$admin->login_num += 1;
$admin->save();
$admin = $admin->toArray();

View File

@@ -8,18 +8,14 @@ use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use think\App;
/**
* Class Admin
* @package app\admin\controller\system
* @ControllerAnnotation(title="商品分类管理")
*/
#[ControllerAnnotation(title: '商品分类管理')]
class Cate extends AdminController
{
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new MallCate();
self::$model = MallCate::class;
}
}

View File

@@ -4,40 +4,38 @@ namespace app\admin\controller\mall;
use app\admin\model\MallCate;
use app\admin\model\MallGoods;
use app\admin\service\annotation\MiddlewareAnnotation;
use app\common\controller\AdminController;
use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use app\Request;
use think\App;
use think\db\exception\DbException;
use think\response\Json;
use Wolfcode\Ai\Enum\AiType;
use Wolfcode\Ai\Service\AiChatService;
/**
* Class Goods
* @package app\admin\controller\mall
* @ControllerAnnotation(title="商城商品管理")
*/
#[ControllerAnnotation(title: '商城商品管理')]
class Goods extends AdminController
{
#[NodeAnnotation(ignore: ['export'])] // 过滤不需要生成的权限节点 默认 CURD 中会自动生成部分节点 可以在此处过滤
protected array $ignoreNode;
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new MallGoods();
$this->assign('cate', (new MallCate())->column('title', 'id'));
self::$model = MallGoods::class;
$this->assign('cate', MallCate::column('title', 'id'));
}
/**
* @NodeAnnotation(title="列表")
* @throws DbException
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
if ($request->isAjax()) {
if (input('selectFields')) return $this->selectList();
list($page, $limit, $where) = $this->buildTableParams();
$count = $this->model->where($where)->count();
$list = $this->model->with(['cate'])->where($where)->page($page, $limit)->order($this->sort)->select()->toArray();
$count = self::$model::where($where)->count();
$list = self::$model::with(['cate'])->where($where)->page($page, $limit)->order($this->sort)->select()->toArray();
$data = [
'code' => 0,
'msg' => '',
@@ -49,12 +47,10 @@ class Goods extends AdminController
return $this->fetch();
}
/**
* @NodeAnnotation(title="入库")
*/
#[NodeAnnotation(title: '入库', auth: true)]
public function stock(Request $request, $id): string
{
$row = $this->model->find($id);
$row = self::$model::find($id);
empty($row) && $this->error('数据不存在');
if ($request->isPost()) {
$post = $request->post();
@@ -73,4 +69,67 @@ class Goods extends AdminController
return $this->fetch();
}
#[MiddlewareAnnotation(ignore: MiddlewareAnnotation::IGNORE_LOGIN)]
public function no_check_login(Request $request): string
{
return '这里演示方法不需要经过登录验证';
}
#[NodeAnnotation(title: 'AI优化', auth: true)]
public function aiOptimization(Request $request): void
{
$message = $request->post('message');
if (empty($message)) $this->error('请输入内容');
// 演示环境下 默认返回的内容
if ($this->isDemo) {
$content = <<<EOF
演示环境中 默认返回的内容
我来帮你优化这个标题,让它更有吸引力且更符合电商平台的搜索逻辑:
"商务男士高端定制马克杯 | 办公室精英必备 | 优质陶瓷防烫手柄"
这个优化后的标题:
1. 突出了目标用户群体(商务男士)
2. 强调了产品定位(高端定制)
3. 点明了使用场景(办公室)
4. 添加了材质和功能特点(优质陶瓷、防烫手柄)
5. 使用了吸引人的关键词(精英必备)
这样的标题不仅更具体,也更容易被搜索引擎识别,同时能精准触达目标客户群。您觉得这个版本如何?
EOF;
$choices = [['message' => [
'role' => 'assistant',
'content' => $content,
]]];
$this->success('success', compact('choices'));
}
try {
$result = AiChatService::instance()
// 当使用推理模型时,可能存在超时的情况,所以需要设置超时时间为 0
// ->setTimeLimit(0)
// 请替换为您需要的模型类型
->setAiType(AiType::QWEN)
// 如果需要指定模型的 API 地址,可自行设置
// ->setAiUrl('https://xxx.com')
// 请替换为您的模型
->setAiModel('qwen-plus')
// 请替换为您的 API KEY
->setAiKey('sk-1234567890')
// 此内容会作为系统提示,会影响到回答的内容 当前仅作为测试使用
->setSystemContent('你现在是一位资深的海外电商产品经理')
->chat($message);
$choices = $result['choices'];
}catch (\Throwable $exception) {
$choices = [['message' => [
'role' => 'assistant',
'content' => $exception->getMessage(),
]]];
}
$this->success('success', compact('choices'));
}
}

View File

@@ -10,14 +10,9 @@ use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use app\Request;
use think\App;
use think\db\exception\DbException;
use think\response\Json;
/**
* Class Admin
* @package app\admin\controller\system
* @ControllerAnnotation(title="管理员管理")
*/
#[ControllerAnnotation(title: '管理员管理')]
class Admin extends AdminController
{
@@ -29,14 +24,11 @@ class Admin extends AdminController
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemAdmin();
$this->assign('auth_list', $this->model->getAuthList());
self::$model = SystemAdmin::class;
$this->assign('auth_list', self::$model::getAuthList());
}
/**
* @NodeAnnotation(title="列表")
* @throws DbException
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
if ($request->isAjax()) {
@@ -44,11 +36,8 @@ class Admin extends AdminController
return $this->selectList();
}
list($page, $limit, $where) = $this->buildTableParams();
$count = $this->model
->where($where)
->count();
$list = $this->model
->withoutField('password')
$count = self::$model::where($where)->count();
$list = self::$model::withoutField('password')
->where($where)
->page($page, $limit)
->order($this->sort)
@@ -64,9 +53,7 @@ class Admin extends AdminController
return $this->fetch();
}
/**
* @NodeAnnotation(title="添加")
*/
#[NodeAnnotation(title: '添加', auth: true)]
public function add(Request $request): string
{
if ($request->isPost()) {
@@ -78,21 +65,19 @@ class Admin extends AdminController
if (empty($post['password'])) $post['password'] = '123456';
$post['password'] = password($post['password']);
try {
$save = $this->model->save($post);
$save = self::$model::create($post);
}catch (\Exception $e) {
$this->error('保存失败');
$this->error('保存失败' . $e->getMessage());
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
return $this->fetch();
}
/**
* @NodeAnnotation(title="编辑")
*/
#[NodeAnnotation(title: '编辑', auth: true)]
public function edit(Request $request, $id = 0): string
{
$row = $this->model->find($id);
$row = self::$model::find($id);
empty($row) && $this->error('数据不存在');
if ($request->isPost()) {
$post = $request->post();
@@ -100,28 +85,22 @@ class Admin extends AdminController
$post['auth_ids'] = implode(',', array_keys($authIds));
$rule = [];
$this->validate($post, $rule);
if (isset($row['password'])) {
unset($row['password']);
}
try {
$save = $row->save($post);
TriggerService::updateMenu($id);
}catch (\Exception $e) {
$this->error('保存失败');
$this->error('保存失败' . $e->getMessage());
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$row->auth_ids = explode(',', $row->auth_ids ?: '');
$this->assign('row', $row);
return $this->fetch();
}
/**
* @NodeAnnotation(title="编辑")
*/
#[NodeAnnotation(title: '设置密码', auth: true)]
public function password(Request $request, $id): string
{
$row = $this->model->find($id);
$row = self::$model::find($id);
empty($row) && $this->error('数据不存在');
if ($request->isAjax()) {
$post = $request->post();
@@ -142,18 +121,16 @@ class Admin extends AdminController
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$row->auth_ids = explode(',', $row->auth_ids ?: '');
$this->assign('row', $row);
return $this->fetch();
}
/**
* @NodeAnnotation(title="删除")
*/
public function delete($id): void
#[NodeAnnotation(title: '删除', auth: true)]
public function delete(Request $request): void
{
$this->checkPostRequest();
$row = $this->model->whereIn('id', $id)->select();
$id = $request->param('id');
$row = self::$model::whereIn('id', $id)->select();
$row->isEmpty() && $this->error('数据不存在');
$id == AdminConstant::SUPER_ADMIN_ID && $this->error('超级管理员不允许修改');
if (is_array($id)) {
@@ -169,9 +146,7 @@ class Admin extends AdminController
$save ? $this->success('删除成功') : $this->error('删除失败');
}
/**
* @NodeAnnotation(title="属性修改")
*/
#[NodeAnnotation(title: '属性修改', auth: true)]
public function modify(Request $request): void
{
$this->checkPostRequest();
@@ -188,7 +163,7 @@ class Admin extends AdminController
if ($post['id'] == AdminConstant::SUPER_ADMIN_ID && $post['field'] == 'status') {
$this->error('超级管理员状态不允许修改');
}
$row = $this->model->find($post['id']);
$row = self::$model::find($post['id']);
empty($row) && $this->error('数据不存在');
try {
$row->save([

View File

@@ -11,11 +11,7 @@ use app\admin\service\annotation\NodeAnnotation;
use app\Request;
use think\App;
/**
* @ControllerAnnotation(title="角色权限管理")
* Class Auth
* @package app\admin\controller\system
*/
#[ControllerAnnotation(title: '角色权限管理', auth: true)]
class Auth extends AdminController
{
@@ -27,34 +23,30 @@ class Auth extends AdminController
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemAuth();
self::$model = SystemAuth::class;
}
/**
* @NodeAnnotation(title="授权")
*/
#[NodeAnnotation(title: '授权', auth: true)]
public function authorize(Request $request, $id): string
{
$row = $this->model->find($id);
$row = self::$model::find($id);
empty($row) && $this->error('数据不存在');
if ($request->isAjax()) {
$list = $this->model->getAuthorizeNodeListByAdminId($id);
$list = self::$model::getAuthorizeNodeListByAdminId($id);
$this->success('获取成功', $list);
}
$this->assign('row', $row);
return $this->fetch();
}
/**
* @NodeAnnotation(title="授权保存")
*/
#[NodeAnnotation(title: '授权保存', auth: true)]
public function saveAuthorize(Request $request): void
{
$this->checkPostRequest();
$id = $request->post('id');
$node = $request->post('node', "[]");
$node = json_decode($node, true);
$row = $this->model->find($id);
$row = self::$model::find($id);
empty($row) && $this->error('数据不存在');
try {
$authNode = new SystemAuthNode();

View File

@@ -12,33 +12,25 @@ use think\App;
use think\facade\Cache;
use think\response\Json;
/**
* Class Config
* @package app\admin\controller\system
* @ControllerAnnotation(title="系统配置管理")
*/
#[ControllerAnnotation(title: '系统配置管理')]
class Config extends AdminController
{
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemConfig();
self::$model = SystemConfig::class;
$this->assign('upload_types', config('admin.upload_types'));
$this->assign('editor_types', config('admin.editor_types'));
}
/**
* @NodeAnnotation(title="列表")
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
return $this->fetch();
}
/**
* @NodeAnnotation(title="保存")
*/
#[NodeAnnotation(title: '保存', auth: true)]
public function save(Request $request): void
{
$this->checkPostRequest();
@@ -50,25 +42,26 @@ class Config extends AdminController
if ($group == 'upload') {
$upload_types = config('admin.upload_types');
// 兼容旧版本
$this->model->where('name', 'upload_allow_type')->update(['value' => implode(',', array_keys($upload_types))]);
self::$model::where('name', 'upload_allow_type')->update(['value' => implode(',', array_keys($upload_types))]);
}
foreach ($post as $key => $val) {
if (in_array($key, $notAddFields)) continue;
if ($this->model->where('name', $key)->count()) {
$this->model->where('name', $key)->update(['value' => $val,]);
if (self::$model::where('name', $key)->count()) {
self::$model::where('name', $key)->update(['value' => $val,]);
}else {
$this->model->create(
self::$model::create(
[
'name' => $key,
'value' => $val,
'group' => $group,
]);
}
if (Cache::has($key)) Cache::set($key, $val);
}
TriggerService::updateMenu();
TriggerService::updateSysConfig();
}catch (\Exception $e) {
$this->error('保存失败');
$this->error('保存失败' . $e->getMessage());
}
$this->success('保存成功');
}

View File

@@ -3,7 +3,6 @@
namespace app\admin\controller\system;
use app\admin\service\curd\BuildCurd;
use app\admin\service\curd\exceptions\TableException;
use app\common\controller\AdminController;
use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
@@ -15,24 +14,16 @@ use think\facade\Db;
use think\helper\Str;
use think\response\Json;
/**
* @ControllerAnnotation(title="CURD可视化管理")
* @package app\admin\controller\system
*/
#[ControllerAnnotation(title: 'CURD可视化管理')]
class CurdGenerate extends AdminController
{
/**
* @NodeAnnotation(title="列表")
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
return $this->fetch();
}
/**
* @NodeAnnotation(title="操作")
* @throws TableException
*/
#[NodeAnnotation(title: '操作', auth: true)]
public function save(Request $request, string $type = ''): ?Json
{
if (!$request->isAjax()) $this->error();
@@ -146,6 +137,7 @@ class CurdGenerate extends AdminController
$command = $request->post('command', '');
if (empty($command)) $this->error('请输入命令');
$commandExp = explode(' ', $command);
$commandExp = array_values(array_filter($commandExp));
try {
$output = Console::call('curd', [...$commandExp]);

View File

@@ -3,6 +3,7 @@
namespace app\admin\controller\system;
use app\admin\model\SystemLog;
use app\admin\service\annotation\MiddlewareAnnotation;
use app\admin\service\tool\CommonTool;
use app\common\controller\AdminController;
use app\admin\service\annotation\ControllerAnnotation;
@@ -15,24 +16,16 @@ use think\db\exception\PDOException;
use think\facade\Db;
use think\response\Json;
/**
* @ControllerAnnotation(title="操作日志管理")
* Class Auth
* @package app\admin\controller\system
*/
#[ControllerAnnotation(title: '操作日志管理')]
class Log extends AdminController
{
protected array $ignoreLog = ['record'];
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemLog();
self::$model = SystemLog::class;
}
/**
* @NodeAnnotation(title="列表")
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
if ($request->isAjax()) {
@@ -41,7 +34,7 @@ class Log extends AdminController
}
[$page, $limit, $where, $excludeFields] = $this->buildTableParams(['month']);
$month = !empty($excludeFields['month']) ? date('Ym', strtotime($excludeFields['month'])) : date('Ym');
$model = $this->model->setMonth($month)->with('admin')->where($where);
$model = (new self::$model)->setSuffix("_$month")->with('admin')->where($where);
try {
$count = $model->count();
$list = $model->page($page, $limit)->order($this->sort)->select();
@@ -60,16 +53,15 @@ class Log extends AdminController
return $this->fetch();
}
/**
* @NodeAnnotation(title="导出")
*/
public function export(): bool
#[NodeAnnotation(title: '导出', auth: true)]
public function export()
{
if (env('EASYADMIN.IS_DEMO', false)) {
$this->error('演示环境下不允许操作');
}
[$page, $limit, $where, $excludeFields] = $this->buildTableParams(['month']);
$tableName = $this->model->getName();
$month = !empty($excludeFields['month']) ? date('Ym', strtotime($excludeFields['month'])) : date('Ym');
$tableName = (new self::$model)->setSuffix("_$month")->getName();
$tableName = CommonTool::humpToLine(lcfirst($tableName));
$prefix = config('database.connections.mysql.prefix');
$dbList = Db::query("show full columns from {$prefix}{$tableName}");
@@ -80,25 +72,64 @@ class Log extends AdminController
$header[] = [$comment, $vo['Field']];
}
}
$month = !empty($excludeFields['month']) ? date('Ym', strtotime($excludeFields['month'])) : date('Ym');
$model = $this->model->setMonth($month)->with('admin')->where($where);
$model = (new self::$model)->setSuffix("_$month")->with('admin')->where($where);
try {
$list = $model
->where($where)
->limit(100000)
->limit(10000)
->order('id', 'desc')
->select()
->toArray();
}catch (PDOException|DbException $exception) {
foreach ($list as &$vo) {
$vo['content'] = json_encode($vo['content'], JSON_UNESCAPED_UNICODE);
$vo['response'] = json_encode($vo['response'], JSON_UNESCAPED_UNICODE);
}
exportExcel($header, $list, '操作日志');
}catch (\Throwable $exception) {
$this->error($exception->getMessage());
}
$fileName = time();
return Excel::exportData($list, $header, $fileName, 'xlsx');
}
/**
* @NodeAnnotation(title="框架日志")
*/
#[NodeAnnotation(title: '删除指定日志', auth: true)]
public function deleteMonthLog(Request $request)
{
if (!$request->isAjax()) {
return $this->fetch();
}
if ($this->isDemo) $this->error('演示环境下不允许操作');
$monthsAgo = $request->param('month/d', 0);
if ($monthsAgo < 1) $this->error('月份错误');
$currentDate = new \DateTime();
$currentDate->modify("-$monthsAgo months");
$dbPrefix = env('DB_PREFIX');
$dbLike = "{$dbPrefix}system_log_";
$tables = Db::query("SHOW TABLES LIKE '$dbLike%'");
$threshold = date('Ym', strtotime("-$monthsAgo month"));
$tableNames = [];
try {
foreach ($tables as $table) {
$tableName = current($table);
if (!preg_match("/^$dbLike\d{6}$/", $tableName)) continue;
$datePart = substr($tableName, -6);
$issetTable = Db::query("SHOW TABLES LIKE '$tableName'");
if (!$issetTable) continue;
if ($datePart - $threshold <= 0) {
Db::execute("DROP TABLE `$tableName`");
$tableNames[] = $tableName;
}
}
}catch (PDOException) {
}
if (empty($tableNames)) $this->error('没有需要删除的表');
$this->success('操作成功 - 共删除 ' . count($tableNames) . ' 张表<br/>' . implode('<br>', $tableNames));
}
#[MiddlewareAnnotation(ignore: MiddlewareAnnotation::IGNORE_LOG)]
#[NodeAnnotation(title: '框架日志', auth: true, ignore: NodeAnnotation::IGNORE_NODE)]
public function record(): Json|string
{
return (new \Wolfcode\PhpLogviewer\thinkphp\LogViewer())->fetch();

View File

@@ -11,14 +11,9 @@ use app\admin\service\annotation\NodeAnnotation;
use app\common\controller\AdminController;
use app\Request;
use think\App;
use think\db\exception\DbException;
use think\response\Json;
/**
* Class Menu
* @package app\admin\controller\system
* @ControllerAnnotation(title="菜单管理",auth=true)
*/
#[ControllerAnnotation(title: '菜单管理')]
class Menu extends AdminController
{
@@ -30,21 +25,18 @@ class Menu extends AdminController
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemMenu();
self::$model = SystemMenu::class;
}
/**
* @NodeAnnotation(title="列表")
* @throws DbException
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
if ($request->isAjax()) {
if (input('selectFields')) {
return $this->selectList();
}
$count = $this->model->count();
$list = $this->model->order($this->sort)->select()->toArray();
$count = self::$model::count();
$list = self::$model::order($this->sort)->select()->toArray();
$data = [
'code' => 0,
'msg' => '',
@@ -56,13 +48,11 @@ class Menu extends AdminController
return $this->fetch();
}
/**
* @NodeAnnotation(title="添加")
*/
#[NodeAnnotation(title: '添加', auth: true)]
public function add(Request $request): string
{
$id = $request->param('id');
$homeId = $this->model->where(['pid' => MenuConstant::HOME_PID,])->value('id');
$homeId = self::$model::where(['pid' => MenuConstant::HOME_PID,])->value('id');
if ($id == $homeId) {
$this->error('首页不能添加子菜单');
}
@@ -75,7 +65,7 @@ class Menu extends AdminController
];
$this->validate($post, $rule);
try {
$save = $this->model->save($post);
$save = self::$model::create($post);
}catch (\Exception $e) {
$this->error('保存失败');
}
@@ -86,18 +76,16 @@ class Menu extends AdminController
$this->error('保存失败');
}
}
$pidMenuList = $this->model->getPidMenuList();
$pidMenuList = self::$model::getPidMenuList();
$this->assign('id', $id);
$this->assign('pidMenuList', $pidMenuList);
return $this->fetch();
}
/**
* @NodeAnnotation(title="编辑")
*/
#[NodeAnnotation(title: '编辑', auth: true)]
public function edit(Request $request, $id = 0): string
{
$row = $this->model->find($id);
$row = self::$model::find($id);
empty($row) && $this->error('数据不存在');
if ($request->isPost()) {
$post = $request->post();
@@ -120,7 +108,7 @@ class Menu extends AdminController
$this->error('保存失败');
}
}
$pidMenuList = $this->model->getPidMenuList();
$pidMenuList = self::$model::getPidMenuList();
$this->assign([
'id' => $id,
'pidMenuList' => $pidMenuList,
@@ -129,13 +117,12 @@ class Menu extends AdminController
return $this->fetch();
}
/**
* @NodeAnnotation(title="删除")
*/
public function delete($id): void
#[NodeAnnotation(title: '删除', auth: true)]
public function delete(Request $request): void
{
$this->checkPostRequest();
$row = $this->model->whereIn('id', $id)->select();
$id = $request->param('id');
$row = self::$model::whereIn('id', $id)->select();
empty($row) && $this->error('数据不存在');
try {
$save = $row->delete();
@@ -150,9 +137,7 @@ class Menu extends AdminController
}
}
/**
* @NodeAnnotation(title="属性修改")
*/
#[NodeAnnotation(title: '属性修改', auth: true)]
public function modify(Request $request): void
{
$this->checkPostRequest();
@@ -163,17 +148,16 @@ class Menu extends AdminController
'value|值' => 'require',
];
$this->validate($post, $rule);
$row = $this->model->find($post['id']);
$row = self::$model::find($post['id']);
if (!$row) {
$this->error('数据不存在');
}
if (!in_array($post['field'], $this->allowModifyFields)) {
$this->error('该字段不允许修改:' . $post['field']);
}
$homeId = $this->model
->where([
'pid' => MenuConstant::HOME_PID,
])
$homeId = self::$model::where([
'pid' => MenuConstant::HOME_PID,
])
->value('id');
if ($post['id'] == $homeId && $post['field'] == 'status') {
$this->error('首页状态不允许关闭');
@@ -189,9 +173,7 @@ class Menu extends AdminController
$this->success('保存成功');
}
/**
* @NodeAnnotation(title="添加菜单提示")
*/
#[NodeAnnotation(title: '添加菜单提示', auth: true)]
public function getMenuTips(): Json
{
$node = input('get.keywords');

View File

@@ -10,37 +10,30 @@ use app\admin\service\annotation\NodeAnnotation;
use app\admin\service\NodeService;
use app\Request;
use think\App;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
/**
* @ControllerAnnotation(title="系统节点管理")
* Class Node
* @package app\admin\controller\system
*/
#[ControllerAnnotation(title: '系统节点管理')]
class Node extends AdminController
{
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemNode();
self::$model = SystemNode::class;
}
/**
* @NodeAnnotation(title="列表")
* @throws DbException
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
if ($request->isAjax()) {
if (input('selectFields')) {
return $this->selectList();
}
$count = $this->model
->count();
$list = $this->model
->getNodeTreeList();
$count = self::$model::count();
$list = self::$model::getNodeTreeList();
$data = [
'code' => 0,
'msg' => '',
@@ -52,24 +45,21 @@ class Node extends AdminController
return $this->fetch();
}
/**
* @NodeAnnotation(title="系统节点更新")
*/
#[NodeAnnotation(title: '系统节点更新', auth: true)]
public function refreshNode($force = 0): void
{
$this->checkPostRequest();
$nodeList = (new NodeService())->getNodeList();
empty($nodeList) && $this->error('暂无需要更新的系统节点');
$model = new SystemNode();
try {
if ($force == 1) {
$updateNodeList = $model->whereIn('node', array_column($nodeList, 'node'))->select();
$updateNodeList = self::$model::whereIn('node', array_column($nodeList, 'node'))->select();
$formatNodeList = array_format_key($nodeList, 'node');
foreach ($updateNodeList as $vo) {
isset($formatNodeList[$vo['node']])
&& $model->where('id', $vo['id'])->update(
&& self::$model::where('id', $vo['id'])->update(
[
'title' => $formatNodeList[$vo['node']]['title'],
'is_auth' => $formatNodeList[$vo['node']]['is_auth'],
@@ -77,7 +67,7 @@ class Node extends AdminController
);
}
}
$existNodeList = $model->field('node,title,type,is_auth')->select();
$existNodeList = self::$model::field('node,title,type,is_auth')->select();
foreach ($nodeList as $key => $vo) {
foreach ($existNodeList as $v) {
if ($vo['node'] == $v->node) {
@@ -86,27 +76,26 @@ class Node extends AdminController
}
}
}
$model->saveAll($nodeList);
TriggerService::updateNode();
if (!empty($nodeList)) {
(new self::$model)->saveAll($nodeList);
TriggerService::updateNode();
}
}catch (\Exception $e) {
$this->error('节点更新失败');
}
$this->success('节点更新成功');
}
/**
* @NodeAnnotation(title="清除失效节点")
*/
#[NodeAnnotation(title: '清除失效节点', auth: true)]
public function clearNode(): void
{
$this->checkPostRequest();
$nodeList = (new NodeService())->getNodeList();
$model = new SystemNode();
try {
$existNodeList = $model->field('id,node,title,type,is_auth')->select()->toArray();
$existNodeList = self::$model::field('id,node,title,type,is_auth')->select()->toArray();
$formatNodeList = array_format_key($nodeList, 'node');
foreach ($existNodeList as $vo) {
!isset($formatNodeList[$vo['node']]) && $model->where('id', $vo['id'])->delete();
!isset($formatNodeList[$vo['node']]) && self::$model::where('id', $vo['id'])->delete();
}
TriggerService::updateNode();
}catch (\Exception $e) {

View File

@@ -9,11 +9,7 @@ use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use think\App;
/**
* @ControllerAnnotation(title="快捷入口管理")
* Class Quick
* @package app\admin\controller\system
*/
#[ControllerAnnotation(title: '快捷入口管理')]
class Quick extends AdminController
{
@@ -25,7 +21,7 @@ class Quick extends AdminController
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemQuick();
self::$model = SystemQuick::class;
}
}

View File

@@ -8,17 +8,14 @@ use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use think\App;
/**
* @ControllerAnnotation(title="上传文件管理")
* @package app\admin\controller\system
*/
#[ControllerAnnotation(title: '上传文件管理')]
class Uploadfile extends AdminController
{
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new SystemUploadfile();
self::$model = SystemUploadfile::class;
$this->assign('upload_types', config('admin.upload_types'));
}

0
app/admin/entity/.keep Normal file
View File

12
app/admin/entity/Test.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
namespace app\admin\entity;
use app\common\entity\BaseEntity;
/**
* ThinkORM 4.0 实体模型案例
* 可与 Model 并存 或者 单独使用
* @package app\admin\entity
*/
class Test extends BaseEntity {}

View File

@@ -35,7 +35,7 @@ class CheckAuth
!$check && $this->error('无权限访问');
// 判断是否为演示环境
if (env('EASYADMIN.IS_DEMO', false) && $request->isPost()) {
if (!in_array($currentNode, ['system.log/record', ''])) $this->error('演示环境下不允许修改');
if (!in_array($currentNode, ['system.log/record', 'mall.goods/aiOptimization'])) $this->error('演示环境下不允许修改');
}
}
return $next($request);

View File

@@ -7,6 +7,7 @@ use app\Request;
use Closure;
use ReflectionClass;
use ReflectionException;
use app\admin\service\annotation\MiddlewareAnnotation;
class CheckLogin
{
@@ -24,13 +25,25 @@ class CheckLogin
$controllerClass = 'app\\admin\\controller\\' . $controller;
$classObj = new ReflectionClass($controllerClass);
$properties = $classObj->getDefaultProperties();
$ignoreAuth = $properties['ignoreAuth'] ?? false;
$adminUserInfo = session('admin');
if (!$ignoreAuth) {
// 整个控制器是否忽略登录
$ignoreLogin = $properties['ignoreLogin'] ?? false;
$adminUserInfo = session('admin');
if (!$ignoreLogin) {
$noNeedCheck = $properties['noNeedCheck'] ?? [];
if (in_array($action, $noNeedCheck)) {
return $next($request);
}
try {
$reflectionMethod = new \ReflectionMethod($controllerClass, $action);
$attributes = $reflectionMethod->getAttributes(MiddlewareAnnotation::class);
foreach ($attributes as $attribute) {
$annotation = $attribute->newInstance();
$_ignore = (array)$annotation->ignore;
// 控制器中的某个方法忽略登录
if (in_array('LOGIN', $_ignore)) return $next($request);
}
}catch (\Throwable) {
}
if (empty($adminUserInfo)) {
return redirect(__url('login/index'));
}

View File

@@ -0,0 +1,45 @@
<?php
namespace app\admin\middleware;
use app\common\traits\JumpTrait;
use app\Request;
use Closure;
use Wolfcode\RateLimiting\Bootstrap;
class RateLimiting
{
use JumpTrait;
/**
* 启用限流器需要开启Redis
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next): mixed
{
// 是否启用限流器
if (!env('RATE_LIMITING_STATUS', false)) return $next($request);
if ($request->method() == 'GET') return $next($request);
$controller = $request->controller();
$module = app('http')->getName();
$appNamespace = config('app.app_namespace');
$controllerClass = "app\\{$module}\\controller\\{$controller}{$appNamespace}";
$controllerClass = str_replace('.', '\\', $controllerClass);
$action = $request->action();
try {
Bootstrap::init($controllerClass, $action, [
# Redis 相关配置
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => (int)env('REDIS_PORT', 6379),
'password' => env('REDIS_PASSWORD', ''),
'prefix' => env('REDIS_PREFIX', ''),
'database' => (int)env('REDIS_DATABASE', 0),
]);
}catch (\Throwable $exception) {
$this->error($exception->getMessage());
}
return $next($request);
}
}

View File

@@ -3,14 +3,12 @@
namespace app\admin\middleware;
use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\MiddlewareAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use app\admin\service\SystemLogService;
use app\common\traits\JumpTrait;
use app\Request;
use ReflectionClass;
use Closure;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\DocParser;
use ReflectionException;
class SystemLog
@@ -34,7 +32,8 @@ class SystemLog
public function handle(Request $request, Closure $next)
{
$response = $next($request);
$params = $request->param();
if (!env('APP_ADMIN_SYSTEM_LOG', true)) return $response;
$params = $request->param();
if (isset($params['s'])) unset($params['s']);
foreach ($params as $key => $val) {
in_array($key, $this->sensitiveParams) && $params[$key] = "***********";
@@ -56,19 +55,27 @@ class SystemLog
$pathInfoExp = explode('.', $pathInfoExp[0] ?? '');
$_name = $pathInfoExp[0] ?? '';
$_controller = ucfirst($pathInfoExp[1] ?? '');
if ($_name && $_controller && $_action) {
$className = "app\admin\controller\\{$_name}\\{$_controller}";
$reflectionClass = new \ReflectionClass($className);
$properties = $reflectionClass->getDefaultProperties();
$ignoreLog = $properties['ignoreLog'] ?? [];
if (in_array($_action, $ignoreLog)) return $response;
$parser = new DocParser();
$parser->setIgnoreNotImportedAnnotations(true);
$reader = new AnnotationReader($parser);
$controllerAnnotation = $reader->getClassAnnotation($reflectionClass, ControllerAnnotation::class);
$reflectionAction = $reflectionClass->getMethod($_action);
$nodeAnnotation = $reader->getMethodAnnotation($reflectionAction, NodeAnnotation::class);
$title = $controllerAnnotation->title . ' - ' . $nodeAnnotation->title;
$className = $_controller ? "app\admin\controller\\{$_name}\\{$_controller}" : "app\admin\controller\\{$_name}";
if ($_name && $_action) {
$reflectionMethod = new \ReflectionMethod($className, $_action);
$attributes = $reflectionMethod->getAttributes(MiddlewareAnnotation::class);
foreach ($attributes as $attribute) {
$annotation = $attribute->newInstance();
$_ignore = (array)$annotation->ignore;
if (in_array('log', array_map('strtolower', $_ignore))) return $response;
}
$controllerTitle = $nodeTitle = '';
$controllerAttributes = (new \ReflectionClass($className))->getAttributes(ControllerAnnotation::class);
$actionAttributes = $reflectionMethod->getAttributes(NodeAnnotation::class);
foreach ($controllerAttributes as $controllerAttribute) {
$controllerAnnotation = $controllerAttribute->newInstance();
$controllerTitle = $controllerAnnotation->title ?? '';
}
foreach ($actionAttributes as $actionAttribute) {
$actionAnnotation = $actionAttribute->newInstance();
$nodeTitle = $actionAnnotation->title ?? '';
}
$title = $controllerTitle . ' - ' . $nodeTitle;
}
}catch (\Throwable $exception) {
}

View File

@@ -8,6 +8,11 @@ use app\common\model\TimeModel;
class MallCate extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
}

View File

@@ -8,10 +8,12 @@ use think\model\relation\HasOne;
class MallGoods extends TimeModel
{
protected $table = "";
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
// * +++++++++++++++++++++++++++
// | 以下两种写法适用于 with 关联

View File

@@ -8,14 +8,29 @@ use app\common\model\TimeModel;
class SystemAdmin extends TimeModel
{
protected $deleteTime = 'delete_time';
public function getAuthList()
protected function getOptions(): array
{
$list = (new SystemAuth())
->where('status', 1)
->column('title', 'id');
return $list;
return [
'deleteTime' => 'delete_time',
];
}
public array $notes = [
'login_type' => [
1 => '密码登录',
2 => '密码 + 谷歌验证码登录'
],
];
public static function getAuthIdsAttr($value): array
{
if (!$value) return [];
return explode(',', $value);
}
public static function getAuthList(): array
{
return SystemAuth::where('status', 1)->column('title', 'id');
}
}

View File

@@ -3,44 +3,52 @@
namespace app\admin\model;
use app\common\model\TimeModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
class SystemAuth extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
/**
* 根据角色ID获取授权节点
* @param $authId
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function getAuthorizeNodeListByAdminId($authId)
public static function getAuthorizeNodeListByAdminId($authId): array
{
$checkNodeList = (new SystemAuthNode())
->where('auth_id', $authId)
->column('node_id');
$systemNode = new SystemNode();
$nodelList = $systemNode
$systemNode = new SystemNode();
$nodeList = $systemNode
->where('is_auth', 1)
->field('id,node,title,type,is_auth')
->select()
->toArray();
$newNodeList = [];
foreach ($nodelList as $vo) {
$newNodeList = [];
foreach ($nodeList as $vo) {
if ($vo['type'] == 1) {
$vo = array_merge($vo, ['field' => 'node', 'spread' => true]);
$vo = array_merge($vo, ['field' => 'node', 'spread' => true]);
$vo['checked'] = false;
$vo['title'] = "{$vo['title']}{$vo['node']}";
$children = [];
foreach ($nodelList as $v) {
$vo['title'] = "{$vo['title']}{$vo['node']}";
$children = [];
foreach ($nodeList as $v) {
if ($v['type'] == 2 && strpos($v['node'], $vo['node'] . '/') !== false) {
$v = array_merge($v, ['field' => 'node', 'spread' => true]);
$v = array_merge($v, ['field' => 'node', 'spread' => true]);
$v['checked'] = in_array($v['id'], $checkNodeList) ? true : false;
$v['title'] = "{$v['title']}{$v['node']}";
$children[] = $v;
$v['title'] = "{$v['title']}{$v['node']}";
$children[] = $v;
}
}
!empty($children) && $vo['children'] = $children;

View File

@@ -4,24 +4,23 @@ namespace app\admin\model;
use app\admin\service\SystemLogService;
use app\common\model\TimeModel;
use think\model\relation\BelongsTo;
class SystemLog extends TimeModel
{
public function __construct(array $data = [])
{
parent::__construct($data);
$this->name = 'system_log_' . date('Ym');
}
protected array $type = [
'content' => 'json',
'response' => 'json',
];
public function setMonth($month)
protected function init(): void
{
SystemLogService::instance()->detectTable();
$this->name = 'system_log_' . $month;
return $this;
}
public function admin()
public function admin(): BelongsTo
{
return $this->belongsTo('app\admin\model\SystemAdmin', 'admin_id', 'id');
}

View File

@@ -4,31 +4,41 @@ namespace app\admin\model;
use app\common\constants\MenuConstant;
use app\common\model\TimeModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
class SystemMenu extends TimeModel
{
protected $deleteTime = 'delete_time';
public function getPidMenuList()
protected function getOptions(): array
{
$list = $this->field('id,pid,title')
->where([
['pid', '<>', MenuConstant::HOME_PID],
['status', '=', 1],
])
->select()
->toArray();
$pidMenuList = $this->buildPidMenu(0, $list);
$pidMenuList = array_merge([[
return [
'deleteTime' => 'delete_time',
];
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
*/
public static function getPidMenuList(): array
{
$list = self::field('id,pid,title')->where([
['pid', '<>', MenuConstant::HOME_PID],
['status', '=', 1],
])->select()->toArray();
$pidMenuList = self::buildPidMenu(0, $list);
return array_merge([[
'id' => 0,
'pid' => 0,
'title' => '顶级菜单',
]], $pidMenuList);
return $pidMenuList;
}
protected function buildPidMenu($pid, $list, $level = 0)
protected static function buildPidMenu($pid, $list, $level = 0): array
{
$newList = [];
foreach ($list as $vo) {
@@ -47,7 +57,7 @@ class SystemMenu extends TimeModel
$vo['title'] = $markString . $vo['title'];
}
$newList[] = $vo;
$childList = $this->buildPidMenu($vo['id'], $list, $level);
$childList = self::buildPidMenu($vo['id'], $list, $level);
!empty($childList) && $newList = array_merge($newList, $childList);
}

View File

@@ -7,22 +7,21 @@ use app\common\model\TimeModel;
class SystemNode extends TimeModel
{
public function getNodeTreeList()
public static function getNodeTreeList(): array
{
$list = $this->select()->toArray();
$list = $this->buildNodeTree($list);
return $list;
$list = self::select()->toArray();
return self::buildNodeTree($list);
}
protected function buildNodeTree($list)
protected static function buildNodeTree($list): array
{
$newList = [];
$newList = [];
$repeatString = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
foreach ($list as $vo) {
if ($vo['type'] == 1) {
$newList[] = $vo;
foreach ($list as $v) {
if ($v['type'] == 2 && strpos($v['node'], $vo['node'] . '/') !== false) {
if ($v['type'] == 2 && str_contains($v['node'], $vo['node'] . '/')) {
$v['node'] = "{$repeatString}{$repeatString}" . $v['node'];
$newList[] = $v;
}

View File

@@ -7,6 +7,11 @@ use app\common\model\TimeModel;
class SystemQuick extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
}

View File

@@ -9,11 +9,10 @@ class ConfigService
public static function getVersion()
{
$version = cache('version');
$version = cache('site_version');
if (empty($version)) {
$version = sysConfig('site', 'site_version');
cache('site_version', $version);
Cache::set('version', $version, 3600);
}
return $version;
}

View File

@@ -4,6 +4,7 @@ namespace app\admin\service;
use app\admin\model\SystemUploadfile;
use OSS\Core\OssException;
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use think\facade\Env;
use think\file\UploadedFile;
@@ -112,10 +113,21 @@ class UploadService
$accessKeySecret = $config['oss_access_key_secret'];
$endpoint = $config['oss_endpoint'];
$bucket = $config['oss_bucket'];
// 升级 aliyuncs/oss-sdk-php 到 v2.7.2 以上, 使用签名 v4 版本
putenv('OSS_ACCESS_KEY_ID=' . $accessKeyId);
putenv('OSS_ACCESS_KEY_SECRET=' . $accessKeySecret);
$region = str_replace(['http://oss-', 'https://oss-', 'oss-'], '', explode('.aliyuncs.com', $endpoint)[0] ?? '');
$provider = new EnvironmentVariableCredentialsProvider();
$args = [
"provider" => $provider,
"endpoint" => $endpoint,
"signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
"region" => $region
];
if ($file->isValid()) {
$object = $this->setFilePath($file, Env::get('EASYADMIN.OSS_STATIC_PREFIX', 'easyadmin8') . '/');
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
$ossClient = new OssClient($args);
$_rs = $ossClient->putObject($bucket, $object, file_get_contents($file->getRealPath()));
$oss_request_url = $_rs['oss-request-url'] ?? '';
if (empty($oss_request_url)) return ['code' => 0, 'data' => '上传至OSS失败'];
@@ -164,7 +176,7 @@ class UploadService
if (empty($location)) return ['code' => 0, 'data' => '上传至COS失败'];
$location = 'https://' . $location;
$this->setSaveData($file);
} catch (Exception $e) {
}catch (Exception $e) {
return ['code' => 0, 'data' => $e->getMessage()];
}
$data = ['url' => $location];

View File

@@ -2,38 +2,20 @@
namespace app\admin\service\annotation;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use Attribute;
/**
* Class ControllerAnnotation
*
* @Annotation
* @Target("CLASS")
* @Attributes({
* @Attribute("title", type="string"),
* })
*
* @since 2.0
* controller 节点注解类
*/
#[Attribute]
final class ControllerAnnotation
{
/**
* Route group prefix for the controller
*
* @Required()
*
* @var string
* @param string $title
* @param bool $auth 是否需要权限
* @param string|array $ignore
*/
public $title = '';
/**
* 是否开启权限控制
* @Enum({true,false})
* @var bool
*/
public $auth = true;
public function __construct(public string $title = '', public bool $auth = true, public string|array $ignore = '')
{
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace app\admin\service\annotation;
use Attribute;
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
final class MiddlewareAnnotation
{
/** 过滤日志 */
const IGNORE_LOG = 'LOG';
/** 免登录 */
const IGNORE_LOGIN = 'LOGIN';
public function __construct(public string $type = '', public string|array $ignore = '')
{
}
}

View File

@@ -2,32 +2,24 @@
namespace app\admin\service\annotation;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Attribute;
/**
* 创建节点注解类
*
* @Annotation
* @Target({"METHOD","CLASS"})
* @Attributes({
* @Attribute("time", type = "int")
* })
* action 节点注解类
*/
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD| Attribute::TARGET_PROPERTY)]
final class NodeAnnotation
{
/** 过滤节点 */
const IGNORE_NODE = 'NODE';
/**
* 节点名称
* @Required()
* @var string
* @param string $title
* @param bool $auth 是否需要权限
* @param string|array $ignore
*/
public string $title;
/**
* 是否开启权限控制
* @Enum({true,false})
* @var bool
*/
public bool $auth = true;
public function __construct(public string $title = '', public bool $auth = true, public string|array $ignore = '')
{
}
}

View File

@@ -67,15 +67,23 @@ class Node
// 遍历读取所有方法的注释的参数信息
foreach ($methods as $method) {
// 读取NodeAnnotation的注解
$nodeAnnotation = $reader->getMethodAnnotation($method, NodeAnnotation::class);
if (!empty($nodeAnnotation)) {
$actionTitle = !empty($nodeAnnotation->title) ? $nodeAnnotation->title : null;
$actionAuth = !empty($nodeAnnotation->auth) ? $nodeAnnotation->auth : false;
// 忽略掉不需要的节点
$property = $reflectionClass->getProperty('ignoreNode');
$propertyAttributes = $property->getAttributes(NodeAnnotation::class);
if (!empty($propertyAttributes[0])) {
$propertyAttribute = $propertyAttributes[0]->newInstance();
if (in_array($method->name, $propertyAttribute->ignore)) continue;
}
$attributes = $reflectionClass->getMethod($method->name)->getAttributes(NodeAnnotation::class);
foreach ($attributes as $attribute) {
$annotation = $attribute->newInstance();
if (!empty($annotation->ignore)) if (strtolower($annotation->ignore) == 'node') continue;
$actionList[] = [
'node' => $controllerFormat . '/' . $method->name,
'title' => $actionTitle,
'is_auth' => $actionAuth,
'title' => $annotation->title ?? null,
'is_auth' => $annotation->auth ?? false,
'type' => 2,
];
}
@@ -83,16 +91,17 @@ class Node
// 方法非空才读取控制器注解
if (!empty($actionList)) {
// 读取Controller的注解
$controllerAnnotation = $reader->getClassAnnotation($reflectionClass, ControllerAnnotation::class);
$controllerTitle = !empty($controllerAnnotation->title) ? $controllerAnnotation->title : null;
$controllerAuth = !empty($controllerAnnotation->auth) ? $controllerAnnotation->auth : false;
$nodeList[] = [
'node' => $controllerFormat,
'title' => $controllerTitle,
'is_auth' => $controllerAuth,
'type' => 1,
];
$nodeList = array_merge($nodeList, $actionList);
$attributes = $reflectionClass->getAttributes(ControllerAnnotation::class);
foreach ($attributes as $attribute) {
$controllerAnnotation = $attribute->newInstance();
$nodeList[] = [
'node' => $controllerFormat,
'title' => $controllerAnnotation->title ?? null,
'is_auth' => $controllerAnnotation->auth ?? false,
'type' => 1,
];
}
$nodeList = array_merge($nodeList, $actionList);
}
}

View File

@@ -300,7 +300,7 @@ class BuildCurd
}
}
$this->tableComment = $this->table;
}catch (Exception $e) {
} catch (Exception $e) {
throw new TableException($e->getMessage());
}
@@ -308,11 +308,11 @@ class BuildCurd
$nodeArray = explode('_', $this->table);
if (count($nodeArray) == 1) {
$this->controllerFilename = ucfirst($nodeArray[0]);
}else {
} else {
foreach ($nodeArray as $k => $v) {
if ($k == 0) {
$this->controllerFilename = "{$v}{$this->DS}";
}else {
} else {
$this->controllerFilename .= ucfirst($v);
}
}
@@ -355,6 +355,7 @@ class BuildCurd
if (!empty($bindSelectField) && !in_array($bindSelectField, array_column($columns, 'Field'))) {
throw new TableException("关联表{$relationTable}不存在该字段: {$bindSelectField}");
}
$onlyFields = [];
foreach ($columns as $vo) {
if (empty($primaryKey) && $vo['Key'] == 'PRI') {
$primaryKey = $vo['Field'];
@@ -362,6 +363,7 @@ class BuildCurd
if (!empty($onlyShowFields) && !in_array($vo['Field'], $onlyShowFields)) {
continue;
}
if (!empty($onlyShowFields)) $onlyFields[] = $vo['Field'];
$colum = [
'type' => $vo['Type'],
'comment' => $vo['Comment'],
@@ -388,15 +390,16 @@ class BuildCurd
'bindSelectField' => $bindSelectField,
'delete' => $delete,
'tableColumns' => $formatColumns,
'onlyFields' => $onlyFields,
];
if (!empty($bindSelectField)) {
$relationArray = explode('\\', $modelFilename);
$this->tableColumns[$foreignKey]['bindSelectField'] = $bindSelectField;
$this->tableColumns[$foreignKey]['bindRelation'] = end($relationArray);
$this->tableColumns[$foreignKey]['bindRelation'] = lcfirst(end($relationArray)) . ucfirst($bindSelectField);
}
$this->relationArray[$relationTable] = $relation;
$this->selectFields[] = $foreignKey;
}catch (Exception $e) {
} catch (Exception $e) {
throw new TableException($e->getMessage());
}
return $this;
@@ -652,7 +655,6 @@ class BuildCurd
if (in_array($key, ['describe', 'content', 'details'])) {
$this->editorFields[] = $key;
}
}
return $this;
}
@@ -706,7 +708,7 @@ class BuildCurd
}
}
!empty($formatDefine) && $colum['define'] = $formatDefine;
}else {
} else {
$colum['define'] = $define;
}
}
@@ -730,7 +732,8 @@ class BuildCurd
$this->getTemplate("controller{$this->DS}select"),
[
'name' => $name,
]);
]
);
return $selectCode;
}
@@ -754,7 +757,8 @@ class BuildCurd
[
'name' => $name,
'values' => $values,
]);
]
);
return $selectCode;
}
@@ -775,7 +779,8 @@ class BuildCurd
'name' => "notes['$field']",
'relation' => $relation,
'values' => $field,
]);
]
);
return $selectCode;
}
@@ -794,7 +799,8 @@ class BuildCurd
[
'name' => "notes['$field']",
'select' => $select,
]);
]
);
}
/**
@@ -813,7 +819,8 @@ class BuildCurd
'field' => $field,
'name' => "notes['$field']",
'select' => $select,
]);
]
);
}
/**
@@ -832,7 +839,8 @@ class BuildCurd
'field' => $field,
'name' => "notes['$field']",
'select' => $select,
]);
]
);
}
/**
@@ -945,7 +953,6 @@ class BuildCurd
$this->tableColumns[$field]['formType'] = 'select';
continue;
}
}
// 关联表
@@ -1018,7 +1025,6 @@ class BuildCurd
}
return $this;
}
/**
@@ -1027,20 +1033,25 @@ class BuildCurd
*/
protected function renderController(): static
{
$controllerFile = "{$this->rootDir}app{$this->DS}admin{$this->DS}controller{$this->DS}{$this->controllerFilename}.php";
$controllerFile = "{$this->rootDir}app{$this->DS}admin{$this->DS}controller{$this->DS}{$this->controllerFilename}.php";
$constructRelation = '';
if (empty($this->relationArray)) {
$controllerIndexMethod = '';
}else {
} else {
$relationCode = '';
foreach ($this->relationArray as $key => $val) {
$relation = CommonTool::lineToHump($key);
$relationCode = "->withJoin('{$relation}', 'LEFT')\r";
$relationCode = "with(['{$relation}'])";
if (!empty($val['bindSelectField']) && !empty($val['primaryKey'])) {
$constructRelation = '$notes["' . lcfirst($val['foreignKey']) . '"] = \app\admin\model\\' . $val['modelFilename'] . '::column("' . $val['bindSelectField'] . '", "' . $val['primaryKey'] . '");';
}
}
$controllerIndexMethod = CommonTool::replaceTemplate(
$this->getTemplate("controller{$this->DS}indexMethod"),
[
'relationIndexMethod' => $relationCode,
]);
]
);
}
$selectList = '';
// foreach ($this->relationArray as $relation) {
@@ -1066,7 +1077,9 @@ class BuildCurd
'modelFilename' => "\app\admin\model\\{$modelFilenameExtend}",
'indexMethod' => $controllerIndexMethod,
'selectList' => $selectList,
]);
'constructRelation' => $constructRelation,
]
);
$this->fileList[$controllerFile] = $controllerValue;
return $this;
}
@@ -1087,9 +1100,10 @@ class BuildCurd
$this->getTemplate("model{$this->DS}relation"),
[
'relationMethod' => $relation,
'relationModel' => "\app\admin\model\\{$val['modelFilename']}",
'foreignKey' => $val['foreignKey'],
'primaryKey' => $val['primaryKey'],
'relationModel' => "{$val['modelFilename']}::class",
'foreignKey' => $val['primaryKey'],
'primaryKey' => $val['foreignKey'],
'relationFields' => empty($val['onlyFields']) ? "" : "->field('{$val['primaryKey']}," . implode(',', $val['onlyFields']) . "')",
]);
$relationList .= $relationCode;
}
@@ -1124,7 +1138,9 @@ class BuildCurd
'relationList' => $relationList,
// 'selectList' => $selectList,
'selectArrays' => CommonTool::replaceArrayString(var_export($selectArrays, true)),
]);
]
);
$this->fileList[$modelFile] = $modelValue;
@@ -1153,11 +1169,14 @@ class BuildCurd
[
'modelName' => $val['modelName'],
'modelNamespace' => "app\admin\model{$extendNamespace}",
'prefix_table' => $this->tablePrefix == config('database.connections.mysql.prefix') ? "" : $this->tablePrefix . $this->table,
'table' => $key,
'deleteTime' => $val['delete'] ? '"delete_time"' : 'false',
'relationList' => '',
'selectList' => '',
]);
'selectArrays' => "[]",
]
);
$this->fileList[$relationModelFile] = $relationModelValue;
}
return $this;
@@ -1176,7 +1195,8 @@ class BuildCurd
[
'controllerUrl' => $this->controllerUrl,
'notesScript' => $this->formatNotesScript(),
]);
]
);
$this->fileList[$viewIndexFile] = $viewIndexValue;
// 添加页面
@@ -1194,42 +1214,44 @@ class BuildCurd
// 根据formType去获取具体模板
if ($val['formType'] == 'image') {
$templateFile = "view{$this->DS}module{$this->DS}image";
}elseif ($val['formType'] == 'images') {
} elseif ($val['formType'] == 'images') {
$templateFile = "view{$this->DS}module{$this->DS}images";
$define = $val['define'] ?? '|';
}elseif ($val['formType'] == 'file') {
} elseif ($val['formType'] == 'file') {
$templateFile = "view{$this->DS}module{$this->DS}file";
}elseif ($val['formType'] == 'files') {
} elseif ($val['formType'] == 'files') {
$templateFile = "view{$this->DS}module{$this->DS}files";
$define = $val['define'] ?? '|';
}elseif ($val['formType'] == 'editor') {
} elseif ($val['formType'] == 'editor') {
$templateFile = "view{$this->DS}module{$this->DS}editor";
$val['default'] = '""';
}elseif ($val['formType'] == 'date') {
} elseif ($val['formType'] == 'date') {
$templateFile = "view{$this->DS}module{$this->DS}date";
$define = 'date';
}elseif ($val['formType'] == 'datetime') {
} elseif ($val['formType'] == 'datetime') {
$templateFile = "view{$this->DS}module{$this->DS}date";
$define = 'datetime';
}elseif ($val['formType'] == 'radio') {
} elseif ($val['formType'] == 'radio') {
$templateFile = "view{$this->DS}module{$this->DS}radio";
if (!empty($val['define'])) {
$define = $this->buildRadioView($field, '{in name="k" value="' . $val['default'] . '"}checked=""{/in}');
}
}elseif ($val['formType'] == 'checkbox') {
} elseif ($val['formType'] == 'checkbox') {
$templateFile = "view{$this->DS}module{$this->DS}checkbox";
if (!empty($val['define'])) {
$define = $this->buildCheckboxView($field, '{in name="k" value="' . $val['default'] . '"}checked=""{/in}');
}
}elseif ($val['formType'] == 'select') {
} elseif ($val['formType'] == 'select') {
$templateFile = "view{$this->DS}module{$this->DS}select";
if (isset($val['bindRelation'])) {
$define = $this->buildOptionView($val['bindRelation']);
}elseif (!empty($val['define'])) {
$define = $this->buildOptionView($field);
} elseif (!empty($val['define'])) {
$define = $this->buildOptionView($field);
}
}elseif ($field == 'remark' || $val['formType'] == 'textarea') {
} elseif ($field == 'remark' || $val['formType'] == 'textarea') {
$templateFile = "view{$this->DS}module{$this->DS}textarea";
} elseif ($field == 'sort') {
$templateFile = "view{$this->DS}module{$this->DS}sort";
}
$addFormList .= CommonTool::replaceTemplate(
$this->getTemplate($templateFile),
@@ -1239,13 +1261,15 @@ class BuildCurd
'required' => $this->buildRequiredHtml($val['required']),
'value' => $val['default'],
'define' => $define,
]);
]
);
}
$viewAddValue = CommonTool::replaceTemplate(
$this->getTemplate("view{$this->DS}form"),
[
'formList' => $addFormList,
]);
]
);
$this->fileList[$viewAddFile] = $viewAddValue;
@@ -1266,43 +1290,44 @@ class BuildCurd
// 根据formType去获取具体模板
if ($val['formType'] == 'image') {
$templateFile = "view{$this->DS}module{$this->DS}image";
}elseif ($val['formType'] == 'images') {
} elseif ($val['formType'] == 'images') {
$templateFile = "view{$this->DS}module{$this->DS}images";
}elseif ($val['formType'] == 'file') {
} elseif ($val['formType'] == 'file') {
$templateFile = "view{$this->DS}module{$this->DS}file";
}elseif ($val['formType'] == 'files') {
} elseif ($val['formType'] == 'files') {
$templateFile = "view{$this->DS}module{$this->DS}files";
}elseif ($val['formType'] == 'editor') {
} elseif ($val['formType'] == 'editor') {
$templateFile = "view{$this->DS}module{$this->DS}editor";
$value = '$row["' . $field . '"]';
}elseif ($val['formType'] == 'date') {
} elseif ($val['formType'] == 'date') {
$templateFile = "view{$this->DS}module{$this->DS}date";
$define = 'date';
}elseif ($val['formType'] == 'datetime') {
} elseif ($val['formType'] == 'datetime') {
$templateFile = "view{$this->DS}module{$this->DS}date";
$define = 'datetime';
}elseif ($val['formType'] == 'radio') {
} elseif ($val['formType'] == 'radio') {
$templateFile = "view{$this->DS}module{$this->DS}radio";
if (!empty($val['define'])) {
$define = $this->buildRadioView($field, '{in name="k" value="$row.' . $field . '"}checked=""{/in}');
}
}elseif ($val['formType'] == 'checkbox') {
} elseif ($val['formType'] == 'checkbox') {
$templateFile = "view{$this->DS}module{$this->DS}checkbox";
if (!empty($val['define'])) {
$define = $this->buildCheckboxView($field, '{in name="k" value="$row.' . $field . '"}checked=""{/in}');
}
}elseif ($val['formType'] == 'select') {
} elseif ($val['formType'] == 'select') {
$templateFile = "view{$this->DS}module{$this->DS}select";
if (isset($val['bindRelation'])) {
$define = $this->buildOptionView($val['bindRelation'], '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
}elseif (!empty($val['define'])) {
$define = $this->buildOptionView($field, '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
} elseif (!empty($val['define'])) {
$define = $this->buildOptionView($field, '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
}
}elseif ($field == 'remark' || $val['formType'] == 'textarea') {
} elseif ($field == 'remark' || $val['formType'] == 'textarea') {
$templateFile = "view{$this->DS}module{$this->DS}textarea";
$value = '{$row.' . $field . '|raw|default=\'\'}';
} elseif ($field == 'sort') {
$templateFile = "view{$this->DS}module{$this->DS}sort";
}
$editFormList .= CommonTool::replaceTemplate(
$this->getTemplate($templateFile),
[
@@ -1311,15 +1336,26 @@ class BuildCurd
'required' => $this->buildRequiredHtml($val['required']),
'value' => $value,
'define' => $define,
]);
]
);
}
$viewEditValue = CommonTool::replaceTemplate(
$this->getTemplate("view{$this->DS}form"),
[
'formList' => $editFormList,
]);
]
);
$this->fileList[$viewEditFile] = $viewEditValue;
$viewRecycleFile = "{$this->rootDir}app{$this->DS}admin{$this->DS}view{$this->DS}{$this->viewFilename}{$this->DS}recycle.html";
$viewRecycleValue = CommonTool::replaceTemplate(
$this->getTemplate("view{$this->DS}recycle"),
[
'controllerUrl' => $this->controllerUrl,
'notesScript' => $this->formatNotesScript(),
]
);
$this->fileList[$viewRecycleFile] = $viewRecycleValue;
return $this;
}
@@ -1338,31 +1374,33 @@ class BuildCurd
if ($val['formType'] == 'image') {
$templateValue = "{field: '{$field}', title: '{$val['comment']}', templet: ea.table.image}";
}elseif ($val['formType'] == 'images') {
} elseif ($val['formType'] == 'datetime') {
$templateValue = "{field: '{$field}', search: 'range', title: '{$val['comment']}'}";
} elseif ($val['formType'] == 'images') {
continue;
}elseif ($val['formType'] == 'file') {
} elseif ($val['formType'] == 'file') {
$templateValue = "{field: '{$field}', title: '{$val['comment']}', templet: ea.table.url}";
}elseif ($val['formType'] == 'files') {
} elseif ($val['formType'] == 'files') {
continue;
}elseif ($val['formType'] == 'editor') {
} elseif ($val['formType'] == 'editor') {
continue;
}elseif (in_array($field, $this->switchFields)) {
} elseif (in_array($field, $this->switchFields)) {
if (!empty($val['define'])) {
$templateValue = "{field: '{$field}', search: 'select', selectList: notes?.{$field} || {}, title: '{$val['comment']}', templet: ea.table.switch}";
}else {
} else {
$templateValue = "{field: '{$field}', title: '{$val['comment']}', templet: ea.table.switch}";
}
}elseif (in_array($val['formType'], ['select', 'checkbox', 'radio', 'switch'])) {
} elseif (in_array($val['formType'], ['select', 'checkbox', 'radio', 'switch'])) {
if (!empty($val['define'])) {
$templateValue = "{field: '{$field}', search: 'select', selectList: notes?.{$field} || {}, title: '{$val['comment']}'}";
}else {
} else {
$templateValue = "{field: '{$field}', title: '{$val['comment']}'}";
}
}elseif ($field == 'remark') {
} elseif ($field == 'remark') {
$templateValue = "{field: '{$field}', title: '{$val['comment']}', templet: ea.table.text}";
}elseif (in_array($field, $this->sortFields)) {
} elseif (in_array($field, $this->sortFields)) {
$templateValue = "{field: '{$field}', title: '{$val['comment']}', edit: 'text'}";
}else {
} else {
$templateValue = "{field: '{$field}', title: '{$val['comment']}'}";
}
@@ -1375,38 +1413,41 @@ class BuildCurd
foreach ($tableVal['tableColumns'] as $field => $val) {
if ($val['formType'] == 'image') {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}', templet: ea.table.image}";
}elseif ($val['formType'] == 'images') {
} elseif ($val['formType'] == 'images') {
continue;
}elseif ($val['formType'] == 'file') {
} elseif ($val['formType'] == 'file') {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}', templet: ea.table.url}";
}elseif ($val['formType'] == 'files') {
} elseif ($val['formType'] == 'files') {
continue;
}elseif ($val['formType'] == 'editor') {
} elseif ($val['formType'] == 'editor') {
continue;
}elseif ($val['formType'] == 'select') {
} elseif ($val['formType'] == 'select') {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}'}";
}elseif ($field == 'remark') {
} elseif ($field == 'remark') {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}', templet: ea.table.text}";
}elseif (in_array($field, $this->switchFields)) {
} elseif (in_array($field, $this->switchFields)) {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}', templet: ea.table.switch}";
}elseif (in_array($field, $this->sortFields)) {
} elseif (in_array($field, $this->sortFields)) {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}', edit: 'text'}";
}else {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}'}";
} else {
$templateValue = "";
}
$indexCols .= $this->formatColsRow("{$templateValue},\r");
if ($templateValue) $indexCols .= $this->formatColsRow("{$templateValue},\r");
}
}
$indexCols .= $this->formatColsRow("{width: 250, title: '操作', templet: ea.table.tool},\r");
$recycleCols = $indexCols;
$indexCols .= $this->formatColsRow("{width: 250, title: '操作', templet: ea.table.tool},\r");
$jsValue = CommonTool::replaceTemplate(
$this->getTemplate("static{$this->DS}js"),
[
'controllerUrl' => $this->controllerUrl,
'indexCols' => $indexCols,
]);
'recycleCols' => $recycleCols,
]
);
$this->fileList[$jsFile] = $jsValue;
return $this;
}
@@ -1531,4 +1572,4 @@ class BuildCurd
{
return ' let notes = JSON.parse(\'{$notes|json_encode=256|raw}\');';
}
}
}

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types = 1);
namespace EasyAdmin\curd\exceptions;
namespace app\admin\service\curd\exceptions;
class CurdException extends \Exception
{
}

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types = 1);
namespace EasyAdmin\curd\exceptions;
namespace app\admin\service\curd\exceptions;
class FileException extends \Exception
{
}

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types = 1);
namespace app\admin\service\curd\exceptions;
class TableException extends \Exception
{
}

View File

@@ -7,9 +7,7 @@ use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use think\App;
/**
* @ControllerAnnotation(title="{{controllerAnnotation}}")
*/
#[ControllerAnnotation(title: '{{controllerAnnotation}}')]
class {{controllerName}} extends AdminController
{
@@ -18,8 +16,10 @@ class {{controllerName}} extends AdminController
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new {{modelFilename}}();
$this->notes = $notes = $this->model->notes;
self::$model = {{modelFilename}}::class;
$notes = self::$model::$notes;
{{constructRelation}}
$this->notes =$notes;
$this->assign(compact('notes'));
}

View File

@@ -1,17 +1,15 @@
/**
* @NodeAnnotation(title="列表")
*/
public function index()
#[NodeAnnotation(title: '列表', auth: true)]
public function index(\app\Request $request): \think\response\Json|string
{
if ($this->request->isAjax()) {
if ($request->isAjax()) {
if (input('selectFields')) {
return $this->selectList();
}
list($page, $limit, $where) = $this->buildTableParams();
$count = $this->model{{relationIndexMethod}}->where($where)->count();
$list = $this->model{{relationIndexMethod}}->where($where)->page($page, $limit)->order($this->sort)->select()->toArray();
$data = [
$count = self::$model::where($where)->count();
$list = self::$model::where($where)->{{relationIndexMethod}}->page($page, $limit)->order($this->sort)->select()->toArray();
$data = [
'code' => 0,
'msg' => '',
'count' => $count,

View File

@@ -7,12 +7,17 @@ use app\common\model\TimeModel;
class {{modelName}} extends TimeModel
{
protected $name = "{{table}}";
protected function getOptions(): array
{
return [
'name' => "{{table}}",
'table' => "{{prefix_table}}",
'deleteTime' => {{deleteTime}},
];
}
protected $table = "{{prefix_table}}";
public static array $notes = {{selectArrays}};
protected $deleteTime = {{deleteTime}};
public array $notes = {{selectArrays}};
{{relationList}}
}

View File

@@ -1,5 +1,5 @@
public function {{relationMethod}}()
{
return $this->belongsTo('{{relationModel}}', '{{foreignKey}}', '{{primaryKey}}');
return $this->hasOne({{relationModel}}, '{{foreignKey}}', '{{primaryKey}}'){{relationFields}};
}

View File

@@ -9,6 +9,7 @@ define(["jquery", "easy-admin"], function ($, ea) {
delete_url: '{{controllerUrl}}/delete',
export_url: '{{controllerUrl}}/export',
modify_url: '{{controllerUrl}}/modify',
recycle_url: '{{controllerUrl}}/recycle',
};
return {
@@ -29,5 +30,62 @@ define(["jquery", "easy-admin"], function ($, ea) {
edit: function () {
ea.listen();
},
recycle: function () {
init.index_url = init.recycle_url;
ea.table.render({
init: init,
toolbar: ['refresh',
[{
class: 'layui-btn layui-btn-sm',
method: 'get',
field: 'id',
icon: 'fa fa-refresh',
text: '全部恢复',
title: '确定恢复?',
auth: 'recycle',
url: init.recycle_url + '?type=restore',
checkbox: true
}, {
class: 'layui-btn layui-btn-danger layui-btn-sm',
method: 'get',
field: 'id',
icon: 'fa fa-delete',
text: '彻底删除',
title: '确定彻底删除?',
auth: 'recycle',
url: init.recycle_url + '?type=delete',
checkbox: true
}], 'export',
],
cols: [[
{{recycleCols}}
{
width: 250,
title: '操作',
templet: ea.table.tool,
operat: [
[{
title: '确认恢复?',
text: '恢复数据',
filed: 'id',
url: init.recycle_url + '?type=restore',
method: 'get',
auth: 'recycle',
class: 'layui-btn layui-btn-xs layui-btn-success',
}, {
title: '想好了吗?',
text: '彻底删除',
filed: 'id',
method: 'get',
url: init.recycle_url + '?type=delete',
auth: 'recycle',
class: 'layui-btn layui-btn-xs layui-btn-normal layui-bg-red',
}]]
}
]],
});
ea.listen();
},
};
});

View File

@@ -4,7 +4,9 @@
data-auth-add="{:auth('{{controllerUrl}}/add')}"
data-auth-edit="{:auth('{{controllerUrl}}/edit')}"
data-auth-delete="{:auth('{{controllerUrl}}/delete')}"
data-auth-recycle="{:auth('{{controllerUrl}}/recycle')}"
lay-filter="currentTable">
<!-- searchTableShow="false" 隐藏搜索框 -->
</table>
</div>
</div>

View File

@@ -0,0 +1,7 @@
<div class="layui-form-item">
<label class="layui-form-label">{{comment}}</label>
<div class="layui-input-block">
<input type="number" name="{{field}}" class="layui-input" lay-affix="number" {{required}} placeholder="请输入{{comment}}" value="{{value}}">
</div>
</div>

View File

@@ -0,0 +1,13 @@
<div class="layuimini-container">
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
data-auth-recycle="{:auth('{{controllerUrl}}/recycle')}"
lay-filter="currentTable">
<!-- searchTableShow="false" 隐藏搜索框 -->
</table>
</div>
</div>
<script>
{{notesScript}}
</script>

View File

@@ -100,6 +100,9 @@ class CommonTool
{
$arrayString = str_replace('array (', '[', $arrayString);
$arrayString = str_replace(')', ']', $arrayString);
$arrayString = str_replace('=>
[', '=> [', $arrayString);
return $arrayString;
}
}

View File

@@ -5,7 +5,7 @@ namespace app\admin\traits;
use app\admin\service\annotation\NodeAnnotation;
use app\admin\service\tool\CommonTool;
use app\Request;
use jianyan\excel\Excel;
use think\db\exception\PDOException;
use think\facade\Db;
use think\response\Json;
@@ -17,9 +17,7 @@ use think\response\Json;
trait Curd
{
/**
* @NodeAnnotation(title="列表")
*/
#[NodeAnnotation(title: '列表', auth: true)]
public function index(Request $request): Json|string
{
if ($request->isAjax()) {
@@ -27,8 +25,8 @@ trait Curd
return $this->selectList();
}
list($page, $limit, $where) = $this->buildTableParams();
$count = $this->model->where($where)->count();
$list = $this->model->where($where)->page($page, $limit)->order($this->sort)->select()->toArray();
$count = self::$model::where($where)->count();
$list = self::$model::where($where)->page($page, $limit)->order($this->sort)->select()->toArray();
$data = [
'code' => 0,
'msg' => '',
@@ -40,9 +38,7 @@ trait Curd
return $this->fetch();
}
/**
* @NodeAnnotation(title="添加")
*/
#[NodeAnnotation(title: '添加', auth: true)]
public function add(Request $request): string
{
if ($request->isPost()) {
@@ -50,8 +46,8 @@ trait Curd
$rule = [];
$this->validate($post, $rule);
try {
Db::transaction(function () use ($post, &$save) {
$save = $this->model->save($post);
Db::transaction(function() use ($post, &$save) {
$save = self::$model::create($post);
});
}catch (\Exception $e) {
$this->error('新增失败:' . $e->getMessage());
@@ -61,19 +57,17 @@ trait Curd
return $this->fetch();
}
/**
* @NodeAnnotation(title="编辑")
*/
#[NodeAnnotation(title: '编辑', auth: true)]
public function edit(Request $request, $id = 0): string
{
$row = $this->model->find($id);
$row = self::$model::find($id);
empty($row) && $this->error('数据不存在');
if ($request->isPost()) {
$post = $request->post();
$rule = [];
$this->validate($post, $rule);
try {
Db::transaction(function () use ($post, $row, &$save) {
Db::transaction(function() use ($post, $row, &$save) {
$save = $row->save($post);
});
}catch (\Exception $e) {
@@ -85,13 +79,13 @@ trait Curd
return $this->fetch();
}
/**
* @NodeAnnotation(title="删除")
*/
public function delete($id): void
#[NodeAnnotation(title: '删除', auth: true)]
public function delete(Request $request): void
{
// 如果不是id作为主键 请在对应的控制器中覆盖重写
$id = $request->param('id', []);
$this->checkPostRequest();
$row = $this->model->whereIn('id', $id)->select();
$row = self::$model::whereIn('id', $id)->select();
$row->isEmpty() && $this->error('数据不存在');
try {
$save = $row->delete();
@@ -101,16 +95,14 @@ trait Curd
$save ? $this->success('删除成功') : $this->error('删除失败');
}
/**
* @NodeAnnotation(title="导出")
*/
#[NodeAnnotation(title: '导出', auth: true)]
public function export()
{
if (env('EASYADMIN.IS_DEMO', false)) {
$this->error('演示环境下不允许操作');
}
list($page, $limit, $where) = $this->buildTableParams();
$tableName = $this->model->getName();
$tableName = (new self::$model)->getName();
$tableName = CommonTool::humpToLine(lcfirst($tableName));
$prefix = config('database.connections.mysql.prefix');
$dbList = Db::query("show full columns from {$prefix}{$tableName}");
@@ -121,19 +113,19 @@ trait Curd
$header[] = [$comment, $vo['Field']];
}
}
$list = $this->model
->where($where)
$list = self::$model::where($where)
->limit(100000)
->order('id', 'desc')
->order($this->sort)
->select()
->toArray();
$fileName = time();
return Excel::exportData($list, $header, $fileName, 'xlsx');
try {
exportExcel($header, $list);
}catch (\Throwable $exception) {
$this->error('导出失败: ' . $exception->getMessage() . PHP_EOL . $exception->getFile() . PHP_EOL . $exception->getLine());
}
}
/**
* @NodeAnnotation(title="属性修改")
*/
#[NodeAnnotation(title: '属性修改', auth: true)]
public function modify(Request $request): void
{
$this->checkPostRequest();
@@ -144,7 +136,7 @@ trait Curd
'value|值' => 'require',
];
$this->validate($post, $rule);
$row = $this->model->find($post['id']);
$row = self::$model::find($post['id']);
if (!$row) {
$this->error('数据不存在');
}
@@ -152,7 +144,7 @@ trait Curd
$this->error('该字段不允许修改:' . $post['field']);
}
try {
Db::transaction(function () use ($post, $row) {
Db::transaction(function() use ($post, $row) {
$row->save([
$post['field'] => $post['value'],
]);
@@ -163,4 +155,49 @@ trait Curd
$this->success('保存成功');
}
#[NodeAnnotation(title: '回收站', auth: true)]
public function recycle(Request $request): Json|string
{
if (!$request->isAjax()) {
return $this->fetch();
}
$id = $request->param('id', []);
$type = $request->param('type', '');
$deleteTimeField = (new self::$model)->getOption('deleteTime'); // 获取软删除字段
$defaultErrorMsg = 'Model 中未设置软删除 deleteTime 对应字段 或 数据表中不存在该字段';
if (!$deleteTimeField) $this->success($defaultErrorMsg);
switch ($type) {
case 'restore':
self::$model::withTrashed()->whereIn('id', $id)->strict(false)->update([$deleteTimeField => null, 'update_time' => time()]);
$this->success('success');
break;
case 'delete':
self::$model::destroy($id, true);
$this->success('success');
break;
default:
list($page, $limit, $where) = $this->buildTableParams();
try {
$count = self::$model::withTrashed()->where($where)->whereNotNull($deleteTimeField)->count();
$list = self::$model::withTrashed()->where($where)->page($page, $limit)->order($this->sort)->whereNotNull($deleteTimeField)->select()->toArray();
$data = [
'code' => 0,
'msg' => '',
'count' => $count,
'data' => $list,
];
} catch (\Throwable $e) {
$error = $e->getMessage();
if ($e instanceof PDOException) $error .= '<br>' . $defaultErrorMsg;
$data = [
'code' => -1,
'msg' => $error,
'count' => 0,
'data' => [],
];
}
return json($data);
}
}
}

View File

@@ -30,6 +30,15 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">登录方式</label>
<div class="layui-input-block">
{foreach notes.login_type as $key=>$val}
<input type="radio" name="login_type" lay-skin="primary" title="{$val}" value="{$key}" lay-filter="loginType-filter" {if $key==$row.login_type}checked=""{/if}>
{/foreach}
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">备注信息</label>
<div class="layui-input-block">

View File

@@ -26,15 +26,6 @@
</ul>
<ul class="layui-nav layui-layout-right">
<!-- <li class="layui-nav-item" lay-unselect>-->
<!-- <div class="layui-form ws-header-theme" lay-filter="header-theme">-->
<!-- <input type="checkbox" name="theme-mode" id="ID-header-theme-mode" lay-filter="header-theme-mode" lay-skin="switch">-->
<!-- <div lay-checkbox>-->
<!-- <i class="layui-icon layui-icon-moon"></i> |-->
<!-- <i class="layui-icon layui-icon-light"></i>-->
<!-- </div>-->
<!-- </div>-->
<!-- </li>-->
<li class="layui-nav-item" lay-unselect>
<a href="http://easyadmin8.top" target="_blank"><i class="fa fa-home"></i></a>
</li>
@@ -47,6 +38,15 @@
<li class="layui-nav-item mobile layui-hide-xs" lay-unselect>
<a href="javascript:;" data-check-screen="full"><i class="fa fa-arrows-alt"></i></a>
</li>
<li class="layui-nav-item" lay-unselect>
<div class="layui-form ws-header-theme" lay-filter="header-theme">
<input type="checkbox" name="theme-mode" lay-filter="header-theme-mode" lay-skin="switch">
<div lay-checkbox>
<i class="layui-icon layui-icon-moon"></i> |
<i class="layui-icon layui-icon-light"></i>
</div>
</div>
</li>
<li class="layui-nav-item layuimini-setting">
<a href="javascript:;">
<img src="{:session('admin.head_img')}" class="layui-nav-img" width="50" height="50">

View File

@@ -0,0 +1,45 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form" autocomplete="off">
{if $old_secret}
<div class="layui-card">
<div class="layui-card-header">提示</div>
<div class="layui-card-body">
当前账号已经绑定过了 谷歌验证码 ,如果重新保存将替换
</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label required">验证秘钥</label>
<div class="layui-input-block">
<input type="text" name="ga_secret" class="layui-input" value="{$secret}" readonly disabled>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">二维码</label>
<div class="layui-input-block">
<img src="{$dataUri}" alt="二维码" style="width: 200px;height: 200px">
<div class="layui-text layui-font-cyan layui-font-12">
使用&nbsp;
<a href="https://2fas.com" target="_blank"><span class="layui-text layui-font-blue">2FAS</span></a>
&nbsp;或者&nbsp;
<a href="https://cn.bing.com/search?q=Google+Authenticator" target="_blank"><span class="layui-text layui-font-blue">Google Authenticator</span></a>
&nbsp;APP 扫描二维码 输入验证码 进行绑定
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">谷歌验证码</label>
<div class="layui-input-block">
<input type="text" name="ga_code" class="layui-input" maxlength="6" lay-verify="required" placeholder="扫描二维码,输入验证码" value="">
</div>
</div>
<div class=" hr-line">
</div>
<div class="layui-form-item text-center">
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
</div>
</form>
</div>

View File

@@ -69,16 +69,27 @@
<div class="layui-card-body">
<div class="welcome-module">
<div class="layui-row layui-col-space10">
<div class="swiper mySwiper">
<div class="swiper-wrapper">
{foreach $quicks as $value}
{foreach $quicks as $vo}
<div class="layui-col-xs3 layuimini-qiuck-module">
<a layuimini-content-href="{:url($vo['href'])}" data-title="{$vo['title']}">
<i class="{$vo['icon']|raw}"></i>
<cite>{$vo['title']}</cite>
</a>
<div class="swiper-slide">
{foreach $value as $vo}
<div class="layui-col-xs3 layuimini-qiuck-module">
<a layuimini-content-href="{:url($vo['href'])}" data-title="{$vo['title']}">
<i class="{$vo['icon']|raw}"></i>
<cite>{$vo['title']}</cite>
</a>
</div>
{/foreach}
</div>
{/foreach}
</div>
</div>
{/foreach}
<div class="swiper-pagination"></div>
</div>
</div>
</div>
@@ -140,12 +151,18 @@
<tr>
<td>DEBUG模式</td>
<td>
<button type="button" class="layui-btn layui-btn-xs {:env('APP_DEBUG')?'layui-btn-warm':'layui-bg-gray'}">
<button type="button" class="layui-btn layui-btn-xs {:env('APP_DEBUG')?'layui-bg-cyan':'layui-bg-gray'}">
{:env('APP_DEBUG')?'开启中':'已关闭'}
</button>
<span class="layui-badge layui-bg-gray">建议线上环境关闭 APP_DEBUG</span>
</td>
</tr>
<tr>
<td>composer信息</td>
<td>
<button type="button" class="layui-btn layui-btn-xs layui-bg-cyan" lay-on="showComposerInfo">点击查看</button>
</td>
</tr>
<tr>
<td>主要特色</td>
<td>
@@ -169,7 +186,7 @@
<td>Github</td>
<td>
<a href="https://github.com/wolf-leo/easyAdmin8" target="_blank">
<i class="layui-icon layui-icon-github layui-font-20 layui-font-cyan layui-text"></i>
<i class="layui-icon layui-icon-github layui-font-20 layui-font-gray layui-text"></i>
</a>
</td>
</tr>
@@ -181,7 +198,7 @@
<div class="layui-card">
<div class="layui-card-header"><i class="fa fa-paper-plane-o icon"></i>作者心语</div>
<div class="layui-card-body layui-text">
<p class="layui-font-cyan">
<p>
本模板基于layui2.9.x以及font-awesome-4.7.0进行实现。
<a class="layui-btn layui-btn-xs layui-btn-danger" style="vertical-align: baseline;" target="_blank" href="http://layui.dev/docs">layui文档</a>
</p>

View File

@@ -6,6 +6,7 @@
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="icon" href="{:sysConfig('site', 'site_ico')}" type="image/x-icon">
<!--[if lt IE 9]>
<script src="https://cdn.staticfile.org/html5shiv/r29/html5.min.js"></script>
<script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
@@ -26,7 +27,6 @@
EDITOR_TYPE: "{$adminEditor|default='wangEditor'}",
};
</script>
<script src="/static/plugs/xmSelect/xm-select.js" charset="utf-8"></script>
<script src="/static/plugs/layui-v2.x/layui.js" charset="utf-8"></script>
<script src="/static/plugs/require-2.3.6/require.js" charset="utf-8"></script>
<script src="/static/config-admin.js?v={$version}" charset="utf-8"></script>

View File

@@ -22,6 +22,11 @@
<span class="bind-password icon icon-4"></span>
</div>
<div class="item layui-hide" id="gaCode">
<span class="icon icon-3"></span>
<input type="text" name="ga_code" placeholder="谷歌验证码" maxlength="6">
</div>
{if $captcha == 1}
<div id="validatePanel" class="item" style="width: 137px;">
<input type="text" name="captcha" placeholder="请输入验证码" maxlength="4">
@@ -36,7 +41,7 @@
<a href="javascript:" class="forget-password">忘记密码?</a>
</div>
<div class="layui-form-item" style="text-align:center; width:100%;height:100%;margin:0px;">
<button class="login-btn" lay-submit>立即登录</button>
<button type="button" class="login-btn" lay-submit>立即登录</button>
</div>
</form>
</div>

View File

@@ -22,7 +22,7 @@
<div class="layui-form-item">
<label class="layui-form-label">分类排序</label>
<div class="layui-input-block">
<input type="number" name="sort" class="layui-input" placeholder="请输入分类排序" value="0">
<input type="number" name="sort" class="layui-input" lay-affix="number" placeholder="请输入分类排序" value="0">
</div>
</div>

View File

@@ -22,7 +22,7 @@
<div class="layui-form-item">
<label class="layui-form-label">分类排序</label>
<div class="layui-input-block">
<input type="number" name="sort" class="layui-input" placeholder="请输入分类排序" value="{$row.sort|default=''}">
<input type="number" name="sort" class="layui-input" lay-affix="number" placeholder="请输入分类排序" value="{$row.sort|default=''}">
</div>
</div>

View File

@@ -26,9 +26,18 @@
<!-- </div>-->
<div class="layui-form-item">
<label class="layui-form-label">商品标题</label>
<div class="layui-input-block">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="">
<div class="layui-row">
<label class="layui-form-label required">商品标题</label>
<div class="layui-input-block layui-col-space5">
<div class="layui-col-xs10">
<div class="layui-input-wrap">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="">
</div>
</div>
<div class="layui-col-xs2">
<button class="layui-btn layui-bg-purple layui-btn-fluid" type="button" lay-on="AiOptimization">AI优化</button>
</div>
</div>
</div>
</div>
@@ -78,7 +87,7 @@
<div class="layui-form-item">
<label class="layui-form-label">分类排序</label>
<div class="layui-input-block">
<input type="number" name="sort" class="layui-input" placeholder="请输入分类排序" value="0">
<input type="number" name="sort" class="layui-input" lay-affix="number" placeholder="请输入分类排序" value="0">
</div>
</div>

View File

@@ -26,12 +26,22 @@
<!-- </div>-->
<div class="layui-form-item">
<label class="layui-form-label">商品标题</label>
<div class="layui-input-block">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="{$row.title|default=''}">
<div class="layui-row">
<label class="layui-form-label required">商品标题</label>
<div class="layui-input-block layui-col-space5">
<div class="layui-col-xs10">
<div class="layui-input-wrap">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="{$row.title|default=''}">
</div>
</div>
<div class="layui-col-xs2">
<button class="layui-btn layui-bg-purple" type="button" lay-on="AiOptimization">AI优化</button>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">商品LOGO</label>
<div class="layui-input-block layuimini-upload">
@@ -78,7 +88,7 @@
<div class="layui-form-item">
<label class="layui-form-label">分类排序</label>
<div class="layui-input-block">
<input type="number" name="sort" class="layui-input" placeholder="请输入分类排序" value="{$row.sort|default=''}">
<input type="number" name="sort" class="layui-input" lay-affix="number" placeholder="请输入分类排序" value="{$row.sort|default=''}">
</div>
</div>

View File

@@ -5,6 +5,7 @@
data-auth-edit="{:auth('mall.goods/edit')}"
data-auth-delete="{:auth('mall.goods/delete')}"
data-auth-stock="{:auth('mall.goods/stock')}"
data-auth-recycle="{:auth('mall.goods/recycle')}"
lay-filter="currentTable">
</table>
</div>

View File

@@ -0,0 +1,11 @@
<div class="layuimini-container">
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
data-auth-recycle="{:auth('mall.goods/recycle')}"
lay-filter="currentTable">
</table>
</div>
</div>
<script>
let cateSelects = JSON.parse('{$cate|json_encode=256|raw}')
</script>

View File

@@ -25,7 +25,7 @@
<div class="layui-form-item">
<label class="layui-form-label">入库数量</label>
<div class="layui-input-block">
<input type="number" name="stock" class="layui-input" lay-verify="required" placeholder="请输入入库数量" value="0">
<input type="number" name="stock" class="layui-input" lay-affix="number" lay-verify="required" placeholder="请输入入库数量" value="0">
</div>
</div>

View File

@@ -1,4 +1,11 @@
<link rel="stylesheet" href="/static/plugs/zTree/fontawesome.css">
<link rel="stylesheet" href="/static/plugs/zTree/zTreeStyle.css?v={$version}">
<script src='/static/plugs/jquery-3.4.1/jquery-3.4.1.min.js'></script>
<script src='/static/plugs/zTree/jquery.ztree.core.js'></script>
<script src='/static/plugs/zTree/jquery.ztree.excheck.js'></script>
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
@@ -10,8 +17,8 @@
<div class="layui-form-item">
<label class="layui-form-label required">分配节点</label>
<div class="layui-input-block">
<div id="node_ids" class="demo-tree-more"></div>
<div class="layui-input-block" id="zTree">
<ul id="tree" class="ztree"></ul>
</div>
</div>
@@ -24,4 +31,4 @@
</div>
</form>
</div>
</div>

View File

@@ -1,22 +1,16 @@
<div class="layuimini-container">
<div class="layuimini-main" id="app">
<div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
<ul class="layui-tab-title">
<div class="layui-tabs layui-tabs-card layui-panel " id="docDemoTabBrief">
<ul class="layui-tabs-header layui-bg-tint">
<li class="layui-this" data-group="site">网站设置</li>
<li data-group="logo">LOGO配置</li>
<li data-group="upload">上传配置</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
{include file="system/config/site" /}
</div>
<div class="layui-tab-item">
{include file="system/config/logo" /}
</div>
<div class="layui-tab-item">
{include file="system/config/upload" /}
</div>
<div class="layui-tabs-body">
<div class="layui-tabs-item layui-show"> {include file="system/config/site" /}</div>
<div class="layui-tabs-item"> {include file="system/config/logo" /}</div>
<div class="layui-tabs-item">{include file="system/config/upload" /}</div>
</div>
</div>

View File

@@ -0,0 +1,23 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
<div class="layui-input-group">
<div class="layui-input-prefix layui-input-split">删除</div>
<label>
<input type="number" name="month" lay-affix="number" placeholder="" min="1" class="layui-input" value="3">
</label>
<div class="layui-input-suffix layui-input-split">个月前的日志</div>
</div>
</div>
<div class="hr-line"></div>
<div class="layui-form-item text-center">
<button type="button" class="layui-btn" lay-submit lay-filter="submit">提交</button>
</div>
</form>
</div>

View File

@@ -2,6 +2,7 @@
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
data-auth-record="{:auth('system.log/record')}"
data-auth-deleteMonthLog="{:auth('system.log/deleteMonthLog')}"
lay-filter="currentTable">
</table>
</div>

View File

@@ -53,7 +53,7 @@
<div class="layui-form-item">
<label class="layui-form-label">菜单排序</label>
<div class="layui-input-block">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="0" class="layui-input">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="0" class="layui-input" lay-affix="number">
</div>
</div>

View File

@@ -53,7 +53,7 @@
<div class="layui-form-item">
<label class="layui-form-label">菜单排序</label>
<div class="layui-input-block">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="{$row.sort|default=''}" class="layui-input">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="{$row.sort|default=''}" class="layui-input" lay-affix="number">
</div>
</div>

View File

@@ -31,7 +31,7 @@
<div class="layui-form-item">
<label class="layui-form-label">菜单排序</label>
<div class="layui-input-block">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="0" class="layui-input">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="0" class="layui-input" lay-affix="number">
</div>
</div>

View File

@@ -31,7 +31,7 @@
<div class="layui-form-item">
<label class="layui-form-label">菜单排序</label>
<div class="layui-input-block">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="{$row.sort|default=''}" class="layui-input">
<input type="number" name="sort" lay-reqtext="菜单排序不能为空" placeholder="请输入菜单排序" value="{$row.sort|default=''}" class="layui-input" lay-affix="number">
</div>
</div>

View File

@@ -2,6 +2,8 @@
// 应用公共文件
use app\common\service\AuthService;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
@@ -19,6 +21,7 @@ if (!function_exists('__url')) {
*/
function __url(string $url = '', array $vars = [], bool $suffix = true, bool $domain = false): string
{
if (filter_var($url, FILTER_VALIDATE_URL)) return $url;
return url($url, $vars, $suffix, $domain)->build();
}
}
@@ -99,21 +102,63 @@ if (!function_exists('auth')) {
$authService = new AuthService(session('admin.id'));
return $authService->checkNode($node);
}
}
/**
* @param string|null $detail
* @param string $name
* @param string $placeholder
* @return string
*/
function editor_textarea(?string $detail, string $name = 'desc', string $placeholder = '请输入'): string
{
$editor_type = sysConfig('site', 'editor_type');
return match ($editor_type) {
'ckeditor' => "<textarea name='{$name}' rows='20' class='layui-textarea editor' placeholder='{$placeholder}'>{$detail}</textarea>",
'ueditor' => "<script type='text/plain' id='{$name}' name='{$name}' class='editor' data-content='{$detail}'></script>",
'EasyMDE' => "<textarea id='{$name}' class='editor' name='{$name}'>{$detail}</textarea>",
default => "<div class='wangEditor_div'><textarea name='{$name}' rows='20' class='layui-textarea editor layui-hide'>{$detail}</textarea><div id='editor_toolbar_{$name}'></div><div id='editor_{$name}' style='height: 300px'></div></div>",
};
/**
* @param string|null $detail
* @param string $name
* @param string $placeholder
* @return string
*/
function editor_textarea(?string $detail, string $name = 'desc', string $placeholder = '请输入'): string
{
$editor_type = sysConfig('site', 'editor_type');
return match ($editor_type) {
'ckeditor' => "<textarea name='{$name}' rows='20' class='layui-textarea editor' placeholder='{$placeholder}'>{$detail}</textarea>",
'ueditor' => "<script type='text/plain' id='{$name}' name='{$name}' class='editor' data-content='{$detail}'></script>",
'EasyMDE' => "<textarea id='{$name}' class='editor' name='{$name}'>{$detail}</textarea>",
default => "<div class='wangEditor_div'><textarea name='{$name}' rows='20' class='layui-textarea editor layui-hide'>{$detail}</textarea><div id='editor_toolbar_{$name}'></div><div id='editor_{$name}' style='height: 500px'></div></div>",
};
}
/**
* @desc 导出excel
* @tip 追求性能请使用 xlsWriter https://xlswriter-docs.viest.me/zh-cn
* @param array $header
* @param array $list
* @param string $fileName
* @return void
* @throws Exception
*/
function exportExcel(array $header = [], array $list = [], string $fileName = ''): void
{
if (empty($fileName)) $fileName = time();
if (empty($header) || empty($list)) throw new \Exception('导出数据不能为空');
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$headers = array_column($header, 0) ?? array_keys($list[0]);
$sheet->fromArray([$headers], null, 'A1');
$rowIndex = 2;
foreach ($list as $row) {
$rowData = [];
foreach ($header as $item) {
$value = $row[$item[1]] ?? '';
if ($value === null) {
$rowData[] = '';
continue;
}
$rowData[] = $value;
}
$sheet->fromArray([$rowData], null, "A{$rowIndex}");
$rowIndex++;
}
foreach (range('A', $sheet->getHighestColumn()) as $col) {
$sheet->getColumnDimension($col)->setAutoSize(true);
}
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $fileName . '.xlsx"');
header('Cache-Control: max-age=0');
$writer = new Xlsx($spreadsheet);
$writer->save('php://output');
die();
}

View File

@@ -168,6 +168,12 @@ class Curd extends Command
}
}
}
if (PHP_SAPI == 'cli') {
$output->info(">>>>>>>>>>>>>>>");
$output->info('执行成功');
}else {
$output->writeln('执行成功');
}
}catch (\Exception $e) {
if (PHP_SAPI == 'cli')
CliEcho::error($e->getMessage());

View File

@@ -1,47 +0,0 @@
<?php
namespace app\common\command;
use EasyAdmin\console\CliEcho;
use EasyAdmin\tool\CommonTool;
use EasyAdmin\upload\driver\alioss\Oss;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
class OssStatic extends Command
{
protected function configure()
{
$this->setName('OssStatic')
->setDescription('将静态资源上传到oss上');
}
protected function execute(Input $input, Output $output)
{
$output->writeln("========正在上传静态资源到OSS上========" . date('Y-m-d H:i:s'));
$dir = root_path() . 'public' . DIRECTORY_SEPARATOR . 'static';
$list = CommonTool::readDirAllFiles($dir);
$uploadConfig = sysConfig('upload');
$uploadPrefix = config('app.oss_static_prefix', 'oss_static_prefix');
foreach ($list as $key => $val) {
list($objectName, $filePath) = [$uploadPrefix . DIRECTORY_SEPARATOR . $key, $val];
try {
$upload = Oss::instance($uploadConfig)
->save($objectName, $filePath);
} catch (\Exception $e) {
CliEcho::error('文件上传失败:' . $filePath . '。错误信息:' . $e->getMessage());
continue;
}
if ($upload['save'] == true) {
CliEcho::success('文件上传成功:' . $filePath . '。上传地址:' . $upload['url']);
} else {
CliEcho::error('文件上传失败:' . $filePath . '。错误信息:' . $upload['msg']);
}
}
$output->writeln("========已完成静态资源上传到OSS上========" . date('Y-m-d H:i:s'));
}
}

View File

@@ -19,9 +19,9 @@ class AdminController extends BaseController
/**
* 当前模型
* @Model
* @var object
* @var mixed
*/
protected object $model;
protected static mixed $model;
/**
* 字段排序
@@ -44,6 +44,12 @@ class AdminController extends BaseController
'title',
];
/**
* 过滤节点更新
* @var array
*/
protected array $ignoreNode = [];
/**
* 不导出的字段信息
* @var array
@@ -166,7 +172,7 @@ class AdminController extends BaseController
$where = [];
$excludes = [];
// 判断是否关联查询
$tableName = Str::snake(lcfirst($this->model->getName()));
$tableName = Str::snake(lcfirst((new self::$model)->getName()));
foreach ($filters as $key => $val) {
if (in_array($key, $excludeFields)) {
$excludes[$key] = $val;
@@ -212,7 +218,7 @@ class AdminController extends BaseController
public function selectList(): Json
{
$fields = input('selectFields');
$data = $this->model->where($this->selectWhere)->field($fields)->select()->toArray();
$data = self::$model::where($this->selectWhere)->field($fields)->select()->toArray();
$this->success(null, $data);
}
@@ -243,7 +249,7 @@ class AdminController extends BaseController
'version' => env('APP_DEBUG') ? time() : ConfigService::getVersion(),
'adminUploadUrl' => url('ajax/upload', [], false),
'adminEditor' => sysConfig('site', 'editor_type') ?: 'wangEditor',
'iframeOpenTop' => sysConfig('site', 'iframe_open_top') ?: 0,
'iframeOpenTop' => sysConfig('site', 'iframe_open_top') ?: 0,
];
View::assign($data);
}

View File

@@ -0,0 +1,22 @@
<?php
namespace app\common\entity;
use think\Entity;
use think\model\type\DateTime;
class BaseEntity extends Entity
{
protected function getOptions(): array
{
return [
'type' => [
'create_time' => DateTime::class,
'update_time' => DateTime::class,
'delete_time' => DateTime::class,
],
];
}
}

View File

@@ -13,28 +13,20 @@ use think\model\concern\SoftDelete;
class TimeModel extends Model
{
/**
* 自动时间戳类型
* @var string
*/
protected $autoWriteTimestamp = true;
/**
* 添加时间
* @var string
*/
protected $createTime = 'create_time';
/**
* 更新时间
* @var string
*/
protected $updateTime = 'update_time';
/**
* 软删除
*/
use SoftDelete;
protected $deleteTime = false;
protected function getOptions(): array
{
return [
'autoWriteTimestamp' => true,
'createTime' => 'create_time',
'updateTime' => 'update_time',
'deleteTime' => false,
];
}
}

View File

@@ -2,8 +2,8 @@
namespace app\common\service;
use app\admin\service\annotation\NodeAnnotation;
use app\common\constants\AdminConstant;
use EasyAdmin\tool\CommonTool;
use think\facade\Db;
/**
@@ -53,7 +53,7 @@ class AuthService
/***
* 构造方法
* AuthService constructor.
* @param null $adminId
* @param null $adminId
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
@@ -69,7 +69,7 @@ class AuthService
/**
* 检测检测权限
* @param null $node
* @param null $node
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
@@ -88,7 +88,7 @@ class AuthService
// 判断是否需要获取当前节点
if (empty($node)) {
$node = $this->getCurrentNode();
} else {
}else {
$node = $this->parseNodeStr($node);
}
// 判断是否加入节点控制,优先获取缓存信息
@@ -107,9 +107,30 @@ class AuthService
if (in_array($node, $this->adminNode)) {
return true;
}
if ($this->checkNodeAnnotationAttrAuth($node)) return true;
return false;
}
protected function checkNodeAnnotationAttrAuth(string $node): bool
{
$bool = false;
$controller = request()->controller();
try {
$controllerExplode = explode('.', $controller);
[$_name, $_controller] = $controllerExplode;
$nodeExplode = explode('/', $node);
$action = end($nodeExplode);
$reflectionClass = new \ReflectionClass("app\admin\controller\\{$_name}\\{$_controller}");
$attributes = $reflectionClass->getMethod($action)->getAttributes(NodeAnnotation::class);
foreach ($attributes as $attribute) {
$annotation = $attribute->newInstance();
$bool = $annotation->auth === false;
}
}catch (\Throwable) {
}
return $bool;
}
/**
* 获取当前节点
* @return string
@@ -131,25 +152,25 @@ class AuthService
{
$nodeList = [];
$adminInfo = Db::name($this->config['system_admin'])
->where([
'id' => $this->adminId,
'status' => 1,
])->find();
->where([
'id' => $this->adminId,
'status' => 1,
])->find();
if (!empty($adminInfo) && !empty($adminInfo['auth_ids'])) {
$buildAuthSql = Db::name($this->config['system_auth'])
->distinct(true)
->whereIn('id', $adminInfo['auth_ids'])
->field('id')
->buildSql(true);
->distinct(true)
->whereIn('id', $adminInfo['auth_ids'])
->field('id')
->buildSql(true);
$buildAuthNodeSql = Db::name($this->config['system_auth_node'])
->distinct(true)
->where("auth_id IN {$buildAuthSql}")
->field('node_id')
->buildSql(true);
->distinct(true)
->where("auth_id IN {$buildAuthSql}")
->field('node_id')
->buildSql(true);
$nodeList = Db::name($this->config['system_node'])
->distinct(true)
->where("id IN {$buildAuthNodeSql}")
->column('node');
->distinct(true)
->where("id IN {$buildAuthNodeSql}")
->column('node');
}
return $nodeList;
}
@@ -163,7 +184,7 @@ class AuthService
public function getNodeList()
{
return Db::name($this->config['system_node'])
->column('id,node,title,type,is_auth', 'node');
->column('id,node,title,type,is_auth', 'node');
}
/**
@@ -178,13 +199,13 @@ class AuthService
public function getAdminInfo()
{
return Db::name($this->config['system_admin'])
->where('id', $this->adminId)
->find();
->where('id', $this->adminId)
->find();
}
/**
* 驼峰转下划线规则
* @param string $node
* @param string $node
* @return string
*/
public function parseNodeStr($node)

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types = 1);
namespace app\common\utils;
class Helper
{
/**
* 获取当前IP地址
* @return string
*/
public static function getIp(): string
{
return request()->ip();
}
/**
* 获取当前登录用户ID
* @return int|string
*/
public static function getAdminUid(): int|string
{
return session('admin.id') ?: 0;
}
}

View File

@@ -22,8 +22,8 @@ class Install extends BaseController
// $this->redirect('/');
$isInstall = true;
$errorInfo = '已安装系统,如需重新安装请删除文件:/config/install/lock/install.lock或者删除 /install 路由';
}elseif (version_compare(phpversion(), '8.0.0', '<')) {
$errorInfo = 'PHP版本不能小于8.0.0';
}elseif (version_compare(phpversion(), '8.1.0', '<')) {
$errorInfo = 'PHP版本不能小于8.1.0';
}elseif (!extension_loaded("PDO")) {
$errorInfo = '当前未开启PDO无法进行安装';
}
@@ -31,9 +31,17 @@ class Install extends BaseController
$errorInfo = '.env 文件不存在,请先配置 .env 文件';
}
if (!$request->isAjax()) {
$envInfo = [
'DB_HOST' => $isInstall ? '' : env('DB_HOST', '127.0.0.1'),
'DB_NAME' => $isInstall ? '' : env('DB_NAME', 'easyadmin8'),
'DB_USER' => $isInstall ? '' : env('DB_USER', 'root'),
'DB_PASS' => $isInstall ? '' : env('DB_PASS', 'root'),
'DB_PORT' => $isInstall ? '' : env('DB_PORT', 3306),
'DB_PREFIX' => $isInstall ? '' : env('DB_PREFIX', 'ea8_'),
];
$currentHost = '://';
$result = compact('errorInfo', 'currentHost', 'isInstall');
return view('index/install/index', $result);
$result = compact('errorInfo', 'currentHost', 'isInstall', 'envInfo');
return view('index@install/index', $result);
}
if ($errorInfo) $this->error($errorInfo);
$charset = 'utf8mb4';
@@ -91,12 +99,11 @@ class Install extends BaseController
$installPath = config_path() . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
$sqlPath = file_get_contents($installPath . 'sql' . DIRECTORY_SEPARATOR . 'install.sql');
$sqlArray = $this->parseSql($sqlPath, $config['prefix'], 'ea_');
$conn = mysqli_connect($config['host'], $config['username'], $config['password'], null, $config['port']);
$dsn = $this->pdoDsn($config, true);
try {
mysqli_set_charset($conn, $config['charset']);
mysqli_select_db($conn, $config['database']);
$pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
foreach ($sqlArray as $sql) {
mysqli_query($conn, $sql);
$pdo->query($sql);
}
$_password = password($password);
$tableName = 'system_admin';
@@ -108,9 +115,8 @@ class Install extends BaseController
'update_time' => time()
];
foreach ($update as $_k => $_up) {
mysqli_query($conn, "UPDATE {$config['prefix']}{$tableName} SET {$_k} = '{$_up}' WHERE id = 1");
$pdo->query("UPDATE {$config['prefix']}{$tableName} SET {$_k} = '{$_up}' WHERE id = 1");
}
mysqli_close($conn);
// 处理安装文件
!is_dir($installPath) && @mkdir($installPath);
!is_dir($installPath . 'lock' . DIRECTORY_SEPARATOR) && @mkdir($installPath . 'lock' . DIRECTORY_SEPARATOR);
@@ -161,11 +167,11 @@ class Install extends BaseController
protected function createDatabase($database, $config): bool
{
$dsn = $this->pdoDsn($config);
try {
$con = mysqli_connect($config['host'] ?? '127.0.0.1', $config['username'] ?? 'root', $config['password'] ?? '', null, $config['port'] ?? '');
mysqli_query($con, "CREATE DATABASE IF NOT EXISTS `{$database}` DEFAULT CHARACTER SET {$config['charset']} COLLATE=utf8mb4_general_ci");
mysqli_close($con);
}catch (\Throwable $e) {
$pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
$pdo->query("CREATE DATABASE IF NOT EXISTS `{$database}` DEFAULT CHARACTER SET {$config['charset']} COLLATE=utf8mb4_general_ci");
}catch (\PDOException $e) {
return false;
}
return true;
@@ -187,19 +193,32 @@ class Install extends BaseController
protected function checkConnect(array $config): ?bool
{
$dsn = $this->pdoDsn($config);
try {
$con = mysqli_connect($config['host'] ?? '127.0.0.1', $config['username'] ?? 'root', $config['password'] ?? '', null, $config['port'] ?? '');
$res = mysqli_query($con, 'select VERSION()');
$mysqlVersion = mysqli_fetch_row($res);
mysqli_close($con);
$_version = $mysqlVersion[0] ?? 0;
$pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
$res = $pdo->query('select VERSION()');
$_version = $res->fetch()[0] ?? 0;
if (version_compare($_version, '5.7.0', '<')) {
$this->error('mysql版本最低要求 5.7.x');
}
}catch (\mysqli_sql_exception $e) {
}catch (\PDOException $e) {
$this->error($e->getMessage());
}
return true;
}
/**
* @param array $config
* @param bool $needDatabase
* @return string
*/
protected function pdoDsn(array $config, bool $needDatabase = false): string
{
$host = $config['host'] ?? '127.0.0.1';
$database = $config['database'] ?? '';
$port = $config['port'] ?? '3306';
$charset = $config['charset'] ?? 'utf8mb4';
if ($needDatabase) return "mysql:host=$host;port=$port;dbname=$database;charset=$charset";
return "mysql:host=$host;port=$port;charset=$charset";
}
}

View File

@@ -20,24 +20,26 @@
}
],
"require": {
"php": ">=8.0.0",
"php": ">=8.1.0",
"topthink/framework": "^8.0",
"topthink/think-orm": "^3.0",
"topthink/think-multi-app": "^1.0",
"topthink/think-orm": "^4.0",
"topthink/think-multi-app": "^1.1.0",
"topthink/think-view": "^2.0",
"topthink/think-captcha": "^3.0",
"topthink/think-filesystem": "^2.0",
"aliyuncs/oss-sdk-php": "^2.6",
"aliyuncs/oss-sdk-php": "^2.7.2",
"qcloud/cos-sdk-v5": "^2.6",
"jianyan74/php-excel": "^1.0.2",
"doctrine/annotations": "^1.13",
"phpoffice/phpspreadsheet": "^1.28",
"doctrine/annotations": "^2.0.0",
"phpoffice/phpspreadsheet": "^4.1.0",
"myclabs/php-enum": "^1.8",
"qiniu/php-sdk": "^7.11.0",
"wolf-leo/phplogviewer": "^0.11.3",
"wolfcode/authenticator": "^0.0.6",
"wolfcode/rate-limiting": "^0.1.0",
"wolfcode/php-ai": "^0.1.2",
"ext-json": "*",
"qiniu/php-sdk": "v7.11.0",
"ext-mysqli": "*",
"ext-pdo": "*",
"wolf-leo/phplogviewer": "^0.11.1"
"ext-pdo": "*"
},
"require-dev": {
"symfony/var-dumper": ">=4.2",

View File

@@ -97,6 +97,8 @@ CREATE TABLE `ea_system_admin`
`create_time` int(11) DEFAULT NULL COMMENT '创建时间',
`update_time` int(11) DEFAULT NULL COMMENT '更新时间',
`delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
`login_type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '登录方式',
`ga_secret` varchar(32) NOT NULL DEFAULT '' COMMENT '谷歌验证码秘钥',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE,
KEY `phone` (`phone`)
@@ -106,7 +108,7 @@ CREATE TABLE `ea_system_admin`
-- Records of ea_system_admin
-- ----------------------------
INSERT INTO `ea_system_admin`
VALUES ('1', null, '/static/admin/images/head.jpg', 'admin', 'a33b679d5581a8692988ec9f92ad2d6a2259eaa7', 'admin', 'admin', '0', '0', '1', '1589454169', '1589476815', null);
VALUES ('1', null, '/static/admin/images/head.jpg', 'admin', 'a33b679d5581a8692988ec9f92ad2d6a2259eaa7', 'admin', 'admin', '0', '0', '1', '1589454169', '1589476815', null,1,'');
-- ----------------------------
-- Table structure for ea_system_auth
@@ -233,11 +235,11 @@ VALUES ('64', 'oss_domain', 'upload', '填你的', '阿里云oss访问域名', '
INSERT INTO `ea_system_config`
VALUES ('65', 'logo_title', 'site', 'EasyAdmin', 'LOGO标题', '0', null, null);
INSERT INTO `ea_system_config`
VALUES ('66', 'logo_image', 'site', '/favicon.ico', 'logo图片', '0', null, null);
VALUES ('66', 'logo_image', 'site', '/static/common/images/logo-1.png', 'logo图片', '0', null, null);
INSERT INTO `ea_system_config`
VALUES ('68', 'site_name', 'site', 'EasyAdmin后台系统', '站点名称', '0', null, null);
INSERT INTO `ea_system_config`
VALUES ('69', 'site_ico', 'site', '填你的', '浏览器图标', '0', null, null);
VALUES ('69', 'site_ico', 'site', '/favicon.ico', '浏览器图标', '0', null, null);
INSERT INTO `ea_system_config`
VALUES ('70', 'site_copyright', 'site', '填你的', '版权信息', '0', null, null);
INSERT INTO `ea_system_config`
@@ -569,3 +571,22 @@ INSERT INTO `ea_system_uploadfile`
VALUES ('290', 'oss', 'image/jpeg', 'https://lxn-99php.oss-cn-shenzhen.aliyuncs.com/upload/20191111/2c412adf1b30c8be3a913e603c7b6e4a.jpg', '', '', '', '0', 'image/jpeg', '0', 'jpg', '', 1573612437, null, null);
INSERT INTO `ea_system_uploadfile`
VALUES ('296', 'cos', 'image/jpeg', 'https://easyadmin-1251997243.cos.ap-guangzhou.myqcloud.com/upload/20191114/2381eaf81208ac188fa994b6f2579953.jpg', '', '', '', '0', 'image/jpeg', '0', 'jpg', '', 1573612437, null, null);
-- ----------------------------
-- Table structure for ea_system_log
-- ----------------------------
DROP TABLE IF EXISTS `ea_system_log`;
CREATE TABLE `ea_system_log`
(
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`admin_id` int unsigned DEFAULT '0' COMMENT '管理员ID',
`url` varchar(1500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '操作页面',
`method` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求方法',
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '日志标题',
`content` json NOT NULL COMMENT '请求数据',
`response` json DEFAULT NULL COMMENT '回调数据',
`ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP',
`useragent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'User-Agent',
`create_time` int DEFAULT NULL COMMENT '操作时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='后台操作日志表 - 202412';

View File

@@ -42,4 +42,6 @@ return [
'default_jsonp_handler' => 'jsonpReturn',
// 默认JSONP处理方法
'var_jsonp_handler' => 'callback',
// 操作方法的参数绑定方式 route get param
'action_bind_param' => 'param',
];

3
log.md
View File

@@ -1,2 +1,5 @@
> 2025年03月27日 重构了 `model` 的调用方式 原因查看 [https://github.com/top-think/think-orm/issues/704](https://github.com/top-think/think-orm/issues/704)
>
> 2025年01月01日 `PHP` 要求升级到 `8.1+`
>
> 2024年05月 更新 `EasyAdmin8` 重置版,多处语法、写法进行变更

View File

@@ -0,0 +1,59 @@
.green {
color: #11c26d !important;
}
.red {
color: #ff4c52 !important;
}
.cyan {
color: #0bb2d4 !important;
}
.purple {
color: #9463f7 !important;
}
.blue {
color: #3e8ef7 !important;
}
.orange {
color: #eb6709 !important;
}
.indigo {
color: #6610f2 !important;
}
.pink {
color: #e83e8c !important;
}
.teal {
color: #20c997 !important;
}
.white {
color: #fff !important;
}
.gray {
color: #6c757d !important;
}
.gray-dark {
color: #343a40 !important;
}
.secondary {
color: #6c757d !important;
}
.yellow {
color: rgb(255, 214, 102) !important;
}
.magenta {
color: #eb2f96 !important;
}

View File

@@ -1,3 +1,6 @@
body {
background: #333333;
}
.demo {
padding-top: 20px;
@@ -170,7 +173,6 @@
}
.container {
background: #333333;
position: static;
font-size: 12px;
height: 100%;

View File

@@ -1,21 +1,23 @@
@import url("../../plugs/layui-v2.x/css/layui.css");
@import url("../../plugs/font-awesome-4.7.0/css/font-awesome.min.css");
@import url("../css/color.css");
@import url("../css/themes/index.css");
@import url("../css/iconfont.css");
html, body {
height: 100%;
background: #f2f2f2;
}
html.dark, body {
height: 100%;
background: var(--lay-color-bg-1);
html,
body {
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
.ws-header-theme .layui-form-switch {
vertical-align: baseline;
}
.ws-header-theme .layui-form-onswitch {
background: #333333;
border: 1px solid rgba(255, 255, 255, .7);
}
.layuimini-container {
min-height: 250px;
padding: 15px;
@@ -58,10 +60,8 @@ html.dark, body {
}
.hr-line {
color: #fff;
height: 1px;
margin: 30px 0;
background-color: #fff;
border-top: 1px dashed #e7eaec;
}
@@ -75,8 +75,8 @@ html.dark, body {
/**重写layui表格自适应*/
.layuimini-container .layui-table-cell {
height: 100%;
max-width: 100%;
height: 50px;
line-height: 42px;
}
/**数据表格-搜索表单样式*/
@@ -84,7 +84,6 @@ html.dark, body {
margin: 0;
border: 1px solid #e6e6e6;
padding: 10px 20px 5px 20px;
color: #6b6b6b;
}
/**数据表格-搜索表单样式*/
@@ -134,10 +133,17 @@ html.dark, body {
color: #a29c9c;
}
.layui-form-item xm-select {
.tableSearch-xmSelect xm-select {
min-height: 30px !important;
line-height: 30px !important;
margin: 0 !important;
background-color: var(--lay-color-fill-2);
}
.elem-style-dark .tableSearch-xmSelect xm-select {
background-color: var(--lay-color-fill-2);
color: var(--lay-color-text-2);
border-color: var(--lay-color-border-1) !important;
}
/** 按钮背景色 */
@@ -265,7 +271,6 @@ table样式
.layuimini-container .layui-table-box .layui-table-header th {
font-weight: bold !important;
color: #565656 !important;
}
/**
@@ -340,7 +345,7 @@ table样式
弹出层样式
*/
.layui-layer-easy .layui-layer-title {
background: #2c3e50 !important;
background: var(--lay-color-bg-5);
color: #fff !important;
border-bottom: none;
}
@@ -363,7 +368,6 @@ table样式
.layui-layer-easy .layui-layer-btn {
text-align: center !important;
padding: 10px !important;
background: #ecf0f1;
overflow: hidden;
}
@@ -512,4 +516,5 @@ table样式
.wangEditor_div {
z-index: 99999;
border: 1px solid var(--w-e-textarea-slight-border-color);
}

View File

@@ -0,0 +1,802 @@
// 通常用于背景色
$black-color :rgb(2, 17, 20);
// 通常用于字体颜色
$main-color :rgb(126, 252, 246);
// 通常用于边框
$less-main-color:rgb(6, 216, 215);
$plus-main-color:rgb(0, 125, 124);
// 通常用于激活状态,通常跟背景色搭配
$cover-color :rgba(62, 251, 251, 0.05);
// 更强的激活状态,适合做选项操作时使用
$active-color :rgba(62, 251, 251, 0.5);
$red-color :rgb(255, 0, 0);
$yellow-color:rgb(255, 255, 153);
$green-color :rgb(0, 255, 0);
& {
background-color: $black-color;
color: $main-color;
}
.layuimini-main {
background-color: $black-color;
}
.layuimini-container {
background-color: $black-color;
}
.layuimini-container .layui-table-tool {
background-color: $black-color;
}
$box-shape: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
.layui-btn {
&.layui-btn-lg {
padding-right: 25px;
}
clip-path : $box-shape;
padding-right : 10px;
position : relative;
background-color: transparent;
border : 1px solid;
color : rgb(126, 252, 246);
text-shadow : rgb(126, 252, 246) 0px 0px 1px;
background-color: rgb(126, 252, 246);
&::before {
content: '';
display: block;
position: absolute;
z-index: -1;
clip-path: $box-shape;
width: 100%;
height: 100%;
left: -0.5px;
top: -0.5px;
background-color: $black-color;
}
&:hover::after {
content: '';
display: block;
position: absolute;
z-index: -1;
clip-path: $box-shape;
width: 100%;
height: 100%;
left: -0.5px;
top: -0.5px;
background-color: rgba(126, 252, 246, 0.2);
}
&.layui-btn-disabled {
&::after {
// todo:实现按钮禁用样式
}
}
&.layui-btn-success {
text-shadow: rgb(0, 255, 0) 0px 0px 1px;
color: rgb(0, 255, 0);
background-color: rgba(0, 255, 0);
border: unset;
&:hover::after {
background-color: rgba(0, 255, 0, 0.2);
}
}
&.layui-btn-danger {
text-shadow: rgb(255, 0, 0) 0px 0px 1px;
color: rgb(255, 0, 0);
background-color: rgb(255, 0, 0);
border: unset;
&:hover::after {
background-color: rgba(255, 0, 0, 0.2);
}
}
&.layuimini-btn-primary {
text-shadow: rgb(255, 255, 153) 0px 0px 1px;
color: rgb(255, 255, 153);
background-color: rgb(255, 255, 153);
border: unset;
&:hover::after {
background-color: rgba(255, 255, 153, 0.2);
}
}
&.layui-btn-primary {
border: unset;
}
&.layui-btn-normal {
color: $black-color;
background-color: $main-color;
border: unset;
&::before {
background-color: $main-color;
}
&:hover::after {
background-color: $main-color;
}
}
}
.layui-table {
background: $black-color;
color: $main-color;
tr {
background-color: $cover-color;
}
td {
border-color: $less-main-color;
}
}
.layui-table-header {
background-color: $black-color;
}
.layuimini-container .layui-table-box {
border-color: $less-main-color;
}
.layuimini-container .layui-table-box .layui-table-header th {
color: $main-color !important;
}
.layui-table-tool .layui-inline[lay-event] {
color: $main-color;
border: none;
position: relative;
&::after {
border-color: $main-color;
border-width: 1px;
border-style: solid;
clip-path: $box-shape;
position: absolute;
left: 0;
top: 0;
width: calc(100% - 1px);
height: calc(100% - 1px);
content: '';
display: block;
}
}
.layui-table tbody tr:hover {
background-color: rgba(126, 252, 246, 0.1);
}
.layuimini-main {
border-color: $less-main-color;
}
.layui-table-header {
border-color: $less-main-color;
}
.color-content>ul>li>a>div>span:nth-child(2) {
background-color: $black-color !important;
}
.layuimini-color .elem-content li {
clip-path: $box-shape;
}
.layuimini-color .elem-content li.layui-this {
background-color: $main-color;
color: $black-color !important;
border-color: $less-main-color;
}
.layuimini-color .more-menu-item {
color: $main-color;
&:hover {
background-color: rgba(62, 251, 251, 0.05);
}
}
.layui-layout-admin .layui-header {
background-color: $cover-color !important;
}
.layuimini-tab .layui-tab-title {
background-color: $cover-color !important;
border-color: $less-main-color;
}
.layuimini-tab .layui-tab-title span {
color: $active-color;
}
.layui-header .layuimini-menu-header-pc.layui-nav .layui-nav-item a:hover,
.layui-header .layuimini-header-menu.layuimini-pc-show.layui-nav .layui-this a {
color: $black-color !important;
}
.layui-layout-admin .layui-header .layui-nav .layui-nav-item a {
color: $main-color !important;
}
.layui-layout-admin .layui-header .layui-nav .layui-nav-item {
background-color: $active-color;
}
.layui-layout-admin .layui-header .layuimini-header-content>ul>.layui-nav-item.layui-this,
.layuimini-tool i:hover {
background-color: $main-color !important;
}
.layuimini-tab .layui-tab-control>li {
background-color: $black-color;
border-color: $less-main-color;
}
.layuimini-tab .layui-tab-title li {
border-color: $less-main-color;
}
.layui-flow-more a *,
.layui-laypage input,
.layui-table-view select[lay-ignore] {
border-color: $less-main-color;
background-color: $black-color;
color: $main-color;
}
.layui-laypage button,
.layui-laypage input {
border-color: $less-main-color;
background-color: $black-color;
color: $main-color;
}
.layuimini-container .layui-form-switch {
border-color: $less-main-color !important;
background-color: $black-color !important;
}
.layuimini-container .layui-form-onswitch {
background-color: $main-color !important;
}
.layuimini-container .layui-form-switch.layui-form-onswitch i {
background-color: $black-color !important;
}
.layuimini-container .layui-laypage .layui-laypage-curr .layui-laypage-em {
background-color: $main-color !important;
}
.layui-laypage .layui-laypage-curr em {
color: $black-color !important;
}
.layuimini-tab .layui-tab-title .layui-this .layuimini-tab-active {
background-color: $less-main-color;
}
.layui-table-view .layui-form-checkbox.layui-form-checked[lay-skin="primary"] i {
border-color: $main-color;
background-color: $main-color;
color: $black-color;
}
.layui-table-view .layui-form-checkbox[lay-skin="primary"] i {
background-color: $cover-color;
}
.layui-table-init {
background-color: $black-color;
}
.box-border-line {
position: relative;
border: 1px solid $less-main-color;
border-width: 1px;
$border-offset: 3px;
&::before {
content: '';
position: absolute;
width: 50px;
height: 20px;
transition: all 0.2s;
border: 1px solid $less-main-color;
top: -$border-offset;
left: -$border-offset;
border-right: none;
border-bottom: none;
pointer-events: none;
}
&::after {
content: "";
position: absolute;
width: 50px;
height: 20px;
transition: all 0.2s;
border: 1px solid $less-main-color;
bottom: -$border-offset;
right: -$border-offset;
border-left: none;
border-top: none;
pointer-events: none;
}
&:hover {
&::after,
&::before {
width: calc(100% + $border-offset);
height: calc(100% + $border-offset);
}
}
}
.table-search-fieldset {
.layui-input-inline {
margin-right: 0;
}
.layui-form-item:not(:last-child) {
margin-right: 10px;
@extend .box-border-line;
}
.layui-form-label {
border: unset;
}
}
.layui-form-pane .layui-form-label {
background-color: $cover-color;
color: $main-color;
}
.layuimini-container .table-search-fieldset {
color: $main-color;
border-color: $less-main-color;
}
.layui-form-select,
.layui-form-autocomplete {
dl {
color: $main-color;
background-color: $black-color;
border-color: $less-main-color;
dd {
&.layui-this {
background-color: $main-color !important;
color: $black-color;
}
&:hover {
background-color: $less-main-color !important;
color: $black-color;
}
}
}
}
.layui-iconpicker-item,
.layui-iconpicker-item:hover {
border-color: $less-main-color !important;
color: $black-color;
.layui-iconpicker-icon:hover {
border-color: $less-main-color !important;
}
}
.layui-nav-child {
background-color: $black-color;
color: $main-color;
}
.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child a {
color: $main-color !important;
}
.layui-badge,
.layui-badge-dot,
.layui-badge-rim {
background-color: $main-color;
}
.layui-nav .layui-nav-child a:hover {
background-color: $active-color;
}
.layui-table-tool-panel {
background-color: $black-color;
}
.layui-form-checkbox[lay-skin="primary"] span {
color: $main-color;
}
.layui-table-tool-panel li:hover {
background-color: $active-color;
}
.layui-side.layui-bg-black,
.layui-side.layui-bg-black>.layuimini-menu-left>ul,
.layuimini-menu-left-zoom>ul {
background-color: $black-color !important;
}
.layui-side {
border-right: 1px solid $main-color;
}
.layui-bg-blue {
background-color: #0000ff !important;
}
.layui-bg-orange {
background-color: $yellow-color !important;
color: $black-color !important;
}
.layui-bg-green {
background-color: $green-color !important;
color: $black-color !important;
}
.layui-table-hover {
background-color: $cover-color;
}
.layui-table-grid-down {
background-color: $black-color;
color: $main-color;
border-color: unset;
}
.layui-table-tips-main {
background-color: $main-color;
color: $black-color;
}
.layui-layout-admin .layui-header .layuimini-tool i {
color: $main-color;
}
.color-red {
color: $red-color !important;
}
.layuimini-tab .layui-tab-title .layui-this span {
color: $main-color;
}
.layui-card {
background-color: $cover-color;
border-radius: 0;
@extend .box-border-line;
.layui-card-header {
color: $main-color;
}
}
.layui-text {
color: $main-color;
}
.layuimini-qiuck-module {
cursor: pointer;
a i {
background-color: $cover-color;
color: $active-color;
}
a cite {
color: $active-color;
}
&:hover {
a i {
background-color: $active-color;
color: $main-color;
}
a cite {
color: $main-color;
}
}
}
.layui-bg-number {
background-color: $cover-color;
}
.layui-input,
.layui-select,
.layui-textarea,
.city-picker-span,
.main-input {
background-color: $cover-color;
color: $main-color;
}
.city-picker-span>.title>span {
color: $main-color;
&:hover {
background-color: $main-color;
color: $black-color;
}
}
.layui-form-radio:hover *,
.layui-form-radioed,
.layui-form-radioed>i {
color: $main-color;
}
.layui-form-checked[lay-skin="primary"] i {
border-color: $main-color !important;
background-color: $main-color;
color: $black-color;
}
.layui-input:focus,
.layui-textarea:focus {
border-color: $main-color !important;
}
.layui-input:hover,
.layui-textarea:hover {
border-color: $main-color !important;
}
.layui-badge-rim,
.layui-border,
.layui-colla-content,
.layui-colla-item,
.layui-collapse,
.layui-elem-field,
.layui-form-pane .layui-form-item[pane],
.layui-form-pane .layui-form-label,
.layui-input,
.layui-layedit,
.layui-layedit-tool,
.layui-panel,
.layui-quote-nm,
.layui-select,
.layui-tab-bar,
.layui-tab-card,
.layui-tab-title,
.layui-tab-title .layui-this::after,
.layui-textarea {
border-color: $main-color !important;
}
.form-search .layui-input-inline input,
.form-search .layui-input-inline select {
border-width: 0 0 0 1px;
}
.layuimini-tab .layui-tab-tool .layui-nav-child {
border-color: $main-color;
}
.layui-nav .layui-nav-child a {
color: $main-color;
}
.layui-form-item {
margin-bottom: 12px;
}
.layuimini-upload .layuimini-upload-btn {
background-color: $black-color;
}
.layui-layer {
border: 1px solid $main-color;
background-color: $black-color;
}
.layui-layer-easy .layui-layer-title {
background-color: $plus-main-color !important;
color: $main-color !important;
}
.layui-table td,
.layui-table th,
.layui-table-col-set,
.layui-table-fixed-r,
.layui-table-grid-down,
.layui-table-header,
.layui-table-page,
.layui-table-tips-main,
.layui-table-tool,
.layui-table-total,
.layui-table-view,
.layui-table[lay-skin="line"],
.layui-table[lay-skin="row"] {
border-color: $less-main-color;
}
.layui-table tbody tr:hover,
.layui-table thead tr,
.layui-table-click,
.layui-table-header,
.layui-table-hover,
.layui-table-mend,
.layui-table-patch,
.layui-table-tool,
.layui-table-total,
.layui-table-total tr {
background-color: $black-color;
}
.hr-line {
color: $main-color;
border-color: $less-main-color;
background-color: $main-color;
}
.layui-layer-easy .layui-layer-btn {
background-color: $black-color;
border-top: 1px solid $less-main-color;
}
.layui-layer-easy .layui-layer-btn .layui-layer-btn0 {
background-color: $main-color;
border-color: $less-main-color;
color: $main-color !important;
clip-path: $box-shape;
}
.layui-layer-easy .layui-layer-btn a {
@extend .layui-btn
}
.layui-layer-shade {
background-color: #fff !important;
}
.layuimini-menu-left {
.layui-nav-tree .layui-nav-item {
margin: 5px;
width: calc(100% - 10px);
}
.layui-nav .layui-nav-item {
clip-path: $box-shape;
color: $main-color;
background-color: $active-color;
&.layui-this a {
background-color: $main-color !important;
color: $black-color !important;
}
&.layui-this {
background-color: $main-color !important;
color: $black-color !important;
span {
background-color: $main-color !important;
color: $black-color !important;
}
}
}
.layui-nav .layui-nav-item a:hover {
background-color: $active-color !important;
}
}
.layuimini-menu-left .layui-nav .layui-nav-item a,
.layuimini-menu-left-zoom.layui-nav .layui-nav-item a {
color: $main-color !important;
}
.layuimini-menu-left .layui-nav-item a span {
color: $main-color !important;
}
.layuimini-menu-left .layui-nav-item:hover a span {
color: $black-color !important;
}
.layui-layout-admin .layuimini-logo {
background-color: $black-color !important;
}
.layuimini-menu-left .layui-nav-itemed>.layui-nav-child {
background-color: $black-color !important;
}
.layuimini-menu-left .layui-nav-tree .layui-this,
.layuimini-menu-left .layui-nav-tree .layui-this>a,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this>a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this a {
background-color: $main-color !important;
color: $black-color !important;
.layui-left-nav {
color: $black-color !important;
}
}
.layui-iconpicker-icon {
border-color: $less-main-color !important;
background-color: $less-main-color !important;
}
.layui-iconpicker .layui-anim {
background-color: $black-color;
}
.layui-iconpicker .layui-iconpicker-list {
background-color: $black-color;
}
.layui-header .layui-nav .layui-nav-child dd.layui-this a,
.layui-header .layui-nav-child dd.layui-this,
.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this a {
background-color: $active-color !important;
}

View File

@@ -0,0 +1,81 @@
.layui-btn {
color : #808185 !important;
background-color: #cde7ff !important;
border : 1px solid #d3dde6 !important;
}
.layui-layer-easy .layui-layer-title {
background-color: rgb(215, 215, 215) !important;
color : #6C6E71 !important;
}
.layui-layer-easy .layui-layer-setwin>a::after {
color: #6C6E71 !important;
}
.layui-layer-easy .layui-layer-btn .layui-layer-btn0 {
color : #808185 !important;
background-color: #cde7ff !important;
border : 1px solid #d3dde6 !important;
}
.layuimini-tab .layui-tab-title .layuimini-tab-active {
display: none !important;
}
.layuimini-tab .layui-tab-title li {
padding : 0 5px !important;
font-size : 12px;
border-width: 1px 1px 1px 0 !important;
border-style: solid !important;
}
.layuimini-tab .layui-tab-title li.layui-this {
color : #fff !important;
border-color: rgba(0, 0, 255, 1) !important;
background : rgba(90, 173, 255, 1) !important;
}
.layuimini-tab .layui-tab-title .layui-this span {
color: #fff !important;
}
.layuimini-tab>.layui-tab-title li,
.layuimini-tab>.close-box li {
line-height: 27px !important;
}
.layuimini-tab>.layui-tab-title,
.layuimini-tab>.close-box {
height: 28px !important;
}
.layuimini-tab .layui-tab-control>li {
height : 28px !important;
line-height: 28px !important;
}
.layuimini-container .layui-laypage .layui-laypage-curr .layui-laypage-em {
background-color: #6C6E71 !important;
}
.layuimini-container .layui-form-onswitch {
background-color: #cde7ff !important;
border-color : #cde7ff !important;
}
.layuimini-container .layui-form-switch {
background-color: #6C6E71;
border-color : #6C6E71;
}
.layui-table-tool .layui-inline[lay-event] {
border-color: transparent;
}
.layui-form-checked[lay-skin="primary"] i {
border-color : #cde7ff !important;
background-color: #cde7ff !important;
}

View File

@@ -0,0 +1,708 @@
& {
background-color: #f6f5f4;
color : #2e3436;
}
.layuimini-container {}
.layuimini-main {
padding: 0;
}
.layuimini-container .layui-table-box .layui-table-header th {
color : #979a9b !important;
font-weight : 700 !important;
font-weight : bold;
text-shadow : none;
border-color : #d7d2ce;
border-bottom: 1px solid;
&:not(:last-child) {
border-right: 1px solid #d7d2ce;
}
}
.layui-table-view .layui-table td {
border-bottom: unset;
&:not(:last-child) {
border-right: 1px solid #d7d2ce;
}
}
.layui-table {
color : #2e3436;
background: #f6f5f4;
}
.layui-table-view .layui-table[lay-size="sm"] .layui-table-cell {
line-height: 30px;
}
.layui-table tbody tr:hover,
.layui-table thead tr,
.layui-table-click,
.layui-table-header,
.layui-table-hover,
.layui-table-mend,
.layui-table-patch,
.layui-table-tool,
.layui-table-total,
.layui-table-total tr {
background-color: unset;
}
.layui-table-cell {
padding: 0 4px;
}
.layui-table img {
height: 25px !important;
width : auto !important;
}
.layui-btn {
position : relative;
outline : none;
overflow : visible;
line-height : 28px;
border-radius : 5px;
transition : all .15s cubic-bezier(.25, .46, .45, .94);
padding : 4px 9px;
user-select : none;
color : #2e3436;
outline-color : rgba(53, 132, 228, .5);
border : 1px solid #cdc7c2;
border-bottom-color: rgb(205, 199, 194);
border-bottom-color: #bfb8b1;
background-color : #f6f5f4;
box-shadow : inset 0 1px #fff, 0 1px 2px rgba(0, 0, 0, .07);
&::before {
content : ' ';
position : absolute;
pointer-events: none;
width : 100%;
height : 100%;
top : 0px;
left : 0px;
background : linear-gradient(to top, rgba(0, 0, 0, 0.03), transparent);
transition : all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
&::after {
content : '';
position : absolute;
pointer-events: none;
z-index : 2;
display : inline-block;
border : 2px solid transparent;
border-radius : inherit;
transition : all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
top : -4px;
left : -4px;
width : calc(100% - -4px);
height : calc(100% - -4px);
}
&.layui-btn-normal {
color : #fff;
border-color : #1b6acb #1b6acb #15539e;
background-color: #3c88e5;
box-shadow : inset 0 1px #3181e3, 0 1px 2px rgba(0, 0, 0, .07);
}
&.layui-btn-danger {
color : #fff;
border-color : #b2161d #b2161d #851015;
background-color: #e41f28;
box-shadow : inset 0 1px #db1a23, 0 1px 2px rgba(0, 0, 0, .07);
}
&.layui-btn-sm {
line-height: 20px;
}
&.layui-btn-xs {
line-height: 12px
}
}
.layuimini-container .layui-table-tool .layui-inline[lay-event] {
@extend .layui-btn;
line-height : 32px;
min-width : 32px;
min-height : 32px;
padding : 0;
border-radius: 9999px;
}
.layui-laydate .layui-this {
background-color: #3584e4 !important;
}
.layui-laydate-content td,
.layui-laydate-content th {
width : 36px;
height : 36px;
border-radius: 100%;
}
.laydate-footer-btns span {
@extend .layui-btn;
line-height: 16px;
&:not(:last-child) {
border-right-style : none;
border-top-right-radius : 0;
border-bottom-right-radius: 0;
}
&:not(:first-child) {
border-top-left-radius : 0;
border-bottom-left-radius: 0;
}
}
.layui-laydate-footer span {
@extend .layui-btn;
line-height: 16px;
}
.layui-laydate-footer span.layui-laydate-preview {
line-height: 22px;
}
.layui-form-checked[lay-skin="primary"] i {
border-color : #185fb4 !important;
background-color: #5094e8;
background-image: linear-gradient(0deg, #3987e5, #5094e8);
}
.layui-form-checkbox[lay-skin="primary"]:hover i {
border-color : #185fb4;
background-image: linear-gradient(0deg, #5094e8, #6ba5eb);
}
.layui-table-view .layui-form-checkbox[lay-skin="primary"] i {
border-radius: 3px;
}
.layui-table-view .layui-form-checkbox i {
font-weight: bold;
}
.layui-layer-title {
padding : 0 6px;
min-height : 46px;
border : 1px solid #bfb8b1;
border-top-left-radius : 8px;
border-top-right-radius: 8px;
background : #f1f0ee linear-gradient(0deg, #dfdcd8, #f4f2f1) !important;
color : #2e3436 !important;
padding-left : 12px;
padding-right : 12px;
font-weight : bold;
}
.layui-layer {
background-color: #f6f5f4;
}
.layui-layer-easy .layui-layer-title~.layui-layer-setwin>a {
position : relative;
outline : none;
overflow : visible;
border-radius: 5px;
transition : all .15s cubic-bezier(.25, .46, .45, .94);
user-select : none;
outline-color : rgba(53, 132, 228, .5);
border : 1px solid #cdc7c2;
border-bottom-color: rgb(205, 199, 194);
border-bottom-color: #bfb8b1;
background-color : #f6f5f4;
box-shadow : inset 0 1px #fff, 0 1px 2px rgba(0, 0, 0, .07);
height : 20px;
line-height : 20px;
width : 20px;
text-align : center;
display : inline-flex;
align-items : center;
justify-content : center;
&::after {
color: #2e3436 !important;
}
}
.layui-layer-setwin {
display : flex;
align-items: center;
}
.layuimini-container .layui-form-switch.layui-form-onswitch {
color : #ffffff;
border-color : #185fb4;
background-color: #3584e4;
text-shadow : 0 1px rgba(53, 132, 228, 0.5), 0 0 2px rgba(255, 255, 255, 0.6);
}
.layuimini-container .layui-form-switch {
border : 1px solid #cdc7c2;
border-radius : 20px;
color : rgba(46, 52, 54, 0.2);
background-color: #e1dedb;
text-shadow : 0 1px rgba(0, 0, 0, 0.1);
transition : all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.layui-form-switch {
height: 20px;
width : 40px;
}
.layui-form-switch {
padding : 0;
line-height: 20px;
}
.layui-form-switch.layui-form-onswitch i {
top : 0;
height : 20px;
width : 20px;
margin-left: -20px;
}
.layui-form-switch i {
top : 0;
height : 20px;
width : 20px;
margin-left : -5px;
border-radius : 50%;
transition : all 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
color : #2e3436;
outline-color : rgba(53, 132, 228, 0.5);
border-color : #cdc7c2;
border-bottom-color: #bfb8b1;
background-color : #f6f5f4;
box-shadow : inset 0 1px white, 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07);
}
.layuimini-main {
background-color: unset;
}
.layui-tab-title li {
transition : none;
padding : 3px 12px;
min-height : 30px;
min-width : 65px;
display : flex;
justify-content : center;
align-items : center;
white-space : nowrap;
color : #2e3436;
font-weight : normal;
user-select : none;
border : 0;
border-top-style : none;
border-right-style : none;
border-bottom-style: none;
border-left-style : none;
&:hover {
box-shadow : inset 0 -3px #c8c2bc;
background-color: #edebe9;
}
&.layui-this {
box-shadow : inset 0 -3px #3584e4;
background-color: #f1f0ee;
}
}
.layui-tab-title {
display : flex;
align-items : center;
flex-direction: row;
padding-left : 4px;
padding-right : 4px;
}
.layuimini-tab>.layui-tab-title li,
.layuimini-tab>.close-box li {
line-height: 20px !important;
}
.layuimini-tab .layui-tab-title .layui-this .layuimini-tab-active {
display: none;
}
.layuimini-tab .layui-tab-title .layui-this span {
color: #2e3436 !important
}
.layuimini-tab .layui-tab-title span {
color: #2e3436 !important
}
.layuimini-tab .layui-tab-title .layuimini-tab-active {
display: none;
}
.layuimini-tab .layui-tab-title {
background-color: #e1dedb;
border-color : #cdc7c2;
}
.layui-layer-easy .layui-layer-btn {
background-color: #f2f2f2;
}
.layui-layer-dialog .layui-layer-content {
background-color: #f2f2f2;
}
.layui-layer-easy .layui-layer-btn a {
position : relative;
outline : none;
overflow : visible;
line-height : 22px;
height : 22px;
border-radius : 5px;
transition : all .15s cubic-bezier(.25, .46, .45, .94);
padding : 4px 9px;
user-select : none;
color : #2e3436 !important;
outline-color : rgba(53, 132, 228, .5);
border : 1px solid #cdc7c2;
border-bottom-color: rgb(205, 199, 194);
border-bottom-color: #bfb8b1;
background-color : #f6f5f4;
box-shadow : inset 0 1px #fff, 0 1px 2px rgba(0, 0, 0, .07);
&::before {
content : ' ';
position : absolute;
pointer-events: none;
width : 100%;
height : 100%;
top : 0px;
left : 0px;
background : linear-gradient(to top, rgba(0, 0, 0, 0.03), transparent);
transition : all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
&::after {
content : '';
position : absolute;
pointer-events: none;
z-index : 2;
display : inline-block;
border : 2px solid transparent;
border-radius : inherit;
transition : all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
top : -4px;
left : -4px;
width : calc(100% - -4px);
height : calc(100% - -4px);
}
&.layui-layer-btn0 {
color : #fff !important;
border-color : #1b6acb #1b6acb #15539e;
background-color: #3c88e5;
box-shadow : inset 0 1px #3181e3, 0 1px 2px rgba(0, 0, 0, .07);
}
}
.layui-tab-brief {
&>.layui-tab-title {
li {
@extend .layui-btn;
&:not(:last-child) {
border-right-style : none;
border-top-right-radius : 0;
border-bottom-right-radius: 0;
}
&:not(:first-child) {
border-top-left-radius : 0;
border-bottom-left-radius: 0;
}
&.layui-this {
color : unset;
color : #2e3436;
border-color : #cdc7c2;
background-color: #d6d1cd;
box-shadow : inset 0 1px rgba(255, 255, 255, 0);
&::after {
display: none;
}
}
&:hover {
box-shadow : unset;
background-color: #edebe9;
}
}
}
}
.layui-form-radio:hover *,
.layui-form-radioed,
.layui-form-radioed>i {
color: #5094e8;
}
.layui-card {
.layui-card-header {
padding : 0 6px;
min-height : 46px;
border-width : 1px;
border-style : solid;
border-color : #bfb8b1;
border-top-left-radius : 8px;
border-top-right-radius: 8px;
background : #f1f0ee linear-gradient(to top, #dfdcd8, #f4f2f1);
box-shadow : inset 0 1px rgba(255, 255, 255, 0.8), inset 0 -2px 2px rgba(0, 0, 0, 0.02);
.icon {
color: #979a9b !important;
}
}
.layui-card-body {
background-color: #f6f5f4;
border : 1px solid #cdc7c2;
}
}
.layui-btn,
.layui-input,
.layui-select,
.layui-textarea,
.layui-upload-button,
.city-picker-span {
border-radius: 5px;
border : 1px solid #cdc7c2;
}
.layui-side.layui-bg-black,
.layui-side.layui-bg-black>.layuimini-menu-left>ul,
.layuimini-menu-left-zoom>ul {
background-color: #f6f5f4 !important;
}
.layuimini-menu-left {
background-color: #fbfafa;
border-right : 1px solid #cdc7c2;
.layui-nav {
li {
&:not(:first-child) {
border-top: 1px solid #cdc7c2;
}
&.layui-this {
a {
background-color: #347cd3 !important;
color : #fff !important;
}
}
}
}
}
.layuimini-menu-left .layui-nav .layui-this a,
.layuimini-menu-left-zoom.layui-nav .layui-this a {
color: #2e3436 !important;
}
.layuimini-menu-left .layui-nav .layui-nav-item.layui-this a:hover,
.layuimini-menu-left-zoom.layui-nav .layui-nav-item.layui-this a:hover {
color: #fff !important;
}
.layuimini-menu-left .layui-nav .layui-nav-item a,
.layuimini-menu-left-zoom.layui-nav .layui-nav-item a {
color: #2e3436 !important;
&:hover {
color: #2e3436 !important;
}
}
.layuimini-menu-left .layui-nav .layui-nav-item a {
height : 30px;
line-height: 30px;
}
.layuimini-menu-left .layui-nav-itemed>.layui-nav-child {
background-color: rgba(46, 52, 54, 0.05) !important;
border-top : 1px solid #d7d2ce;
dd {
border-bottom: 1px solid #d7d2ce;
}
}
.layuimini-menu-left .layui-nav-tree .layui-this,
.layuimini-menu-left .layui-nav-tree .layui-this>a,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this>a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this a {
background-color: #347cd3 !important;
color : #fff !important;
}
.layui-layout-admin .layuimini-logo {
background-color: unset !important;
}
.layuimini-header-menu>li.layui-nav-item {
@extend .layui-btn;
margin-top : 10px;
height : 30px !important;
line-height: 30px !important;
&::after {
background-color: transparent;
}
&:not(:last-child) {
border-right-style : none;
border-top-right-radius : 0;
border-bottom-right-radius: 0;
}
&:not(:first-child) {
border-top-left-radius : 0;
border-bottom-left-radius: 0;
}
&:nth-last-child(2) {
border-right-style : solid;
border-radius : 5px;
border-top-left-radius : 0;
border-bottom-left-radius: 0;
}
}
.layui-layout-admin .layui-header {
background-color: #f6f5f4 !important;
}
.layui-layer-dialog {
border-radius: 20px;
}
.layui-layer-iframe {
border-radius: 20px;
}
.layuimini-tab .layui-tab-control>li {
background-color: #e1dedb;
border-style : solid;
border-color : #cdc7c2;
}
.form-search .layui-btn {
height : 32px;
line-height: 22px;
}
.form-search .layui-form-select dl {
transition : all 80ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
background-color : #ffffff;
background-clip : padding-box;
border : 1px solid rgba(0, 0, 0, 0.23);
border-top-left-radius : 0;
border-top-right-radius: 0;
border-radius : 9px;
border-top-left-radius : 0px;
border-top-right-radius: 0px;
box-shadow : 0 1px 3px 2px rgba(0, 0, 0, 0.08);
top : 28px;
}
.layui-form-select dl dd,
.layui-form-select dl dt {
height : 30px;
line-height: 30px;
padding : 0 10px;
}
.layuimini-container .layui-form-select dl dd.layui-this {
background-color: #3c88e5 !important;
}
.layui-table-header .layui-table {
margin-bottom: 0;
}
.layui-header .layui-nav .layui-nav-child dd.layui-this a,
.layui-header .layui-nav-child dd.layui-this,
.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this a {
background-color: #3c88e5 !important;
}
.layuimini-mobile-show {
.layui-nav-more {
top: 4px;
}
}
.layui-layer-easy .layui-layer-setwin>span {
color: #2e3436;
}
.layui-layer-easy .layui-layer-setwin>span:after {
color: #2e3436;
}
.layui-layer-easy .layui-layer-setwin .layui-layer-max::after,
.layui-layer-easy .layui-layer-setwin .layui-layer-max::before {
border-color: #2e3436;
}
.layui-layer-easy .layui-layer-setwin .layui-layer-min::before {
background-color: #2e3436;
}

View File

@@ -0,0 +1,284 @@
$main-color:#61677C;
& {
background-color: #EBECF0;
}
.layui-layout-admin .layui-header {
background-color: #EBECF0 !important;
}
.layuimini-container .layui-btn-success {
color: #4bb368;
}
.layuimini-container .layui-btn-danger {
color: #f56c6c;
}
.layuimini-container .layuimini-btn-primary {
color: #2c3e50;
}
.layuimini-container .layui-btn-primary {
color: #5f5f5f;
}
.layuimini-container .layui-btn-normal {
color: #1e9fff;
}
.layui-btn {
border: 0;
outline: 0;
border-radius: 10px;
background-color: #EBECF0;
text-shadow: 1px 1px 0 #FFF;
color: $main-color;
box-shadow: -5px -5px 20px #FFF, 5px 5px 20px #BABECC;
transition: all 0.2s ease-in-out;
cursor: pointer;
font-weight: 600;
&:hover {
box-shadow: -2px -2px 5px #FFF, 2px 2px 5px #BABECC;
}
}
.layui-table-tool .layui-inline[lay-event] {
@extend .layui-btn;
}
.layuimini-main {
@extend .layui-btn;
text-shadow: 0.5px 0.5px 0 #fff;
}
.layui-layer {
@extend .layui-btn;
}
.layui-layer-easy .layui-layer-title {
background-color: transparent !important;
color: $main-color !important;
}
.layui-layer-easy .layui-layer-setwin>span {
color: $main-color !important;
&::before {
border-color: $main-color !important;
}
&::after {
border-color: $main-color !important;
}
}
.layui-layer-easy .layui-layer-setwin .layui-layer-min::before {
background-color: $main-color !important;
}
.layui-card {
@extend .layui-btn;
text-shadow: 0.5px 0.5px 0 #fff;
}
.layuimini-container .layui-table-tool {
background-color: transparent;
}
.layui-table {
background-color: #EBECF0;
}
.panel {
@extend .layui-btn;
text-shadow: 0.5px 0.5px 0 #fff;
}
.layuimini-qiuck-module {
@extend .layui-btn;
i {
background-color: transparent
}
}
.layui-side.layui-bg-black,
.layui-side.layui-bg-black>.layuimini-menu-left>ul,
.layuimini-menu-left-zoom>ul {
@extend .layui-btn;
background-color: #EBECF0 !important;
border-radius: 0;
}
.layuimini-menu-left .layui-nav .layui-nav-item a,
.layuimini-menu-left-zoom.layui-nav .layui-nav-item a {
@extend .layui-btn;
&:hover {
box-shadow: -2px -2px 5px #FFF, 2px 2px 5px #BABECC;
}
}
.layui-nav-tree .layui-nav-item {
margin: 10px;
width: calc(100% - 20px);
}
.layuimini-menu-left .layui-nav-itemed>.layui-nav-child {
background-color: transparent !important;
}
.layui-nav-tree .layui-nav-child dd {
margin: 10px;
width: calc(100% - 20px);
}
.layuimini-menu-left .layui-nav-tree .layui-this,
.layuimini-menu-left .layui-nav-tree .layui-this>a,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this>a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this a {
background-color: inherit !important;
}
.layuimini-menu-left .layui-nav .layui-nav-item a:hover,
.layuimini-menu-left .layui-nav .layui-this a,
.layuimini-menu-left-zoom.layui-nav .layui-nav-item a:hover,
.layuimini-menu-left-zoom.layui-nav .layui-this a {
color: $main-color !important;
}
.layui-layout-admin .layuimini-logo {
background-color: transparent !important;
}
.layui-header {
.layuimini-header-menu>.layui-nav-item {
@extend .layui-btn;
margin: 10px;
line-height: 40px !important;
height: 40px !important;
}
.layui-nav .layui-nav-item {
@extend .layui-btn;
margin: 10px;
line-height: 40px !important;
height: 40px !important;
}
}
.layui-btn-xs {
padding: 0 9px;
}
.layui-input,
.layui-textarea {
border: 0;
outline: 0;
font-size: 16px;
border-radius: 320px;
padding: 16px;
background-color: #EBECF0;
text-shadow: 1px 1px 0 #FFF;
margin-right: 8px;
box-shadow: inset 2px 2px 5px #BABECC, inset -5px -5px 10px #FFF;
width: 100%;
box-sizing: border-box;
transition: all 0.2s ease-in-out;
appearance: none;
-webkit-appearance: none;
}
.layuimini-upload .layuimini-upload-btn {
background-color: transparent !important;
}
.layui-form-pane .layui-form-label {
background-color: transparent;
text-align: right;
}
.layuimini-container .layui-form-select dl dd.layui-this {
background-color: transparent !important;
color: #2c3e50
}
.layuimini-color .elem-content li.layui-this {
text-shadow: 0.5px 0.5px 0 #FFF;
}
.layui-form-select dl {
@extend .layui-btn;
}
.layui-form-checkbox {
@extend .layui-btn;
padding: 10px 10px 10px 24px;
}
.layui-form-checkbox>i {
left: 6px;
top: 10px;
text-shadow: 0.5px 0.5px 0 #fff;
}
.layui-form-checked:hover>div,
.layui-form-checked>div {
color: #16b777;
}
.layui-tree,
.laytable-cell-checkbox {
.layui-form-checkbox[lay-skin="primary"] {
padding: 13px;
}
.layui-form-checkbox[lay-skin="primary"]>i {
left: 5px;
top: 5px
}
}
.layui-tree-txt {
margin-left: 10px
}
.layui-form-radio {
@extend .layui-btn;
}
.layui-form-radio:hover>*,
.layui-form-radioed,
.layui-form-radioed>i {
color: #16b777;
}
.layui-table-tips-c {
text-shadow: 0.5px 0.5px 0 #fff;
}
.layuimini-tab .layui-tab-title {
background-color: #EBECF0;
}
.layuimini-tab .layui-tab-control>li {
background-color: #EBECF0;
}

View File

@@ -0,0 +1,344 @@
a,
button {
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAzElEQVRYR+2X0Q6AIAhF5f8/2jYXZkwEjNSVvVUjDpcrGgT7FUkI2D9xRfQETwNIiWO85wfINfQUEyxBG2ArsLwC0jioGt5zFcwF4OYDPi/mBYKm4t0U8ATgRm3ThFoAqkhNgWkA0jJLvaOVSs7j3qMnSgXWBMiWPXe94QqMBMBc1VZIvaTu5u5pQewq0EqNZvIEMCmxAawK0DNkay9QmfFNAJUXfgGgUkLaE7j/h8fnASkxHTz0DGIBMCnBeeM7AArpUd3mz2x3C7wADglA8BcWMZhZAAAAAElFTkSuQmCC) 14 0, pointer
}
& {
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABFklEQVRYR9WXURLDIAhE6/0PbSdOtUpcd1Gnpv1KGpTHBpCE1/cXq+vrMph7dGvXZTtpfW10DCA5jrH1H0Jhs5E0hnZdCR+vb5S8Nn8mQCeS9BdSalYJqMBjAGzq59xAESN7VFVUgV8AZB/dZBR7QTFDCqGquvUBVVoEtgIwpQRzmANSFHgWQKExHdIrPeuMvQNDarXe6nC/AutgV3JW+6bgqQLeV8FekRtgV+ToDKEKnACYKsfZjjkam7a0ZpYTytwmgainpC3HvwBocgKOxqRjehoR9DFKNFYtOwCGYCszobeCbl26N6yyQ6g8X/Wex/rBPsNEV6qAMaJPMynIHQCoSqS9JSMmwef51LflTgCRszU7DvAGiV6mHWfsaVUAAAAASUVORK5CYII=), auto;
font-family: "ark-pixel";
}
$border-line-width:3px;
.public-border {
border-style: solid;
border-width: $border-line-width;
border-image-slice: 2;
border-image-width: 2;
border-image-repeat: stretch;
border-image-source: url('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" ?><svg version="1.1" width="5" height="5" xmlns="http://www.w3.org/2000/svg"><path d="M2 1 h1 v1 h-1 z M1 2 h1 v1 h-1 z M3 2 h1 v1 h-1 z M2 3 h1 v1 h-1 z" fill="rgb(33,37,41)" /></svg>');
border-image-outset: 2;
}
.layui-btn {
@extend .public-border;
line-height: 32px;
position: relative;
display: inline-block;
text-align: center;
vertical-align: middle;
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAzElEQVRYR+2X0Q6AIAhF5f8/2jYXZkwEjNSVvVUjDpcrGgT7FUkI2D9xRfQETwNIiWO85wfINfQUEyxBG2ArsLwC0jioGt5zFcwF4OYDPi/mBYKm4t0U8ATgRm3ThFoAqkhNgWkA0jJLvaOVSs7j3qMnSgXWBMiWPXe94QqMBMBc1VZIvaTu5u5pQewq0EqNZvIEMCmxAawK0DNkay9QmfFNAJUXfgGgUkLaE7j/h8fnASkxHTz0DGIBMCnBeeM7AArpUd3mz2x3C7wADglA8BcWMZhZAAAAAElFTkSuQmCC) 14 0, pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
color: #212529;
background-color: #fff;
border-image-repeat: stretch;
&::after {
position: absolute;
top: -$border-line-width;
right: -$border-line-width;
bottom: -$border-line-width;
left: -$border-line-width;
content: "";
box-shadow: inset (-$border-line-width) (-$border-line-width) #adafbc;
}
&:hover {
color: #212529;
text-decoration: none;
background-color: #e7e7e7;
}
&.layui-btn-sm {
line-height: 100%;
}
&.layui-btn-xs {
line-height: 16px;
}
&.layui-btn-normal {
color: #fff;
background-color: #209cee;
&::after {
box-shadow: inset (-$border-line-width) (-$border-line-width) #006bb3;
}
&:hover {
background-color: #108de0;
}
}
&.layui-btn-success {
color: #fff;
background-color: #92cc41;
&::after {
box-shadow: inset (-$border-line-width) (-$border-line-width) #4aa52e;
}
&:hover {
background-color: #92cc41;
}
}
&.layui-btn-danger {
color: #fff;
background-color: #e76e55;
&::after {
box-shadow: inset (-$border-line-width) (-$border-line-width) #8c2022;
}
&:hover {
background-color: #ce372b;
}
}
&.layui-btn-warm {
color: #212529;
background-color: #f7d51d;
&::after {
box-shadow: inset (-$border-line-width) (-$border-line-width) #e59400;
}
&:hover {
color: #212529;
background-color: #f2c409;
}
}
}
.form-search .layui-btn {
line-height: 16px;
font-size: 14px;
}
.layuimini-container .layui-table-tool .layui-inline[lay-event] {
@extend .layui-btn;
line-height: 24px;
}
.layui-table img {
image-rendering: pixelated;
}
.layui-form-checkbox[lay-skin="primary"] i {
@extend .public-border;
position: relative;
}
.layui-form-checked[lay-skin="primary"] i {
background-color: unset;
color: #212529;
font-weight: bold;
font-size: 28px;
}
.layui-btn {
font-size: 18px;
}
.layui-btn-sm {
font-size: 16px;
}
.layui-btn-xs {
font-size: 14px;
}
.layui-nav * {
font-size: 16px;
}
.layui-tab-title li {
font-size: 18px;
}
.layui-nav-tree .layui-nav-item {
border-bottom: 3px solid #212529;
}
.layuimini-header-menu>.layui-nav-item {
@extend .public-border;
border-image-outset: 0;
}
.layui-table td,
.layui-table th {
font-size: 16px;
border-width: 0 2px 2px 0;
border-style: solid;
border-color: #212529
}
.layui-input,
.layui-select,
.layui-textarea {
@extend .public-border;
}
.layui-form-label {
background-color: unset;
border: unset;
text-align: right;
color: #212529;
font-size: 16px;
font-weight: bold;
}
.layuimini-form>.layui-form-item>.layui-input-block tip,
.layuimini-form>.layui-form-item>.layui-inline tip {
font-size: 14px;
}
.layui-layer-easy .layui-layer-title {
background: unset !important;
color: #212529 !important;
font-size: 16px;
border-bottom: 1px solid #f0f0f0;
}
.layui-layer-easy .layui-layer-btn {
background: unset !important;
}
.layui-layer-easy .layui-layer-btn a {
@extend .layui-btn;
color: #212529 !important;
&.layui-layer-btn0 {
color: #fff !important;
background-color: #209cee;
&::after {
box-shadow: inset (-$border-line-width) (-$border-line-width) #006bb3;
}
&:hover {
background-color: #108de0;
}
}
}
.layui-layer-dialog .layui-layer-content {
font-size: 16px;
}
.layui-layer-easy .layui-layer-setwin>span {
color: #212529;
}
.layui-layer-easy .layui-layer-setwin>span:after {
color: #212529;
}
.layui-layer-easy .layui-layer-setwin .layui-layer-max::after,
.layui-layer-easy .layui-layer-setwin .layui-layer-max::before {
border-color: #212529;
}
.layui-layer-easy .layui-layer-setwin .layui-layer-min::before {
background-color: #212529;
}
.layui-layer {
@extend .public-border;
}
.layui-card {
@extend .public-border;
.layui-card-header {
font-size: 18px;
}
}
.layuimini-main {
@extend .public-border;
}
.layui-table-box {
@extend .public-border;
border-image-outset: 1;
margin-top: 5px;
}
.layuimini-container .layui-table-box .layui-table-header th {
color: #212529 !important;
}
.layui-table tbody tr:hover,
.layui-table thead tr,
.layui-table-click,
.layui-table-header,
.layui-table-hover,
.layui-table-mend,
.layui-table-patch,
.layui-table-tool,
.layui-table-total,
.layui-table-total tr {
background-color: unset;
}
.layui-table-header .layui-table {
margin-bottom: 0;
}
.layui-badge,
.layui-badge-rim {
position: relative;
border-radius: 0;
&::after {
content: '';
position: absolute;
left: 0;
width: 100%;
height: 100%;
top: 0;
pointer-events: none;
box-shadow: 0 4px #209cee, 0 -4px #209cee, 4px 0 #209cee, -4px 0 #209cee;
}
&.layui-bg-blue {
background-color: #209cee;
&::after {
box-shadow: 0 4px #209cee, 0 -4px #209cee, 4px 0 #209cee, -4px 0 #209cee;
}
}
&.layui-bg-gray {
background-color: #92cc41;
&::after {
box-shadow: 0 4px #92cc41, 0 -4px #92cc41, 4px 0 #92cc41, -4px 0 #92cc41;
}
}
}
.layuimini-color .elem-content li {
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAzElEQVRYR+2X0Q6AIAhF5f8/2jYXZkwEjNSVvVUjDpcrGgT7FUkI2D9xRfQETwNIiWO85wfINfQUEyxBG2ArsLwC0jioGt5zFcwF4OYDPi/mBYKm4t0U8ATgRm3ThFoAqkhNgWkA0jJLvaOVSs7j3qMnSgXWBMiWPXe94QqMBMBc1VZIvaTu5u5pQewq0EqNZvIEMCmxAawK0DNkay9QmfFNAJUXfgGgUkLaE7j/h8fnASkxHTz0DGIBMCnBeeM7AArpUd3mz2x3C7wADglA8BcWMZhZAAAAAElFTkSuQmCC) 14 0, pointer;
}

View File

@@ -0,0 +1,802 @@
// 通常用于背景色
$black-color :rgb(2, 17, 20);
// 通常用于字体颜色
$main-color :rgb(126, 252, 246);
// 通常用于边框
$less-main-color:rgb(6, 216, 215);
$plus-main-color:rgb(0, 125, 124);
// 通常用于激活状态,通常跟背景色搭配
$cover-color :rgba(62, 251, 251, 0.05);
// 更强的激活状态,适合做选项操作时使用
$active-color :rgba(62, 251, 251, 0.5);
$red-color :rgb(255, 0, 0);
$yellow-color:rgb(255, 255, 153);
$green-color :rgb(0, 255, 0);
& {
background-color: $black-color;
color: $main-color;
}
.layuimini-main {
background-color: $black-color;
}
.layuimini-container {
background-color: $black-color;
}
.layuimini-container .layui-table-tool {
background-color: $black-color;
}
$box-shape: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
.layui-btn {
&.layui-btn-lg {
padding-right: 25px;
}
clip-path : $box-shape;
padding-right : 10px;
position : relative;
background-color: transparent;
border : 1px solid;
color : rgb(126, 252, 246);
text-shadow : rgb(126, 252, 246) 0px 0px 1px;
background-color: rgb(126, 252, 246);
&::before {
content: '';
display: block;
position: absolute;
z-index: -1;
clip-path: $box-shape;
width: 100%;
height: 100%;
left: -0.5px;
top: -0.5px;
background-color: $black-color;
}
&:hover::after {
content: '';
display: block;
position: absolute;
z-index: -1;
clip-path: $box-shape;
width: 100%;
height: 100%;
left: -0.5px;
top: -0.5px;
background-color: rgba(126, 252, 246, 0.2);
}
&.layui-btn-disabled {
&::after {
// todo:实现按钮禁用样式
}
}
&.layui-btn-success {
text-shadow: rgb(0, 255, 0) 0px 0px 1px;
color: rgb(0, 255, 0);
background-color: rgba(0, 255, 0);
border: unset;
&:hover::after {
background-color: rgba(0, 255, 0, 0.2);
}
}
&.layui-btn-danger {
text-shadow: rgb(255, 0, 0) 0px 0px 1px;
color: rgb(255, 0, 0);
background-color: rgb(255, 0, 0);
border: unset;
&:hover::after {
background-color: rgba(255, 0, 0, 0.2);
}
}
&.layuimini-btn-primary {
text-shadow: rgb(255, 255, 153) 0px 0px 1px;
color: rgb(255, 255, 153);
background-color: rgb(255, 255, 153);
border: unset;
&:hover::after {
background-color: rgba(255, 255, 153, 0.2);
}
}
&.layui-btn-primary {
border: unset;
}
&.layui-btn-normal {
color: $black-color;
background-color: $main-color;
border: unset;
&::before {
background-color: $main-color;
}
&:hover::after {
background-color: $main-color;
}
}
}
.layui-table {
background: $black-color;
color: $main-color;
tr {
background-color: $cover-color;
}
td {
border-color: $less-main-color;
}
}
.layui-table-header {
background-color: $black-color;
}
.layuimini-container .layui-table-box {
border-color: $less-main-color;
}
.layuimini-container .layui-table-box .layui-table-header th {
color: $main-color !important;
}
.layui-table-tool .layui-inline[lay-event] {
color: $main-color;
border: none;
position: relative;
&::after {
border-color: $main-color;
border-width: 1px;
border-style: solid;
clip-path: $box-shape;
position: absolute;
left: 0;
top: 0;
width: calc(100% - 1px);
height: calc(100% - 1px);
content: '';
display: block;
}
}
.layui-table tbody tr:hover {
background-color: rgba(126, 252, 246, 0.1);
}
.layuimini-main {
border-color: $less-main-color;
}
.layui-table-header {
border-color: $less-main-color;
}
.color-content>ul>li>a>div>span:nth-child(2) {
background-color: $black-color !important;
}
.layuimini-color .elem-content li {
clip-path: $box-shape;
}
.layuimini-color .elem-content li.layui-this {
background-color: $main-color;
color: $black-color !important;
border-color: $less-main-color;
}
.layuimini-color .more-menu-item {
color: $main-color;
&:hover {
background-color: rgba(62, 251, 251, 0.05);
}
}
.layui-layout-admin .layui-header {
background-color: $cover-color !important;
}
.layuimini-tab .layui-tab-title {
background-color: $cover-color !important;
border-color: $less-main-color;
}
.layuimini-tab .layui-tab-title span {
color: $active-color;
}
.layui-header .layuimini-menu-header-pc.layui-nav .layui-nav-item a:hover,
.layui-header .layuimini-header-menu.layuimini-pc-show.layui-nav .layui-this a {
color: $black-color !important;
}
.layui-layout-admin .layui-header .layui-nav .layui-nav-item a {
color: $main-color !important;
}
.layui-layout-admin .layui-header .layui-nav .layui-nav-item {
background-color: $active-color;
}
.layui-layout-admin .layui-header .layuimini-header-content>ul>.layui-nav-item.layui-this,
.layuimini-tool i:hover {
background-color: $main-color !important;
}
.layuimini-tab .layui-tab-control>li {
background-color: $black-color;
border-color: $less-main-color;
}
.layuimini-tab .layui-tab-title li {
border-color: $less-main-color;
}
.layui-flow-more a *,
.layui-laypage input,
.layui-table-view select[lay-ignore] {
border-color: $less-main-color;
background-color: $black-color;
color: $main-color;
}
.layui-laypage button,
.layui-laypage input {
border-color: $less-main-color;
background-color: $black-color;
color: $main-color;
}
.layuimini-container .layui-form-switch {
border-color: $less-main-color !important;
background-color: $black-color !important;
}
.layuimini-container .layui-form-onswitch {
background-color: $main-color !important;
}
.layuimini-container .layui-form-switch.layui-form-onswitch i {
background-color: $black-color !important;
}
.layuimini-container .layui-laypage .layui-laypage-curr .layui-laypage-em {
background-color: $main-color !important;
}
.layui-laypage .layui-laypage-curr em {
color: $black-color !important;
}
.layuimini-tab .layui-tab-title .layui-this .layuimini-tab-active {
background-color: $less-main-color;
}
.layui-table-view .layui-form-checkbox.layui-form-checked[lay-skin="primary"] i {
border-color: $main-color;
background-color: $main-color;
color: $black-color;
}
.layui-table-view .layui-form-checkbox[lay-skin="primary"] i {
background-color: $cover-color;
}
.layui-table-init {
background-color: $black-color;
}
.box-border-line {
position: relative;
border: 1px solid $less-main-color;
border-width: 1px;
$border-offset: 3px;
&::before {
content: '';
position: absolute;
width: 50px;
height: 20px;
transition: all 0.2s;
border: 1px solid $less-main-color;
top: -$border-offset;
left: -$border-offset;
border-right: none;
border-bottom: none;
pointer-events: none;
}
&::after {
content: "";
position: absolute;
width: 50px;
height: 20px;
transition: all 0.2s;
border: 1px solid $less-main-color;
bottom: -$border-offset;
right: -$border-offset;
border-left: none;
border-top: none;
pointer-events: none;
}
&:hover {
&::after,
&::before {
width: calc(100% + $border-offset);
height: calc(100% + $border-offset);
}
}
}
.table-search-fieldset {
.layui-input-inline {
margin-right: 0;
}
.layui-form-item:not(:last-child) {
margin-right: 10px;
@extend .box-border-line;
}
.layui-form-label {
border: unset;
}
}
.layui-form-pane .layui-form-label {
background-color: $cover-color;
color: $main-color;
}
.layuimini-container .table-search-fieldset {
color: $main-color;
border-color: $less-main-color;
}
.layui-form-select,
.layui-form-autocomplete {
dl {
color: $main-color;
background-color: $black-color;
border-color: $less-main-color;
dd {
&.layui-this {
background-color: $main-color !important;
color: $black-color;
}
&:hover {
background-color: $less-main-color !important;
color: $black-color;
}
}
}
}
.layui-iconpicker-item,
.layui-iconpicker-item:hover {
border-color: $less-main-color !important;
color: $black-color;
.layui-iconpicker-icon:hover {
border-color: $less-main-color !important;
}
}
.layui-nav-child {
background-color: $black-color;
color: $main-color;
}
.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child a {
color: $main-color !important;
}
.layui-badge,
.layui-badge-dot,
.layui-badge-rim {
background-color: $main-color;
}
.layui-nav .layui-nav-child a:hover {
background-color: $active-color;
}
.layui-table-tool-panel {
background-color: $black-color;
}
.layui-form-checkbox[lay-skin="primary"] span {
color: $main-color;
}
.layui-table-tool-panel li:hover {
background-color: $active-color;
}
.layui-side.layui-bg-black,
.layui-side.layui-bg-black>.layuimini-menu-left>ul,
.layuimini-menu-left-zoom>ul {
background-color: $black-color !important;
}
.layui-side {
border-right: 1px solid $main-color;
}
.layui-bg-blue {
background-color: #0000ff !important;
}
.layui-bg-orange {
background-color: $yellow-color !important;
color: $black-color !important;
}
.layui-bg-green {
background-color: $green-color !important;
color: $black-color !important;
}
.layui-table-hover {
background-color: $cover-color;
}
.layui-table-grid-down {
background-color: $black-color;
color: $main-color;
border-color: unset;
}
.layui-table-tips-main {
background-color: $main-color;
color: $black-color;
}
.layui-layout-admin .layui-header .layuimini-tool i {
color: $main-color;
}
.color-red {
color: $red-color !important;
}
.layuimini-tab .layui-tab-title .layui-this span {
color: $main-color;
}
.layui-card {
background-color: $cover-color;
border-radius: 0;
@extend .box-border-line;
.layui-card-header {
color: $main-color;
}
}
.layui-text {
color: $main-color;
}
.layuimini-qiuck-module {
cursor: pointer;
a i {
background-color: $cover-color;
color: $active-color;
}
a cite {
color: $active-color;
}
&:hover {
a i {
background-color: $active-color;
color: $main-color;
}
a cite {
color: $main-color;
}
}
}
.layui-bg-number {
background-color: $cover-color;
}
.layui-input,
.layui-select,
.layui-textarea,
.city-picker-span,
.main-input {
background-color: $cover-color;
color: $main-color;
}
.city-picker-span>.title>span {
color: $main-color;
&:hover {
background-color: $main-color;
color: $black-color;
}
}
.layui-form-radio:hover *,
.layui-form-radioed,
.layui-form-radioed>i {
color: $main-color;
}
.layui-form-checked[lay-skin="primary"] i {
border-color: $main-color !important;
background-color: $main-color;
color: $black-color;
}
.layui-input:focus,
.layui-textarea:focus {
border-color: $main-color !important;
}
.layui-input:hover,
.layui-textarea:hover {
border-color: $main-color !important;
}
.layui-badge-rim,
.layui-border,
.layui-colla-content,
.layui-colla-item,
.layui-collapse,
.layui-elem-field,
.layui-form-pane .layui-form-item[pane],
.layui-form-pane .layui-form-label,
.layui-input,
.layui-layedit,
.layui-layedit-tool,
.layui-panel,
.layui-quote-nm,
.layui-select,
.layui-tab-bar,
.layui-tab-card,
.layui-tab-title,
.layui-tab-title .layui-this::after,
.layui-textarea {
border-color: $main-color !important;
}
.form-search .layui-input-inline input,
.form-search .layui-input-inline select {
border-width: 0 0 0 1px;
}
.layuimini-tab .layui-tab-tool .layui-nav-child {
border-color: $main-color;
}
.layui-nav .layui-nav-child a {
color: $main-color;
}
.layui-form-item {
margin-bottom: 12px;
}
.layuimini-upload .layuimini-upload-btn {
background-color: $black-color;
}
.layui-layer {
border: 1px solid $main-color;
background-color: $black-color;
}
.layui-layer-easy .layui-layer-title {
background-color: $plus-main-color !important;
color: $main-color !important;
}
.layui-table td,
.layui-table th,
.layui-table-col-set,
.layui-table-fixed-r,
.layui-table-grid-down,
.layui-table-header,
.layui-table-page,
.layui-table-tips-main,
.layui-table-tool,
.layui-table-total,
.layui-table-view,
.layui-table[lay-skin="line"],
.layui-table[lay-skin="row"] {
border-color: $less-main-color;
}
.layui-table tbody tr:hover,
.layui-table thead tr,
.layui-table-click,
.layui-table-header,
.layui-table-hover,
.layui-table-mend,
.layui-table-patch,
.layui-table-tool,
.layui-table-total,
.layui-table-total tr {
background-color: $black-color;
}
.hr-line {
color: $main-color;
border-color: $less-main-color;
background-color: $main-color;
}
.layui-layer-easy .layui-layer-btn {
background-color: $black-color;
border-top: 1px solid $less-main-color;
}
.layui-layer-easy .layui-layer-btn .layui-layer-btn0 {
background-color: $main-color;
border-color: $less-main-color;
color: $main-color !important;
clip-path: $box-shape;
}
.layui-layer-easy .layui-layer-btn a {
@extend .layui-btn
}
.layui-layer-shade {
background-color: #fff !important;
}
.layuimini-menu-left {
.layui-nav-tree .layui-nav-item {
margin: 5px;
width: calc(100% - 10px);
}
.layui-nav .layui-nav-item {
clip-path: $box-shape;
color: $main-color;
background-color: $active-color;
&.layui-this a {
background-color: $main-color !important;
color: $black-color !important;
}
&.layui-this {
background-color: $main-color !important;
color: $black-color !important;
span {
background-color: $main-color !important;
color: $black-color !important;
}
}
}
.layui-nav .layui-nav-item a:hover {
background-color: $active-color !important;
}
}
.layuimini-menu-left .layui-nav .layui-nav-item a,
.layuimini-menu-left-zoom.layui-nav .layui-nav-item a {
color: $main-color !important;
}
.layuimini-menu-left .layui-nav-item a span {
color: $main-color !important;
}
.layuimini-menu-left .layui-nav-item:hover a span {
color: $black-color !important;
}
.layui-layout-admin .layuimini-logo {
background-color: $black-color !important;
}
.layuimini-menu-left .layui-nav-itemed>.layui-nav-child {
background-color: $black-color !important;
}
.layuimini-menu-left .layui-nav-tree .layui-this,
.layuimini-menu-left .layui-nav-tree .layui-this>a,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-this>a,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this,
.layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this a {
background-color: $main-color !important;
color: $black-color !important;
.layui-left-nav {
color: $black-color !important;
}
}
.layui-iconpicker-icon {
border-color: $less-main-color !important;
background-color: $less-main-color !important;
}
.layui-iconpicker .layui-anim {
background-color: $black-color;
}
.layui-iconpicker .layui-iconpicker-list {
background-color: $black-color;
}
.layui-header .layui-nav .layui-nav-child dd.layui-this a,
.layui-header .layui-nav-child dd.layui-this,
.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this a {
background-color: $active-color !important;
}

View File

@@ -0,0 +1,288 @@
& {
color: #222;
background-color: #f0f0f0;
}
.layui-btn {
background: linear-gradient(180deg, #eee 45%, #ddd 0, #bbb);
border: 1.5px solid #888;
border-radius: 3px;
box-shadow: inset 0 -1px 1px hsla(0, 0%, 100%, .8), inset 0 1px 1px #fff;
box-sizing: border-box;
color: #222;
&:hover {
background: linear-gradient(180deg, #e5f4fd 45%, #b3e0f9 0);
border-color: #72a2c5;
}
}
.layui-layer-easy .layui-layer-btn a {
@extend .layui-btn;
color: #222 !important;
height: 26px;
line-height: 26px;
font-size: 12px;
}
.layui-layer-title {
background: linear-gradient(90deg, hsla(0, 0%, 100%, .4), rgba(0, 0, 0, .1), hsla(0, 0%, 100%, .2)), linear-gradient(55deg, transparent, rgba(0, 0, 0, .1) 20%, rgba(0, 0, 0, .1) 50%, transparent 53%), #4580c4 !important;
border: 1px solid rgba(0, 0, 0, .6);
border-radius: 6px 6px 0 0;
// box-shadow : inset 0 0 .5px .5px #fff;
height: 27px;
line-height: 27px;
color: #000 !important;
letter-spacing: 0;
font-size: 12px;
text-shadow: 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff;
padding: 0 80px 0 8px;
border: 0;
box-shadow: inset 0 .5px #fff;
}
.layui-layer-easy .layui-layer-title~.layui-layer-setwin {
height: 27px !important;
top: 1px;
}
.layui-layer-easy .layui-layer-title~.layui-layer-setwin>span {
height: 20px !important;
line-height: 20px !important;
background: linear-gradient(hsla(0, 0%, 100%, .3), hsla(0, 0%, 100%, .3) 45%, rgba(0, 0, 0, .1) 50%, rgba(0, 0, 0, .1) 75%, hsla(0, 0%, 100%, .5)) !important;
border: 0;
border-radius: 0;
border-bottom-right-radius: 0px;
border-right: 1px solid rgba(0, 0, 0, .6);
box-shadow: inset 0 0 0 .5px #eee;
box-sizing: border-box;
padding: 0;
position: relative;
margin-left: 0;
min-width: 26px;
text-align: center;
&:hover {
background: radial-gradient(circle at 50% 100%, #2aceda, transparent 60%), linear-gradient(#a9d2e8 50%, #196c9b 0) !important;
}
&:last-child {
border: 0;
border-bottom-right-radius: 5px;
}
&:first-child {
border-bottom-left-radius: 5px;
}
&.layui-layer-close1 {
background-color: #d04a37 !important;
min-width: 45px !important;
&:hover {
filter: contrast(1.3);
background-image: linear-gradient(hsla(0, 0%, 100%, .3), hsla(0, 0%, 100%, .3) 45%, rgba(0, 0, 0, .1) 50%, rgba(0, 0, 0, .1) 75%, hsla(0, 0%, 100%, .5)) !important;
}
}
}
.layui-layer-dialog {
border-radius: 20px;
}
.layui-layer-iframe {
border-radius: 20px;
}
.layui-layer:not(.layui-layer-loading) {
.layui-layer-content {
background: #f0f0f0;
border: 1px solid rgba(0, 0, 0, .6);
box-shadow: 0 -1px 1px .5px #fff;
margin: 0 6px 0 6px;
padding: 6px;
border-bottom-width: 0;
}
&::before {
background: linear-gradient(transparent 20%, hsla(0, 0%, 100%, .7) 40%, transparent 41%), linear-gradient(90deg, hsla(0, 0%, 100%, .4), rgba(0, 0, 0, .1), hsla(0, 0%, 100%, .2)), linear-gradient(55deg, transparent, rgba(0, 0, 0, .1) 20%, rgba(0, 0, 0, .1) 50%, transparent 53%), #4580c4;
border-radius: 6px;
content: "";
height: 100%;
position: absolute;
width: 100%;
z-index: -1;
}
&.layui-layer-iframe {
&::before {
height: calc(100% + 20px);
}
}
}
.layui-layer-easy .layui-layer-btn {
background: #f0f0f0;
border: 1px solid rgba(0, 0, 0, .6);
box-shadow: 0 0 1px .5px #fff;
margin: 0 6px 6px;
padding: 6px;
border-top: 0;
text-align: right !important;
}
.layui-card {
border: 1px solid rgba(0, 0, 0, .6);
border-radius: 6px;
box-shadow: 0 0 5px 1px rgba(0, 0, 0, .6);
font: 9pt Segoe UI, sans-serif;
position: relative;
z-index: 0;
&::before {
background: linear-gradient(transparent 20%, hsla(0, 0%, 100%, .7) 40%, transparent 41%), linear-gradient(90deg, hsla(0, 0%, 100%, .4), rgba(0, 0, 0, .1), hsla(0, 0%, 100%, .2)), linear-gradient(55deg, transparent, rgba(0, 0, 0, .1) 20%, rgba(0, 0, 0, .1) 50%, transparent 53%), #4580c4;
border-radius: 6px;
content: "";
height: 100%;
position: absolute;
width: 100%;
z-index: -1;
}
.layui-card-header {
align-items: center;
background: linear-gradient(90deg, hsla(0, 0%, 100%, .4), rgba(0, 0, 0, .1), hsla(0, 0%, 100%, .2)), linear-gradient(55deg, transparent, rgba(0, 0, 0, .1) 20%, rgba(0, 0, 0, .1) 50%, transparent 53%), #4580c4;
border-radius: 6px 6px 0 0;
font: 9pt Segoe UI, sans-serif;
justify-content: space-between;
padding: 6px;
border: 0;
box-shadow: inset 0 .5px #fff;
height: 15px;
color: #000;
letter-spacing: 0;
line-height: 15px;
text-shadow: 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff;
}
.layui-card-body {
background: #f0f0f0;
border: 1px solid rgba(0, 0, 0, .6);
box-shadow: 0 0 1px .5px #fff;
margin: 0 6px 6px;
padding: 6px;
}
}
.layui-tab-brief>.layui-tab-title .layui-this {
color: #222;
}
.layuimini-tab .layui-tab-control>li {
height: 27px !important;
line-height: 27px !important;
}
.layui-form-checkbox[lay-skin="primary"] i {
background: #ddd;
border: 1px solid #888;
box-shadow: inset 0 0 0 1.5px #eee, inset 0 2px 4px #888, inset -2px -3px 4px #fff;
box-sizing: border-box;
content: "";
display: inline-block;
height: 13px;
margin-right: 6px;
width: 13px;
color: transparent;
}
.layui-form-checked[lay-skin="primary"] i {
color: #16638f;
border-color: #888;
display: block;
font-weight: 700;
position: absolute;
&::before {
font-size: 14px;
}
}
.layui-form-radio:hover *,
.layui-form-radioed,
.layui-form-radioed>i {
color: #16638f;
}
.layui-input {
background-color: #fff;
border: 1px solid #ccc;
border-top-color: rgb(204, 204, 204);
border-radius: 2px;
border-top-color: #888;
box-sizing: border-box;
font: 9pt Segoe UI, sans-serif;
padding: 3px 4px 5px;
}
.layui-input,
.layui-select,
.layui-textarea {
height: unset
}
.layui-form-label {
padding: 3px 15px;
font: 9pt Segoe UI, sans-serif;
}
.layuimini-form>.layui-form-item .required::after {
top: 2px;
}
.form-search .layui-input-inline input,
.form-search .layui-input-inline select {
height: unset;
line-height: unset;
}
.form-search .layui-form-label {
height: unset;
background: unset;
text-align: right;
border: unset;
}
.layuimini-upload-btn {
.layui-btn {
height: 25px;
line-height: 25px;
}
}

View File

@@ -0,0 +1,95 @@
/*头部右侧背景色 headerRightBg */
.layui-layout-admin .layui-header {
background-color: #ffffff !important;
}
/*头部右侧选中背景色 headerRightBgThis */
.layui-layout-admin .layui-header .layuimini-header-content > ul > .layui-nav-item.layui-this, .layuimini-tool i:hover {
background-color: #e4e4e4 !important;
}
/*头部右侧字体颜色 headerRightColor */
.layui-layout-admin .layui-header .layui-nav .layui-nav-item a {
color: rgba(107, 107, 107, 0.7);
}
/**头部右侧下拉字体颜色 headerRightChildColor */
.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child a {
color: rgba(107, 107, 107, 0.7) !important;
}
/*头部右侧鼠标选中 headerRightColorThis */
.layui-header .layuimini-menu-header-pc.layui-nav .layui-nav-item a:hover, .layui-header .layuimini-header-menu.layuimini-pc-show.layui-nav .layui-this a {
color: #565656 !important;
}
/*头部右侧更多下拉颜色 headerRightNavMore */
.layui-header .layui-nav .layui-nav-more {
border-top-color: rgba(160, 160, 160, 0.7) !important;
}
/*头部右侧更多下拉颜色 headerRightNavMore */
.layui-header .layui-nav .layui-nav-mored, .layui-header .layui-nav-itemed > a .layui-nav-more {
border-color: transparent transparent rgba(160, 160, 160, 0.7) !important;
}
/**头部右侧更多下拉配置色 headerRightNavMoreBg headerRightNavMoreColor */
.layui-header .layui-nav .layui-nav-child dd.layui-this a, .layui-header .layui-nav-child dd.layui-this, .layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this a {
background-color: #1E9FFF !important;
color: #ffffff !important;
}
/*头部缩放按钮样式 headerRightToolColor */
.layui-layout-admin .layui-header .layuimini-tool i {
color: #565656;
}
/*logo背景颜色 headerLogoBg */
.layui-layout-admin .layuimini-logo {
background-color: #192027 !important;
}
/*logo字体颜色 headerLogoColor */
.layui-layout-admin .layuimini-logo h1 {
color: rgb(191, 187, 187);
}
/*左侧菜单更多下拉样式 leftMenuNavMore */
.layuimini-menu-left .layui-nav .layui-nav-more, .layuimini-menu-left-zoom.layui-nav .layui-nav-more {
border-top-color: rgb(191, 187, 187);
}
/*左侧菜单更多下拉样式 leftMenuNavMore */
.layuimini-menu-left .layui-nav .layui-nav-mored, .layuimini-menu-left .layui-nav-itemed > a .layui-nav-more, .layuimini-menu-left-zoom.layui-nav .layui-nav-mored, .layuimini-menu-left-zoom.layui-nav-itemed > a .layui-nav-more {
border-color: transparent transparent rgb(191, 187, 187) !important;
}
/*左侧菜单背景 leftMenuBg */
.layui-side.layui-bg-black, .layui-side.layui-bg-black > .layuimini-menu-left > ul, .layuimini-menu-left-zoom > ul {
background-color: #28333E !important;
}
/*左侧菜单选中背景 leftMenuBgThis */
.layuimini-menu-left .layui-nav-tree .layui-this, .layuimini-menu-left .layui-nav-tree .layui-this > a, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a, .layuimini-menu-left-zoom.layui-nav-tree .layui-this, .layuimini-menu-left-zoom.layui-nav-tree .layui-this > a, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this a {
background-color: #1E9FFF !important
}
/*左侧菜单子菜单背景 leftMenuChildBg */
.layuimini-menu-left .layui-nav-itemed > .layui-nav-child {
background-color: #0c0f13 !important;
}
/*左侧菜单字体颜色 leftMenuColor */
.layuimini-menu-left .layui-nav .layui-nav-item a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a {
color: rgb(191, 187, 187) !important;
}
/*左侧菜单选中字体颜色 leftMenuColorThis */
.layuimini-menu-left .layui-nav .layui-nav-item a:hover, .layuimini-menu-left .layui-nav .layui-this a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a:hover, .layuimini-menu-left-zoom.layui-nav .layui-this a {
color: #ffffff !important;
}
/**tab选项卡选中颜色 tabActiveColor */
.layuimini-tab .layui-tab-title .layui-this .layuimini-tab-active {
background-color: #1e9fff;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
.elem-style-normal {
@import 'normal';
}
.elem-style-demo {
@import 'demo';
}
.elem-style-sicfi {
@import 'sicfi';
}
.elem-style-gtk {
@import 'gtk';
}
.elem-style-nes {
@import 'nes';
}
.elem-style-win7 {
@import 'win7';
}
.elem-style-neomorphic {
@import 'neomorphic';
}
.elem-style-dark {
@import 'dark';
}
@font-face {
font-family: "ark-pixel";
src : url('./nes/ark-pixel-font-12px/ark-pixel-12px-zh_cn.ttf') format('truetype'),
url('./nes/ark-pixel-font-12px/ark-pixel-12px-zh_hk.ttf') format('truetype'),
url('./nes/ark-pixel-font-12px/ark-pixel-12px-zh_tr.ttf') format('truetype'),
url('./nes/ark-pixel-font-12px/ark-pixel-12px-zh_tw.ttf') format('truetype'),
url('./nes/ark-pixel-font-12px/ark-pixel-12px-ja.ttf') format('truetype'),
url('./nes/ark-pixel-font-12px/ark-pixel-12px-ko.ttf') format('truetype'),
url('./nes/ark-pixel-font-12px/ark-pixel-12px-latin.ttf') format('truetype');
}

View File

@@ -0,0 +1,94 @@
Copyright (c) 2021, TakWolf (https://ark-pixel-font.takwolf.com),
with Reserved Font Name 'Ark Pixel'.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More