123 Commits

Author SHA1 Message Date
wolfcode
8a33a4fed3 fix(easy-admin): improve select component rendering and initialization
- Update select component initialization to properly set selected values
- Modify search value comparison to use loose equality for broader compatibility
- Enhance xmSelect rendering with pre-selected values
2025-05-20 10:22:50 +08:00
wolfcode
a4e8a86045 🚀 Layui v2.11.2 2025-05-19 18:30:34 +08:00
wolfcode
61e622d2ad refactor(controller): instantiate model class instead of assigning model name
- Change {{modelFilename}}::class to new {{modelFilename}}() in controller constructor- This modification allows direct access to model properties and methods
2025-05-14 12:32:02 +08:00
wolfcode
1b3265aeb5 feat(menu): add toggle buttons for menu folding and unfolding
- Add a new button for toggling menu folding and unfolding
- Implement functionality to fold and unfold all menu items
- Update button text and icon based on current state (folded/unfolded)
2025-05-13 14:06:20 +08:00
wolfcode
e1c0f6c881 feat(admin): add MIME type for logo upload
- Add image MIME type to the logo upload input
- This change improves file type restriction for logo uploads
2025-05-09 11:37:38 +08:00
wolfcode
3891cf8898 🚀 Layui v2.11.1 2025-05-06 15:59:24 +08:00
wolfcode
4ade618657 fix: correct class selector for tab item in refresh function
- Update the class selector from "layui-tab-item" to "layui-tabs-item" in the refresh function
- This change ensures that the correct tab item is targeted for refreshing
2025-04-30 11:10:29 +08:00
wolfcode
e7253e7de0 fix(layuimini): improve tab context menu positioning and behavior
- Update tab context menu CSS to use fixed positioning with higher z-index
- Modify JavaScript to prevent default context menu and use correct left position
2025-04-28 15:33:50 +08:00
wolfcode
063108a846 feat(easy-admin): dynamic theme color for xmSelect
- Replace static color codes with dynamic theme color retrieval
- Use getComputedStyle to fetch the '--ea8-theme-main-color' variable
- Update xmSelect instances in easy-admin.js and goods.js to use dynamic color
2025-04-22 18:16:26 +08:00
wolfcode
f41943320b Update welcome.html 2025-04-22 16:04:04 +08:00
wolfcode
1f0064743e 🚀 Layui v2.11.0 2025-04-22 15:46:10 +08:00
wolfcode
115573a88c 🚀 Layui v2.11.0 2025-04-22 15:26:25 +08:00
wolfcode
253379f0c6 feat(theme): implement dynamic theme color change
- Add functionality to change the main theme color dynamically
- Update CSS to use a custom property for the main theme color
- Modify JavaScript to set the main theme color based on user preference Adjust form select styles to match the new theme color
2025-04-21 18:03:39 +08:00
wolfcode
9a0ff912b5 feat(mall):新增表单中多选案例 add simulated multi-select feature
- Add simulated multi-select functionality to goods add and edit pages
- Integrate xm-select library for multi-select implementation
- Add random color theme to the multi-select dropdown
- Include predefined options for demonstration purposes
2025-04-18 15:16:06 +08:00
wolfcode
517fd191d3 refactor(curd): improve relation handling and query methods
- Replace with() with withJoin() for more efficient left joins
- Fix foreign key and primary key assignments in relations
- Update index page queries to use relation index method-Modify relation method generation in model to use belongsTo instead of hasOne- Adjust table column definitions for relation fields
2025-04-17 17:25:06 +08:00
wolfcode
d1dfa8b49b Update app.php 2025-04-16 18:40:37 +08:00
wolfcode
c819751a66 chore: update .gitignore files
- Extend and runtime .gitignore: add space after '!' in .gitignore
- Root .gitignore: remove .project file
2025-04-16 18:38:53 +08:00
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
Rodots
34354837d5 fix: 修正命名空间,移除无用类的引用 2024-11-06 14:51:15 +08:00
146 changed files with 8987 additions and 1015 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]

2
.gitignore vendored
View File

@@ -9,4 +9,4 @@ Thumbs.db
/vendor
/.settings
/.buildpath
/.project
/.project

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

@@ -141,7 +141,7 @@ class Index extends AdminController
$old_secret = $row->ga_secret;
$secret = $ga->createSecret(32);
$ga_title = $this->isDemo ? 'EasyAdmin8演示环境' : '可自定义修改显示标题';
$dataUri = $ga->getQRCode($ga_title, $secret)->getDataUri();
$dataUri = $ga->getQRCode($ga_title, $secret);
$this->assign(compact('row', 'dataUri', 'old_secret', 'secret'));
return $this->fetch();
}

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);

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 = new MallGoods();
$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,7 +8,12 @@ use app\common\model\TimeModel;
class SystemAdmin extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
public array $notes = [
'login_type' => [
@@ -17,12 +22,15 @@ class SystemAdmin extends TimeModel
],
];
public function getAuthList()
public static function getAuthIdsAttr($value): array
{
$list = (new SystemAuth())
->where('status', 1)
->column('title', 'id');
return $list;
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 = "withJoin('{$relation}', 'LEFT')";
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']}",
'relationModel' => "{$val['modelFilename']}::class",
'foreignKey' => $val['foreignKey'],
'primaryKey' => $val['primaryKey'],
'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,75 +1374,79 @@ 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']}'}";
}
$indexCols .= $this->formatColsRow("{$templateValue},\r");
}
// 关联表
foreach ($this->relationArray as $table => $tableVal) {
$table = CommonTool::lineToHump($table);
$table = CommonTool::humpToLine($table);
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 {
} else {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}'}";
}
$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 +1571,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 = new {{modelFilename}}();
$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)->{{relationIndexMethod}}->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->belongsTo({{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

@@ -8,7 +8,7 @@
<div class="layui-input-block layuimini-upload">
<input name="head_img" class="layui-input layui-col-xs6" lay-reqtext="请上传用户头像" placeholder="请上传用户头像" value="{$row.head_img|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="head_img" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn" data-upload="head_img" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_head_img" data-upload-select="head_img" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>

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">
@@ -91,8 +91,8 @@
<div class="layuimini-site-mobile"><i class="layui-icon"></i></div>
<div class="layui-body">
<div class="layuimini-tab layui-tab-rollTool layui-tab" lay-filter="layuiminiTab" lay-allowclose="true">
<ul class="layui-tab-title">
<div class="layuimini-tab layui-tabs-rollTool layui-tabs" lay-filter="layuiminiTab" id="layuiminiTab">
<ul class="layui-tabs-header">
<li class="layui-this" id="layuiminiHomeTabId" lay-id=""></li>
</ul>
<div class="layui-tab-control">
@@ -111,8 +111,8 @@
</ul>
</li>
</div>
<div class="layui-tab-content">
<div id="layuiminiHomeTabIframe" class="layui-tab-item layui-show"></div>
<div class="layui-tabs-body">
<div id="layuiminiHomeTabIframe" class="layui-tab-item layui-tabs-item layui-show"></div>
</div>
</div>
</div>

View File

@@ -16,8 +16,8 @@
<span class="layui-badge layui-bg-cyan pull-right ">实时</span>
<div class="panel-content">
<h5>用户统计</h5>
<h1>1234</h1>
<h6>当前分类总记录数</h6>
<h2>1234</h2>
<h6>记录数</h6>
</div>
</div>
</div>
@@ -28,8 +28,8 @@
<span class="layui-badge layui-bg-purple pull-right ">实时</span>
<div class="panel-content">
<h5>商品统计</h5>
<h1>1234</h1>
<h6>当前分类总记录数</h6>
<h2>1234</h2>
<h6>记录数</h6>
</div>
</div>
</div>
@@ -40,8 +40,8 @@
<span class="layui-badge layui-bg-orange pull-right ">实时</span>
<div class="panel-content">
<h5>浏览统计</h5>
<h1>1234</h1>
<h6>当前分类总记录数</h6>
<h2>1234</h2>
<h6>记录数</h6>
</div>
</div>
</div>
@@ -52,8 +52,8 @@
<span class="layui-badge layui-bg-red pull-right ">实时</span>
<div class="panel-content">
<h5>订单统计</h5>
<h1>1234</h1>
<h6>当前分类总记录数</h6>
<h2>1234</h2>
<h6>记录数</h6>
</div>
</div>
</div>
@@ -151,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>
@@ -180,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>
@@ -192,8 +198,8 @@
<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">
本模板基于layui2.9.x以及font-awesome-4.7.0进行实现。
<p>
本模板基于layui2.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>
<hr>

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

@@ -41,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

@@ -13,8 +13,8 @@
<div class="layui-input-block layuimini-upload">
<input name="image" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传分类图片" value="">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="image" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_image" data-upload-select="image" data-upload-number="one" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
<span><a class="layui-btn" data-upload="image" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_image" data-upload-select="image" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
@@ -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

@@ -13,8 +13,8 @@
<div class="layui-input-block layuimini-upload">
<input name="image" class="layui-input layui-col-xs6" lay-verify="required" lay-reqtext="请上传分类图片" placeholder="请上传分类图片" value="{$row.image|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="image" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_image" data-upload-select="image" data-upload-number="one" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
<span><a class="layui-btn" data-upload="image" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_image" data-upload-select="image" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
@@ -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>
@@ -37,8 +46,8 @@
<div class="layui-input-block layuimini-upload">
<input name="logo" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传分类图片" value="">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="logo" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_logo" data-upload-select="logo" data-upload-number="one" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
<span><a class="layui-btn" data-upload="logo" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_logo" data-upload-select="logo" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
@@ -48,8 +57,8 @@
<div class="layui-input-block layuimini-upload">
<input name="images" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传商品图片" value="">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="images" data-upload-number="more" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_images" data-upload-select="images" data-upload-number="more" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
<span><a class="layui-btn" data-upload="images" data-upload-number="more" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_images" data-upload-select="images" data-upload-number="more"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
@@ -78,7 +87,15 @@
<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>
<!-- 文档https://xm-select.com/file/xm-select/v1.2.4/#/basic/use -->
<div class="layui-form-item">
<label class="layui-form-label">模拟多选</label>
<div class="layui-input-block">
<div id="demo1" class="xm-select-demo"></div>
</div>
</div>

View File

@@ -26,19 +26,29 @@
<!-- </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">
<input name="logo" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传分类图片" value="{$row.logo|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="logo" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_logo" data-upload-select="logo" data-upload-number="one" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
<span><a class="layui-btn" data-upload="logo" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_logo" data-upload-select="logo" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
@@ -48,8 +58,8 @@
<div class="layui-input-block layuimini-upload">
<input name="images" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传商品图片" value="{$row.images|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="images" data-upload-number="more" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_images" data-upload-select="images" data-upload-number="more" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
<span><a class="layui-btn" data-upload="images" data-upload-number="more" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_images" data-upload-select="images" data-upload-number="more"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
@@ -78,7 +88,15 @@
<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>
<!-- 文档https://xm-select.com/file/xm-select/v1.2.4/#/basic/use -->
<div class="layui-form-item">
<label class="layui-form-label">模拟多选</label>
<div class="layui-input-block">
<div id="demo1" class="xm-select-demo"></div>
</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

@@ -6,8 +6,8 @@
<div class="layui-input-block layuimini-upload">
<input name="head_img" class="layui-input layui-col-xs6" lay-verify="required" lay-reqtext="请上传用户头像" placeholder="请上传用户头像" value="">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="head_img" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_head_img" data-upload-select="head_img" data-upload-number="one" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
<span><a class="layui-btn" data-upload="head_img" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-icon="image" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_head_img" data-upload-select="head_img" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>

View File

@@ -6,7 +6,7 @@
<div class="layui-input-block layuimini-upload">
<input name="head_img" class="layui-input layui-col-xs6" lay-verify="required" lay-reqtext="请上传用户头像" placeholder="请上传用户头像" value="{$row.head_img|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="head_img" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn" data-upload="head_img" data-upload-number="one" data-upload-exts="png|jpg|ico|jpeg" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_head_img" data-upload-select="head_img" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</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

@@ -13,7 +13,7 @@
<div class="layui-input-block layuimini-upload">
<input name="logo_image" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传LOGO图标" value="{:sysConfig('site','logo_image')}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="logo_image" data-upload-number="one" data-upload-exts="ico|png|jpg|jpeg"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn" data-upload="logo_image" data-upload-number="one" data-upload-exts="ico|png|jpg|jpeg" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_logo_image" data-upload-select="logo_image" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>

View File

@@ -13,7 +13,7 @@
<div class="layui-input-block layuimini-upload">
<input name="site_ico" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传浏览器图标,ico类型" value="{:sysConfig('site','site_ico')}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="site_ico" data-upload-number="one" data-upload-exts="ico"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn" data-upload="site_ico" data-upload-number="one" data-upload-exts="ico" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_site_ico" data-upload-select="site_ico" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
@@ -24,7 +24,7 @@
<div class="layui-input-block layuimini-upload">
<input name="admin_background" class="layui-input layui-col-xs6" placeholder="不填默认#333333" value="{:sysConfig('site','admin_background')}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="admin_background" data-upload-number="one" data-upload-exts="png|jpg|jpeg"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn" data-upload="admin_background" data-upload-number="one" data-upload-exts="png|jpg|jpeg" data-upload-mimetype="image/*"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_admin_background" data-upload-select="admin_background" data-upload-number="one"><i class="fa fa-list"></i> 选择</a></span>
</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

@@ -20,4 +20,5 @@
<button class="layui-btn layui-btn-sm layuimini-btn-primary" data-treetable-refresh><i class="fa fa-refresh"></i></button>
<button class="layui-btn layui-btn-normal layui-btn-sm {if !auth('system.menu/add')}layui-hide{/if}" data-open="system.menu/add" data-title="添加"><i class="fa fa-plus"></i> 添加</button>
<button class="layui-btn layui-btn-sm layui-btn-danger {if !auth('system.menu/delete')}layui-hide{/if}" data-url="system.menu/delete" data-treetable-delete="currentTableRenderId"><i class="fa fa-trash-o"></i> 删除</button>
<button class="layui-btn layui-btn-sm" type="button" data-treetable-arrow data-arrow="up"><i class="fa fa-arrow-up"></i> 一键折叠</button>
</script>

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,25 +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",
"ext-json": "*",
"qiniu/php-sdk": "v7.11.0",
"ext-mysqli": "*",
"ext-pdo": "*",
"qiniu/php-sdk": "^7.11.0",
"wolf-leo/phplogviewer": "^0.11.3",
"wolfcode/authenticator": "^0.0.3"
"wolfcode/authenticator": "^0.0.6",
"wolfcode/rate-limiting": "^0.1.0",
"wolfcode/php-ai": "^0.1.2",
"ext-json": "*",
"ext-mysqli": "*",
"ext-pdo": "*"
},
"require-dev": {
"symfony/var-dumper": ">=4.2",

View File

@@ -108,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
@@ -235,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`
@@ -571,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',
];

2
extend/.gitignore vendored
View File

@@ -1,2 +1,2 @@
*
!.gitignore
!.gitignore

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,27 @@
@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;
:root {
--ea8-theme-main-color: #16b777;
}
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 +64,8 @@ html.dark, body {
}
.hr-line {
color: #fff;
height: 1px;
margin: 30px 0;
background-color: #fff;
border-top: 1px dashed #e7eaec;
}
@@ -75,8 +79,8 @@ html.dark, body {
/**重写layui表格自适应*/
.layuimini-container .layui-table-cell {
height: 100%;
max-width: 100%;
height: 50px;
line-height: 42px;
}
/**数据表格-搜索表单样式*/
@@ -84,7 +88,6 @@ html.dark, body {
margin: 0;
border: 1px solid #e6e6e6;
padding: 10px 20px 5px 20px;
color: #6b6b6b;
}
/**数据表格-搜索表单样式*/
@@ -134,10 +137,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 +275,6 @@ table样式
.layuimini-container .layui-table-box .layui-table-header th {
font-weight: bold !important;
color: #565656 !important;
}
/**
@@ -299,9 +308,17 @@ table样式
}
.layui-form-select dl {
border: 1px #16b777 solid;
border: 1px var(--ea8-theme-main-color) solid;
border-top: none;
z-index: 99999;
padding: 0;
border-radius: 0;
}
.layui-form-select dl dd.layui-this {
background-color: var(--ea8-theme-main-color);
border-top: none;
color: #ffffff;
}
.form-search .layui-form-select dl {
@@ -340,7 +357,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 +380,6 @@ table样式
.layui-layer-easy .layui-layer-btn {
text-align: center !important;
padding: 10px !important;
background: #ecf0f1;
overflow: hidden;
}
@@ -512,4 +528,13 @@ table样式
.wangEditor_div {
z-index: 99999;
border: 1px solid var(--w-e-textarea-slight-border-color);
}
.layui-input:focus, .layui-textarea:focus {
border-color: var(--ea8-theme-main-color) !important;
}
.layui-tabs-item {
height: 100%;
}

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;
}

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