Compare commits

...

136 Commits
v6.1.1 ... v6

Author SHA1 Message Date
邹景立
d31798def8 fix: 同步更新代码 2025-03-27 23:06:31 +08:00
邹景立
2b64663d98 fix: 降级ThinkORM版本,解决部分Composer源无法正常安装的问题 2025-03-27 23:06:18 +08:00
邹景立
9b35f833bb fix: 同步更新插件代码,完整 Model IDE 注释 2025-03-01 22:58:28 +08:00
邹景立
0fe942958c fix: 增加自定义系统事件图标,优化系统菜单展示 2025-02-15 19:54:32 +08:00
邹景立
7855390e80 fix: 更新 layui 版本到 2.9.22 2025-02-15 19:54:08 +08:00
邹景立
9014439e5b fix: 同步更新 layui 版本为 2.9.21 2025-01-13 21:12:54 +08:00
邹景立
fc41382355 修复用户资料编辑及系统配置授权管理 2024-12-30 21:37:39 +08:00
邹景立
081e14bcc5 fix: 同步更新数据库默认配置文件 2024-11-27 16:48:18 +08:00
邹景立
4a376d47fd fix: 同步更新 layui 版本为 2.9.20 2024-11-27 16:47:52 +08:00
邹景立
06f8a5af31 fix: 更新数据库配置说明 2024-11-20 21:10:08 +08:00
邹景立
db4b4d0a35 fix: 更新安装脚本描述 2024-11-20 21:07:44 +08:00
邹景立
134292175c fix: 更新默认数据库配置文件 2024-11-20 20:10:19 +08:00
邹景立
79c5ced555 fix: 初始化项目 .env 配置案例 2024-10-17 11:25:39 +08:00
邹景立
271f7481da fix: 更新文件目录计算 2024-10-17 11:25:17 +08:00
邹景立
34fbc008a5 fix: 更新 layui 版本到最新 2024-10-17 11:24:29 +08:00
邹景立
333835cb80 fix: 同步更新 Static 插件,更新 layui 版本到 2.9.17 2024-10-10 16:04:58 +08:00
邹景立
9b7393037a fix: 修改项目描述,增加 Gitcode 仓库地址 2024-09-20 14:38:38 +08:00
邹景立
784affead0 同步更新代码 2024-09-19 13:20:43 +08:00
邹景立
1ad7dc1f56 fix: 更新 layui 版本到 2.9.15 2024-08-19 23:37:06 +08:00
邹景立
45786f4212 fix: 同步更新插件 2024-08-15 01:22:54 +08:00
邹景立
a10f663ca1 fix: 同步更新插件代码 2024-08-09 00:17:39 +08:00
邹景立
a55067fd8f fix: 同步增加 env 配置支持 2024-08-08 23:47:39 +08:00
邹景立
82d71bb1ff fix: 去除不需要的脚本文件 2024-08-08 23:42:38 +08:00
邹景立
6b2fe21efb fix: 更新项目描述 2024-08-08 23:39:43 +08:00
邹景立
327cffbe64 fix: 同步更新常用插件代码 2024-08-08 23:36:47 +08:00
邹景立
ff08376d9a fix: 更新 Githhub Actions 配置脚本 2024-08-08 23:36:25 +08:00
邹景立
008d30b7aa
Create split.yml 2024-08-02 00:39:54 +08:00
邹景立
e4f8179de7
Create release.yml 2024-08-02 00:38:36 +08:00
邹景立
2e65d799ab 增加 WangEditor 支持 2024-07-26 00:05:28 +08:00
邹景立
e2d44537ab 更新 layui 版本 2024-07-24 01:14:19 +08:00
邹景立
0052d8ad28 更新登录统计 2024-07-06 10:01:22 +08:00
邹景立
217445d3f0 升级插件代码 2024-07-02 11:45:37 +08:00
邹景立
e654ebdaf9 优化一处理表单结构 2024-05-31 19:03:23 +08:00
邹景立
73c2b358fb fix #I9TJ1B 修复文件上传图片安全检查 2024-05-31 19:02:57 +08:00
邹景立
ae5c6e7e22 更新文档链接 2024-05-24 11:56:05 +08:00
邹景立
a24cc46fbb 同步升级 Layui 版本 2.9.10 2024-05-21 21:35:19 +08:00
邹景立
ebcbaecc3b 同步升级ThinkAdmin生态代码 2024-05-11 18:16:21 +08:00
邹景立
52e69529b4 更新后台样式库 2024-04-13 10:23:54 +08:00
邹景立
8c13facb3b 同步更新插件代码 2024-04-06 17:51:18 +08:00
邹景立
53f80d325b 回退 Vue 版本 2024-03-09 22:04:50 +08:00
邹景立
966c5a84c3 升级插件 Static 版本 2024-03-08 19:52:14 +08:00
邹景立
1d528383ed 同步更新 Admin 插件 2024-03-08 19:31:57 +08:00
邹景立
63afce68e8 升级 Static 插件文件 2024-02-29 16:58:28 +08:00
邹景立
34549e1fee 修改安全反馈 2024-02-27 11:02:47 +08:00
邹景立
74031a4783 更新配置文件 2024-02-27 10:56:44 +08:00
邹景立
e3b52d3599 更新协议内容 2024-02-27 10:51:38 +08:00
邹景立
02623f2489 同步更新代码 2024-01-29 21:34:47 +08:00
邹景立
8f8f9867c8 同步更新组件 2024-01-16 11:22:30 +08:00
邹景立
761bd06994 去非必需的文件 2023-12-31 21:07:11 +08:00
邹景立
cd7905eb06 同步更新代码 2023-12-31 21:06:15 +08:00
邹景立
b03aaf6276 同步更新代码 2023-12-25 19:23:45 +08:00
邹景立
2aeb944c94 更新样式 2023-12-22 23:20:43 +08:00
邹景立
2d41dfd996 同步更新 2023-12-22 23:16:37 +08:00
邹景立
2b6c80b63c 修改 Excel.js 数据导出参数 2023-12-10 08:50:46 +08:00
邹景立
85cd63df6f 更新layui版本 2023-12-04 22:57:36 +08:00
邹景立
99e76768b9 同步更新 2023-12-04 22:17:55 +08:00
邹景立
ad28cd3629
Merge pull request #281 from wzy184249652/bugfix/upload-error
解决由于普通上传和秒传返回格式不统一导致的初次上传必然失败的bug
2023-11-24 22:45:25 +08:00
yasuo
bb3a90d28a 解决由于普通上传和秒传返回格式不统一导致的初次上传必然失败的bug 2023-11-16 11:57:54 +08:00
邹景立
c6ffb56e9a 同步更新代码 2023-11-13 17:09:41 +08:00
邹景立
5f130765ba 修改安装链接 2023-10-10 10:43:50 +08:00
邹景立
30bf01a44a 同步更新代码 2023-10-02 18:18:04 +08:00
邹景立
ae2bd376ea 同步更新代码 2023-09-22 18:03:12 +08:00
邹景立
5a1c66e624 Update database.php 2023-09-14 21:10:41 +08:00
邹景立
2e24e77995 同步更新代码 2023-09-14 21:10:10 +08:00
邹景立
8d3db1a6ff 同步更新代码 2023-08-24 23:32:41 +08:00
邹景立
969b45e69f 更新代码 2023-08-16 00:03:14 +08:00
邹景立
c385201fdf 同步更新 2023-08-08 19:37:06 +08:00
邹景立
9d231c7a53 同步更新版本 2023-07-27 02:25:04 +08:00
邹景立
ea1444717b 修改项目描述 2023-07-08 20:35:09 +08:00
邹景立
412912de2f 同步更新插件代码 2023-07-08 20:29:45 +08:00
邹景立
a275e972bb 去除数据库脚本 2023-07-08 20:29:18 +08:00
邹景立
5606df8cd9 修改描述 2023-07-01 20:59:51 +08:00
邹景立
873de9d3c0 Update readme.md 2023-06-29 19:13:24 +08:00
邹景立
1a51281a3c 同步更新微信模块 2023-06-28 20:24:11 +08:00
邹景立
0a47af5138 Delete 20221013031928_install_admin20230621.php 2023-06-25 21:46:02 +08:00
邹景立
bc18754c4a Update .gitignore 2023-06-25 21:44:48 +08:00
邹景立
0ae00022b8 Update readme.md 2023-06-25 21:40:46 +08:00
邹景立
e640723674 Update readme.md 2023-06-25 21:40:08 +08:00
邹景立
a344db0b14 去除 data 模块代码 2023-06-25 21:35:10 +08:00
邹景立
ffca5f8524 同步更新代码 2023-06-25 21:31:03 +08:00
邹景立
7da0b76705 同步更新插件代码 2023-06-16 16:33:31 +08:00
Anyon
58465920ee
!40 修复-1 month出现当月1号的问题
Merge pull request !40 from stranger/master
2023-06-01 03:03:56 +00:00
kang
1d416c44b0 修复-1 month出现当月1号的问题 2023-05-31 14:34:32 +08:00
邹景立
63b31b933e Update readme.md 2023-05-20 21:36:33 +08:00
邹景立
35bd446092 修改项目描述 2023-05-17 20:48:59 +08:00
邹景立
07bd186995 同步更新升级 2023-05-09 13:29:37 +08:00
邹景立
a731e3e45b 同步更新插件代码 2023-05-05 22:35:47 +08:00
邹景立
87c63b21e5 同步更新插件代码 2023-04-27 19:35:51 +08:00
邹景立
5ca548c2d4 同步更新插件代码 2023-04-24 16:35:30 +08:00
邹景立
6f704873ee 同步更新插件代码 2023-04-17 21:38:33 +08:00
邹景立
5772c56005 Update Index.php 2023-04-11 11:46:49 +08:00
邹景立
986b3c86ef 修改订单数量处理 2023-04-11 11:22:27 +08:00
邹景立
73409a52ff 同步插件代码 2023-04-11 10:57:55 +08:00
邹景立
d2cfa2575e 更新同步插件代码 2023-04-09 18:02:37 +08:00
邹景立
2e15451544 增加 Alist 存储配置 2023-04-03 20:01:51 +08:00
邹景立
06679ba6f5 同步插件代码 2023-04-02 15:04:57 +08:00
邹景立
79c2b7c494 修改屏蔽插件脚本 2023-04-02 14:17:08 +08:00
邹景立
9d04f43ec2 升级同步插件 2023-04-01 20:58:54 +08:00
邹景立
1a1f0e86ad Update composer.json 2023-03-26 22:02:48 +08:00
邹景立
15d33afdd2 同步插件代码 2023-03-25 12:57:39 +08:00
邹景立
a6afa20850 Update readme.md 2023-03-15 09:56:47 +08:00
邹景立
da79e89a9e 同步更新代码 2023-03-12 23:36:32 +08:00
邹景立
24bc869f07 更新js代码 2023-03-10 21:19:02 +08:00
邹景立
c36ae62bf4 更新同步插件代码 2023-03-10 16:42:42 +08:00
邹景立
161b05ce61 Update readme.md 2023-03-10 15:53:19 +08:00
邹景立
3744e20a28 Update readme.md 2023-03-10 15:47:41 +08:00
邹景立
707f29f21e Update readme.md 2023-03-10 15:46:25 +08:00
邹景立
57f22864db Update readme.md 2023-03-10 15:43:59 +08:00
邹景立
e21245c067 修改项目描述 2023-03-10 15:42:24 +08:00
邹景立
03ee3e3486 同步插件代码 2023-03-02 21:45:45 +08:00
邹景立
b5064489b1 同步插件代码 2023-02-28 11:55:35 +08:00
邹景立
29835b8884 同步更新插件代码版本 2023-02-28 11:55:35 +08:00
邹景立
4340764b60 更新弹层关闭机制 2023-02-28 11:55:35 +08:00
邹景立
64bb953268 修改项目描述 2023-02-28 11:55:35 +08:00
邹景立
baad5c717c 改用百度行政区域数据 2023-02-28 11:55:34 +08:00
邹景立
af60726538 同步最新插件代码 2023-02-28 11:55:34 +08:00
邹景立
b828e70c9e 同步 Static 插件代码 2023-02-28 11:55:34 +08:00
邹景立
eb81aff262 Update .gitattributes 2023-02-28 11:55:34 +08:00
邹景立
705f7559e8 同步 ThinkPlugsStatic 插件资源文件 2023-02-28 11:55:34 +08:00
邹景立
47fe336024 同步更新 2023-02-28 11:55:34 +08:00
邹景立
b8c7cccb39 去除本地调试配置 2023-02-28 11:55:34 +08:00
邹景立
a9d4ec2390 删除数据库脚本,项目本地不保存 2023-02-18 18:31:52 +08:00
邹景立
a9c5cf9414 同时更新 2023-02-18 18:29:06 +08:00
邹景立
7621cc87a2 同步更新full代码 2023-02-18 10:06:34 +08:00
邹景立
b359a79472 增加 data 模块数据库脚本 2023-02-18 09:42:08 +08:00
邹景立
2db8906c86 同步代码 2023-02-14 19:10:07 +08:00
邹景立
14db72afd4 同步 static 插件 2023-02-14 15:01:53 +08:00
邹景立
02538ca533 更新代码 2023-02-14 12:32:51 +08:00
邹景立
f8b55cdc68 同步更新代码 2023-02-09 14:51:41 +08:00
邹景立
cd2482e4e3 升级系统版本为v6.1兼容 2023-02-08 20:05:47 +08:00
邹景立
2aed16b0cf Update readme.md 2023-01-12 15:31:47 +08:00
邹景立
5b2b1c7aff Update readme.md 2023-01-12 15:19:05 +08:00
邹景立
093ce6a470 Update readme.md 2023-01-12 15:16:34 +08:00
邹景立
89b04416cf 更新项目描述 2023-01-12 15:13:55 +08:00
Anyon
648f7b7b1c
修改文件上传
Signed-off-by: Anyon <zoujingli@qq.com>
2022-11-23 14:06:06 +00:00
Anyon
e9a80653da
去除其他文件选择器
Signed-off-by: Anyon <zoujingli@qq.com>
2022-11-23 14:05:18 +00:00
476 changed files with 31808 additions and 31623 deletions

23
.env.example Normal file
View File

@ -0,0 +1,23 @@
# 数据配置
DB_TYPE=sqlite
DB_MYSQL_HOST=thinkadmin.top
DB_MYSQL_PORT=3306
DB_MYSQL_PREFIX=
DB_MYSQL_USERNAME=root
DB_MYSQL_DATABASE=admin
DB_MYSQL_CHARSET=utf8mb4
DB_MYSQL_PASSWORD=
# 缓存配置
CACHE_TYPE=file
CACHE_REDIS_HOST=127.0.0.1
CACHE_REDIS_PORT=6379
CACHE_REDIS_SELECT=
CACHE_REDIS_PASSWORD=
# 会话配置
SESSION_TYPE=file
SESSION_NAME=ssid
SESSION_STORE=
SESSION_EXPIRE=7200
SESSION_PREFIX=

65
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,65 @@
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Create Release
permissions: write-all
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: npm install -g gen-git-log
- name: Find Last Tag
id: last_tag
run: |
# 获取所有标签,按版本排序(降序)
Tags=$(git tag --list --sort=-version:refname)
# 获取最新的标签(即列表中的第一个)
LATEST_TAG=$(echo "$Tags" | awk 'NR==1 {print $1; exit}')
# 获取倒数第二个标签(如果存在)
if [[ -n "$Tags" ]]; then
# 使用 tail 获取除了最后一个标签之外的所有标签,然后用 head 获取第一个
SECOND_LATEST_TAG=$(echo "$Tags" | tail -n +2 | head -n 1)
else
SECOND_LATEST_TAG=""
fi
# 设置输出变量
echo "::set-output name=tag_last::${LATEST_TAG:-v1.0.0}"
echo "::set-output name=tag_second::${SECOND_LATEST_TAG:-v1.0.0}"
- name: Generate Release Notes
run: |
rm -rf log
newTag=${{ steps.last_tag.outputs.tag_last }}
git-log -m tag -f -S ${{ steps.last_tag.outputs.tag_second }} -v ${newTag#v}
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.last_tag.outputs.tag_last }}
release_name: Release ${{ steps.last_tag.outputs.tag_last }}
body_path: log/${{steps.last_tag.outputs.tag_last}}.md
draft: false
prerelease: false

40
.gitignore vendored
View File

@ -2,17 +2,33 @@
.git
.svn
.idea
.fleet
.vscode
*.log
.DS_Store
vendor
runtime
safefile
nbproject
composer.lock
public/upload
public/.user.ini
public/favicon.ico
public/static/theme/css/_*.css*
public/static/theme/css/node_modules
public/static/theme/css/package-lock.json
/vendor
/runtime
/safefile
/nbproject
/composer.lock
!composer.json
/404.html
/.user.ini
/index.html
/public/upload
/public/404.html
/public/.user.ini
/public/index.html
/public/favicon.ico
/database/sqlite.db
/public/static/theme/css/_*.css*
/public/static/theme/css/node_modules
/public/static/theme/css/package-lock.json
### 屏蔽数据库脚本
/database/migrations/*_install_*.php
/database/migrations/*_upgrade_*.php
!database/migrations/*_install*_table.php
!database/migrations/*_install_package.php

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +0,0 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 尝试创建微信自动回复数据表
CREATE TABLE IF NOT EXISTS `wechat_auto`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '类型(text,image,news)',
`time` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '延迟时间',
`code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '消息编号',
`appid` char(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '公众号APPID',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '文本内容',
`image_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '图片链接',
`voice_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '语音链接',
`music_title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '音乐标题',
`music_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '音乐链接',
`music_image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '缩略图片',
`music_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '音乐描述',
`video_title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '视频标题',
`video_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '视频URL',
`video_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '视频描述',
`news_id` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '图文ID',
`status` tinyint(1) UNSIGNED NULL DEFAULT 1 COMMENT '状态(0禁用,1启用)',
`create_by` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '创建人',
`create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_wechat_auto_type` (`type`) USING BTREE,
INDEX `idx_wechat_auto_keys` (`time`) USING BTREE,
INDEX `idx_wechat_auto_appid` (`appid`) USING BTREE,
INDEX `idx_wechat_auto_code` (`code`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 1
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '微信-回复'
ROW_FORMAT = COMPACT;
-- 调整字段名称长度
ALTER TABLE `system_queue`
MODIFY COLUMN `title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '任务名称' AFTER `code`;
-- 尝试创建数据字典数据表
CREATE TABLE IF NOT EXISTS `system_base`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '数据类型',
`code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '数据代码',
`name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '数据名称',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '数据内容',
`sort` bigint(20) NULL DEFAULT 0 COMMENT '排序权重',
`status` tinyint(1) NULL DEFAULT 1 COMMENT '数据状态(0禁用,1启动)',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '删除状态(0正常,1已删)',
`deleted_at` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '删除时间',
`deleted_by` bigint(20) NULL DEFAULT 0 COMMENT '删除用户',
`create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_system_base_type` (`type`) USING BTREE,
INDEX `idx_system_base_code` (`code`) USING BTREE,
INDEX `idx_system_base_name` (`name`(191)) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 1
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '系统-字典'
ROW_FORMAT = COMPACT;
-- 权限表增加身份权限字段
ALTER TABLE `system_user`
ADD COLUMN `usertype` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '用户类型' AFTER `id`;
-- 尝试创建系统文件数据表
CREATE TABLE IF NOT EXISTS `system_file` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '上传类型',
`hash` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '文件哈希',
`name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '文件名称',
`xext` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '文件后缀',
`xurl` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '访问链接',
`xkey` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '文件路径',
`mime` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '文件类型',
`size` bigint(20) NULL DEFAULT 0 COMMENT '文件大小',
`uuid` bigint(20) NULL DEFAULT 0 COMMENT '用户编号',
`isfast` tinyint(1) NULL DEFAULT 0 COMMENT '是否秒传',
`issafe` tinyint(1) NULL DEFAULT 0 COMMENT '安全模式',
`status` tinyint(1) NULL DEFAULT 1 COMMENT '上传状态(1悬空,2落地)',
`create_at` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_at` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_system_file_type`(`type`) USING BTREE,
INDEX `idx_system_file_hash`(`hash`) USING BTREE,
INDEX `idx_system_file_uuid`(`uuid`) USING BTREE,
INDEX `idx_system_file_xext`(`xext`) USING BTREE,
INDEX `idx_system_file_status`(`status`) USING BTREE,
INDEX `idx_system_file_issafe`(`issafe`) USING BTREE,
INDEX `idx_system_file_isfast`(`isfast`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统-文件' ROW_FORMAT = COMPACT;
SET FOREIGN_KEY_CHECKS = 1;

69
app/admin/Service.php Normal file
View File

@ -0,0 +1,69 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin;
use think\admin\Plugin;
/**
* 插件服务注册
* @class Service
* @package app\admin
*/
class Service extends Plugin
{
/**
* 定义插件名称
* @var string
*/
protected $appName = '系统管理';
/**
* 定义安装包名
* @var string
*/
protected $package = 'zoujingli/think-plugs-admin';
/**
* 定义插件中心菜单
* @return array
*/
public static function menu(): array
{
return [
[
'name' => '系统配置',
'subs' => [
['name' => '系统参数配置', 'icon' => 'layui-icon layui-icon-set', 'node' => 'admin/config/index'],
['name' => '系统任务管理', 'icon' => 'layui-icon layui-icon-log', 'node' => 'admin/queue/index'],
['name' => '系统日志管理', 'icon' => 'layui-icon layui-icon-form', 'node' => 'admin/oplog/index'],
['name' => '数据字典管理', 'icon' => 'layui-icon layui-icon-code-circle', 'node' => 'admin/base/index'],
['name' => '系统文件管理', 'icon' => 'layui-icon layui-icon-carousel', 'node' => 'admin/file/index'],
['name' => '系统菜单管理', 'icon' => 'layui-icon layui-icon-layouts', 'node' => 'admin/menu/index'],
],
],
[
'name' => '权限管理',
'subs' => [
['name' => '系统权限管理', 'icon' => 'layui-icon layui-icon-vercode', 'node' => 'admin/auth/index'],
['name' => '系统用户管理', 'icon' => 'layui-icon layui-icon-username', 'node' => 'admin/user/index'],
],
],
];
}
}

View File

@ -1,30 +1,33 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemAuth;
use think\admin\model\SystemNode;
use think\admin\Plugin;
use think\admin\service\AdminService;
/**
* 系统权限管理
* Class Auth
* @class Auth
* @package app\admin\controller
*/
class Auth extends Controller
@ -41,29 +44,11 @@ class Auth extends Controller
{
SystemAuth::mQuery()->layTable(function () {
$this->title = '系统权限管理';
}, function (QueryHelper $query) {
$query->dateBetween('create_at')->like('title,desc')->equal('status,utype');
}, static function (QueryHelper $query) {
$query->like('title,desc')->equal('status,utype')->dateBetween('create_at');
});
}
/**
* 添加系统权限
* @auth true
*/
public function add()
{
SystemAuth::mForm('form');
}
/**
* 编辑系统权限
* @auth true
*/
public function edit()
{
SystemAuth::mForm('form');
}
/**
* 修改权限状态
* @auth true
@ -85,40 +70,65 @@ class Auth extends Controller
SystemAuth::mDelete();
}
/**
* 权限配置节点
* 添加系统权限
* @auth true
* @throws \ReflectionException
*/
public function apply()
public function add()
{
$map = $this->_vali(['auth.require#id' => '权限ID不能为空']);
if (input('action') === 'get') {
if ($this->app->isDebug()) AdminService::clearCache();
$nodes = SystemNode::mk()->where($map)->column('node');
$this->success('获取权限节点成功!', AdminService::getTree($nodes));
} elseif (input('action') === 'save') {
[$post, $data] = [$this->request->post(), []];
foreach ($post['nodes'] ?? [] as $node) {
$data[] = ['auth' => $map['auth'], 'node' => $node];
}
SystemNode::mk()->where($map)->delete();
SystemNode::mk()->insertAll($data);
sysoplog('系统权限管理', "配置系统权限[{$map['auth']}]授权成功");
$this->success('访问权限修改成功!', 'javascript:history.back()');
} else {
SystemAuth::mForm('apply');
}
SystemAuth::mForm('form');
}
/**
* 编辑系统权限
* @auth true
*/
public function edit()
{
SystemAuth::mForm('form');
}
/**
* 表单后置数据处理
* @param array $data
*/
protected function _apply_form_filter(array &$data)
protected function _form_filter(array $data)
{
if ($this->request->isGet()) {
$this->title = "编辑【{$data['title']}】授权";
$this->title = empty($data['title']) ? "添加访问授权" : "编辑【{$data['title']}】授权";
} elseif ($this->request->post('action') === 'json') {
if ($this->app->isDebug()) AdminService::clear();
$ztree = AdminService::getTree(empty($data['id']) ? [] : SystemNode::mk()->where(['auth' => $data['id']])->column('node'));
usort($ztree, static function ($a, $b) {
if (explode('-', $a['node'])[0] !== explode('-', $b['node'])[0]) {
if (stripos($a['node'], 'plugin-') === 0) return 1;
}
return $a['node'] === $b['node'] ? 0 : ($a['node'] > $b['node'] ? 1 : -1);
});
[$ps, $cs] = [Plugin::get(), (array)$this->app->config->get('app.app_names', [])];
foreach ($ztree as &$n) $n['title'] = lang($cs[$n['node']] ?? (($ps[$n['node']] ?? [])['name'] ?? $n['title']));
$this->success('获取权限节点成功!', $ztree);
} elseif (empty($data['nodes'])) {
$this->error('未配置功能节点!');
}
}
/**
* 节点更新处理
* @param boolean $state
* @param array $post
* @return void
*/
protected function _form_result(bool $state, array $post)
{
if ($state && $this->request->post('action') === 'save') {
[$map, $data] = [['auth' => $post['id']], []];
foreach ($post['nodes'] ?? [] as $node) $data[] = $map + ['node' => $node];
SystemNode::mk()->where($map)->delete();
count($data) > 0 && SystemNode::mk()->insertAll($data);
sysoplog('系统权限管理', "配置系统权限[{$map['auth']}]授权成功");
$this->success('权限修改成功!', 'javascript:history.back()');
}
}
}

View File

@ -1,19 +1,21 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
@ -22,7 +24,7 @@ use think\admin\model\SystemBase;
/**
* 数据字典管理
* Class Base
* @class Base
* @package app\admin\controller
*/
class Base extends Controller
@ -41,7 +43,7 @@ class Base extends Controller
$this->title = '数据字典管理';
$this->types = SystemBase::types();
$this->type = $this->get['type'] ?? ($this->types[0] ?? '-');
}, function (QueryHelper $query) {
}, static function (QueryHelper $query) {
$query->where(['deleted' => 0])->equal('type');
$query->like('code,name,status')->dateBetween('create_at');
});
@ -74,16 +76,16 @@ class Base extends Controller
{
if ($this->request->isGet()) {
$this->types = SystemBase::types();
$this->types[] = '--- 新增类型 ---';
$this->type = input('get.type') ?: ($this->types[0] ?? '-');
$this->types[] = '--- ' . lang('新增类型') . ' ---';
$this->type = $this->get['type'] ?? ($this->types[0] ?? '-');
} else {
$map = [];
$map[] = ['deleted', '=', 0];
$map[] = ['code', '=', $data['code']];
$map[] = ['type', '=', $data['type']];
if (isset($data['id'])) $map[] = ['id', '<>', $data['id']];
$map[] = ['id', '<>', $data['id'] ?? 0];
if (SystemBase::mk()->where($map)->count() > 0) {
$this->error("同类型的数据编码已经存在!");
$this->error("数据编码已经存在!");
}
}
}

View File

@ -1,32 +1,37 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\Plugin;
use think\admin\service\AdminService;
use think\admin\service\ModuleService;
use think\admin\service\RuntimeService;
use think\admin\service\SystemService;
use think\admin\Storage;
use think\admin\storage\AliossStorage;
use think\admin\storage\QiniuStorage;
use think\admin\storage\TxcosStorage;
/**
* 系统参数配置
* Class Config
* @class Config
* @package app\admin\controller
*/
class Config extends Controller
@ -52,21 +57,30 @@ class Config extends Controller
public function index()
{
$this->title = '系统参数配置';
$this->super = AdminService::isSuper();
$this->version = ModuleService::getVersion();
$this->files = Storage::types();
$this->plugins = Plugin::get(null, true);
$this->issuper = AdminService::isSuper();
$this->systemid = ModuleService::getRunVar('uni');
$this->framework = ModuleService::getLibrarys('topthink/framework');
$this->thinkadmin = ModuleService::getLibrarys('zoujingli/think-library');
if (AdminService::isSuper() && $this->app->session->get('user.password') === md5('admin')) {
$url = url('admin/index/pass', ['id' => AdminService::getUserId()]);
$this->showErrorMessage = lang("超级管理员账号的密码未修改,建议立即<a data-modal='%s'>修改密码</a>", [$url]);
}
uasort($this->plugins, static function ($a, $b) {
if ($a['space'] === $b['space']) return 0;
return $a['space'] > $b['space'] ? 1 : -1;
});
$this->fetch();
}
/**
* 修改系统参数
* @auth true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
public function system()
{
$this->_applyFormToken();
if ($this->request->isGet()) {
$this->title = '修改系统参数';
$this->themes = static::themes;
@ -76,32 +90,32 @@ class Config extends Controller
// 修改网站后台入口路径
if (!empty($post['xpath'])) {
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $post['xpath'])) {
$this->error('后台入口名称需要是由英文字母开头');
$this->error('后台入口格式错误');
}
if ($post['xpath'] !== 'admin' && file_exists($this->app->getBasePath() . $post['xpath'])) {
$this->error("后台入口名称{$post['xpath']}已经存在应用!");
if ($post['xpath'] !== 'admin') {
if (is_dir(syspath("app/{$post['xpath']}")) || !empty(Plugin::get($post['xpath']))) {
$this->error(lang('已存在 %s 应用!', [$post['xpath']]));
}
}
SystemService::setRuntime(null, [$post['xpath'] => 'admin']);
RuntimeService::set(null, [$post['xpath'] => 'admin']);
}
// 修改网站 ICON 图标,替换 public/favicon.ico
if (preg_match('#^https?://#', $icon = $post['site_icon'] ?? '')) try {
SystemService::setFavicon($icon);
if (preg_match('#^https?://#', $post['site_icon'] ?? '')) try {
SystemService::setFavicon($post['site_icon'] ?? '');
} catch (\Exception $exception) {
trace_file($exception);
}
// 数据数据到系统配置表
foreach ($post as $k => $v) sysconf($k, $v);
sysoplog('系统配置管理', "修改系统参数成功");
$this->success('修改系统参数成功!', admuri('admin/config/index'));
$this->success('据保存成功!', admuri('admin/config/index'));
}
}
/**
* 修改文件存储
* @auth true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
public function storage()
{

View File

@ -1,19 +1,21 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
@ -24,11 +26,26 @@ use think\admin\Storage;
/**
* 系统文件管理
* Class File
* @class File
* @package app\admin\controller
*/
class File extends Controller
{
/**
* 存储类型
* @var array
*/
protected $types;
/**
* 控制器初始化
* @return void
*/
protected function initialize()
{
$this->types = Storage::types();
}
/**
* 系统文件管理
* @auth true
@ -39,11 +56,10 @@ class File extends Controller
*/
public function index()
{
$this->types = Storage::types();
SystemFile::mQuery()->layTable(function () {
$this->title = '系统文件管理';
$this->xexts = SystemFile::mk()->distinct()->column('xext');
}, function (QueryHelper $query) {
}, static function (QueryHelper $query) {
$query->like('name,hash,xext')->equal('type')->dateBetween('create_at');
$query->where(['issafe' => 0, 'status' => 2, 'uuid' => AdminService::getUserId()]);
});
@ -61,6 +77,16 @@ class File extends Controller
}
}
/**
* 编辑系统文件
* @auth true
* @return void
*/
public function edit()
{
SystemFile::mForm('form');
}
/**
* 删除系统文件
* @auth true
@ -68,21 +94,23 @@ class File extends Controller
*/
public function remove()
{
SystemFile::mDelete();
if (!AdminService::isSuper()) {
$where = ['uuid' => AdminService::getUserId()];
}
SystemFile::mDelete('', $where ?? []);
}
/**
* 清理重复文件
* @auth true
* @return void
* @throws \think\db\exception\DbException
*/
public function distinct()
{
$map = ['uuid' => AdminService::getUserId()];
$db1 = SystemFile::mk()->fieldRaw('max(id) id')->where($map)->group('type,xkey');
$db2 = $this->app->db->table($db1->buildSql())->alias('dt')->field('id');
SystemFile::mk()->whereRaw("id not in {$db2->buildSql()}")->delete();
SystemFile::mk()->where($map)->where(['status' => 1])->delete();
$map = ['issafe' => 0, 'uuid' => AdminService::getUserId()];
$subQuery = SystemFile::mk()->fieldRaw('MAX(id) AS id')->where($map)->group('type, xkey')->buildSql();
SystemFile::mk()->where($map)->whereRaw("id NOT IN ({$subQuery})")->delete();
$this->success('清理重复文件成功!');
}
}

View File

@ -1,17 +1,17 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
namespace app\admin\controller;
@ -23,14 +23,14 @@ use think\admin\service\MenuService;
/**
* 后台界面入口
* Class Index
* @class Index
* @package app\admin\controller
*/
class Index extends Controller
{
/**
* 显示后台首页
* @throws \ReflectionException
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
@ -58,9 +58,7 @@ class Index extends Controller
* 后台主题切换
* @login true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
public function theme()
{
@ -81,13 +79,12 @@ class Index extends Controller
/**
* 修改用户资料
* @login true
* @param mixed $id 用户ID
*/
public function info($id = 0)
public function info()
{
$this->_applyFormToken();
if (AdminService::getUserId() === intval($id)) {
SystemUser::mForm('admin@user/form', 'id', [], ['id' => $id]);
$id = $this->request->param('id');
if (AdminService::getUserId() == intval($id)) {
SystemUser::mForm('user/form', 'id', [], ['id' => $id]);
} else {
$this->error('只能修改自己的资料!');
}
@ -118,20 +115,19 @@ class Index extends Controller
/**
* 修改当前用户密码
* @login true
* @param mixed $id
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function pass($id = 0)
public function pass()
{
$this->_applyFormToken();
$id = $this->request->param('id');
if (AdminService::getUserId() !== intval($id)) {
$this->error('只能修改当前用户的密码!');
$this->error('禁止修改他人密码!');
}
if ($this->app->request->isGet()) {
$this->verify = true;
SystemUser::mForm('admin@user/pass', 'id', [], ['id' => $id]);
SystemUser::mForm('user/pass', 'id', [], ['id' => $id]);
} else {
$data = $this->_vali([
'password.require' => '登录密码不能为空!',
@ -146,6 +142,10 @@ class Index extends Controller
}
if ($user->save(['password' => md5($data['password'])])) {
sysoplog('系统用户管理', "修改用户[{$user['id']}]密码成功");
// 修改密码同步事件处理
$this->app->event->trigger('PluginAdminChangePassword', [
'uuid' => intval($user['id']), 'pass' => $data['password']
]);
$this->success('密码修改成功,下次请使用新密码登录!', '');
} else {
$this->error('密码修改失败,请稍候再试!');

View File

@ -1,19 +1,21 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
@ -21,11 +23,12 @@ use think\admin\extend\CodeExtend;
use think\admin\model\SystemUser;
use think\admin\service\AdminService;
use think\admin\service\CaptchaService;
use think\admin\service\RuntimeService;
use think\admin\service\SystemService;
/**
* 用户登录管理
* Class Login
* @class Login
* @package app\admin\controller
*/
class Login extends Controller
@ -33,9 +36,8 @@ class Login extends Controller
/**
* 后台登录入口
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @return void
* @throws \think\admin\Exception
*/
public function index()
{
@ -43,33 +45,32 @@ class Login extends Controller
if (AdminService::isLogin()) {
$this->redirect(sysuri('admin/index/index'));
} else {
// 当前运行模式
$this->developMode = SystemService::checkRunMode();
// 后台背景处理
$images = str2arr(sysconf('login_image') ?: '', '|') ?: [
SystemService::uri('/static/theme/img/login/bg1.jpg'), SystemService::uri('/static/theme/img/login/bg2.jpg'),
];
$this->loginStyle = sprintf('style="background-image:url(%s)" data-bg-transition="%s"', $images[0], join(',', $images));
// 登录验证令牌
$this->captchaType = 'LoginCaptcha';
$this->captchaToken = CodeExtend::uniqidDate(18);
if (!$this->app->session->get('LoginInputSessionError')) {
$this->app->session->set($this->captchaType, $this->captchaToken);
}
// 更新后台域名
if ($this->request->domain(true) !== sysconf('base.site_host')) {
sysconf('base.site_host', $this->request->domain(true));
}
// 加载登录模板
$this->title = '系统登录';
// 登录验证令牌
$this->captchaType = 'LoginCaptcha';
$this->captchaToken = CodeExtend::uuid();
// 当前运行模式
$this->runtimeMode = RuntimeService::check();
// 后台背景处理
$images = str2arr(sysconf('login_image|raw') ?: '', '|');
if (empty($images)) $images = [
SystemService::uri('/static/theme/img/login/bg1.jpg'),
SystemService::uri('/static/theme/img/login/bg2.jpg'),
];
$this->loginStyle = sprintf('style="background-image:url(%s)" data-bg-transition="%s"', $images[0], join(',', $images));
// 更新后台主域名,用于部分无法获取域名的场景调用
if ($this->request->domain() !== sysconf('base.site_host|raw')) {
sysconf('base.site_host', $this->request->domain());
}
$this->fetch();
}
} else {
$data = $this->_vali([
'username.require' => '登录账号不能为空!',
'username.min:4' => '登录账号不能少于4位字符!',
'username.min:4' => '账号不能少于4位字符!',
'password.require' => '登录密码不能为空!',
'password.min:4' => '登录密码不能少于4位字符!',
'password.min:4' => '密码不能少于4位字符!',
'verify.require' => '图形验证码不能为空!',
'uniqid.require' => '图形验证标识不能为空!',
]);
@ -91,12 +92,15 @@ class Login extends Controller
$this->app->session->set('LoginInputSessionError', true);
$this->error('登录账号或密码错误,请重新输入!');
}
$user->hidden(['sort', 'status', 'password', 'is_deleted']);
$this->app->session->set('user', $user->toArray());
$this->app->session->delete('LoginInputSessionError');
$user->inc('login_num')->update([
'login_at' => date('Y-m-d H:i:s'),
'login_ip' => $this->app->request->ip(),
// 更新登录次数
$user->where(['id' => $user->getAttr('id')])->inc('login_num')->update([
'login_at' => date('Y-m-d H:i:s'), 'login_ip' => $this->app->request->ip(),
]);
// 刷新用户权限
AdminService::apply(true);
sysoplog('系统用户登录', '登录系统后台成功');
$this->success('登录成功', sysuri('admin/index/index'));
}
@ -104,28 +108,29 @@ class Login extends Controller
/**
* 生成验证码
* @return void
*/
public function captcha()
{
$input = $this->_vali([
'type.require' => '验证码类型不能为空!',
'token.require' => '验证码标识不能为空!',
'type.require' => '类型不能为空!',
'token.require' => '标识不能为空!',
]);
$image = CaptchaService::instance()->initialize();
$captcha = ['image' => $image->getData(), 'uniqid' => $image->getUniqid()];
if ($this->app->session->get($input['type']) === $input['token']) {
// 未发生异常时,直接返回验证码内容
if (!$this->app->session->get('LoginInputSessionError')) {
$captcha['code'] = $image->getCode();
$this->app->session->delete($input['type']);
}
$this->success('生成验证码成功', $captcha);
}
/**
* 退出登录
* @return void
*/
public function out()
{
$this->app->session->clear();
$this->app->session->destroy();
$this->success('退出登录成功!', sysuri('admin/login/index'));
}

View File

@ -1,19 +1,21 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
@ -25,7 +27,7 @@ use think\admin\service\NodeService;
/**
* 系统菜单管理
* Class Menu
* @class Menu
* @package app\admin\controller
*/
class Menu extends Controller
@ -41,7 +43,13 @@ class Menu extends Controller
public function index()
{
$this->title = '系统菜单管理';
$this->type = input('get.type', 'index');
$this->type = $this->get['type'] ?? 'index';
// 获取顶级菜单ID
$this->pid = $this->get['pid'] ?? '';
// 查询顶级菜单集合
$this->menupList = SystemMenu::mk()->where(['pid' => 0, 'status' => 1])->order('sort desc,id asc')->column('id,pid,title', 'id');
SystemMenu::mQuery()->layTable();
}
@ -64,6 +72,14 @@ class Menu extends Controller
}
// 菜单数据树数据变平化
$data = DataExtend::arr2table($data);
// 过滤非当前顶级菜单的下级菜单,并重新索引数组
if ($this->type === 'index' && $this->pid) {
$data = array_values(array_filter($data, function ($item) {
return strpos($item['spp'], ",{$this->pid},") !== false;
}));
}
foreach ($data as &$vo) {
if ($vo['url'] !== '#' && !preg_match('/^(https?:)?(\/\/|\\\\)/i', $vo['url'])) {
$vo['url'] = trim(url($vo['url']) . ($vo['params'] ? "?{$vo['params']}" : ''), '\\/');
@ -94,19 +110,17 @@ class Menu extends Controller
/**
* 表单数据处理
* @param array $vo
* @throws \ReflectionException
*/
protected function _form_filter(array &$vo)
{
if ($this->request->isGet()) {
$debug = $this->app->isDebug();
/* 清理权限节点 */
if ($isDebug = $this->app->isDebug()) {
AdminService::clearCache();
}
$debug && AdminService::clear();
/* 读取系统功能节点 */
$this->auths = [];
$this->nodes = MenuService::getList($isDebug);
foreach (NodeService::getMethods($isDebug) as $node => $item) {
$this->nodes = MenuService::getList($debug);
foreach (NodeService::getMethods($debug) as $node => $item) {
if ($item['isauth'] && substr_count($node, '/') >= 2) {
$this->auths[] = ['node' => $node, 'title' => $item['title']];
}

View File

@ -1,74 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免费声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// +----------------------------------------------------------------------
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\service\ModuleService;
/**
* 系统模块管理
* Class Module
* @package app\admin\controller
*/
class Module extends Controller
{
/**
* 系统模块管理
* @auth true
* @menu true
*/
public function index()
{
$this->title = '系统模块管理';
$this->modules = ModuleService::change();
$this->fetch();
}
/**
* 安装更新模块
* @auth true
*/
public function install()
{
$data = $this->_vali(['name.require' => '模块名称不能为空!']);
[$state, $message] = ModuleService::install($data['name']);
$state ? $this->success($message) : $this->error($message);
}
/**
* 查看模块更新
* @auth true
*/
public function change()
{
$data = $this->_vali(['name.require' => '模块名称不能为空!']);
$online = ModuleService::online();
$locals = ModuleService::getModules();
if (isset($online[$data['name']])) {
$this->module = $online[$data['name']];
$this->current = $locals[$data['name']] ?? [];
$pattern = "|^(\d{4})\.(\d{2})\.(\d{2})\.(\d+)$|";
$this->module['change'] = array_reverse($this->module['change']);
foreach ($this->module['change'] as $version => &$change) {
$change = ['content' => $change, 'version' => preg_replace($pattern, '$1年$2月$3日 更新', $version)];
}
$this->fetch();
} else {
$this->error('未查询到模块更新记录!');
}
}
}

View File

@ -1,22 +1,23 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use Exception;
use Ip2Region;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
@ -25,7 +26,7 @@ use think\exception\HttpResponseException;
/**
* 系统日志管理
* Class Oplog
* @class Oplog
* @package app\admin\controller
*/
class Oplog extends Controller
@ -45,23 +46,23 @@ class Oplog extends Controller
$columns = SystemOplog::mk()->column('action,username', 'id');
$this->users = array_unique(array_column($columns, 'username'));
$this->actions = array_unique(array_column($columns, 'action'));
}, function (QueryHelper $query) {
}, static function (QueryHelper $query) {
$query->dateBetween('create_at')->equal('username,action')->like('content,geoip,node');
});
}
/**
* 列表数据处理
* @auth true
* @param array $data
* @throws \Exception
*/
protected function _index_page_filter(array &$data)
{
$region = new Ip2Region();
foreach ($data as &$vo) {
$isp = $region->btreeSearch($vo['geoip']);
$vo['geoisp'] = str_replace(['内网IP', '0', '|'], '', $isp['region'] ?? '') ?: '-';
foreach ($data as &$vo) try {
$vo['geoisp'] = $region->simple($vo['geoip']);
} catch (\Exception $exception) {
$vo['geoip'] = $exception->getMessage();
}
}
@ -77,8 +78,9 @@ class Oplog extends Controller
$this->success('日志清理成功!');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (Exception $exception) {
$this->error("日志清理失败,{$exception->getMessage()}");
} catch (\Exception $exception) {
trace_file($exception);
$this->error(lang("日志清理失败,%s", [$exception->getMessage()]));
}
}

View File

@ -1,22 +1,23 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use Exception;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemQueue;
@ -27,7 +28,7 @@ use think\exception\HttpResponseException;
/**
* 系统任务管理
* Class Queue
* @class Queue
* @package app\admin\controller
*/
class Queue extends Controller
@ -51,7 +52,7 @@ class Queue extends Controller
$this->command = "sudo -u {$_SERVER['USER']} {$this->command}";
}
}
}, function (QueryHelper $query) {
}, static function (QueryHelper $query) {
$query->equal('status')->like('code|title#title,command');
$query->timeBetween('enter_time,exec_time')->dateBetween('create_at');
});
@ -69,11 +70,11 @@ class Queue extends Controller
protected function _index_page_filter(array $data, array &$result)
{
$result['extra'] = ['dos' => 0, 'pre' => 0, 'oks' => 0, 'ers' => 0];
SystemQueue::mk()->field('status,count(1) count')->group('status')->select()->map(function ($item) use (&$result) {
if ($item['status'] === 1) $result['extra']['pre'] = $item['count'];
if ($item['status'] === 2) $result['extra']['dos'] = $item['count'];
if ($item['status'] === 3) $result['extra']['oks'] = $item['count'];
if ($item['status'] === 4) $result['extra']['ers'] = $item['count'];
SystemQueue::mk()->field('status,count(1) count')->group('status')->select()->map(static function ($item) use (&$result) {
if (intval($item['status']) === 1) $result['extra']['pre'] = $item['count'];
if (intval($item['status']) === 2) $result['extra']['dos'] = $item['count'];
if (intval($item['status']) === 3) $result['extra']['oks'] = $item['count'];
if (intval($item['status']) === 4) $result['extra']['ers'] = $item['count'];
});
}
@ -86,11 +87,12 @@ class Queue extends Controller
try {
$data = $this->_vali(['code.require' => '任务编号不能为空!']);
$queue = QueueService::instance()->initialize($data['code'])->reset();
$queue->progress(1, '>>> 任务重置成功 <<<', 0.00);
$queue->progress(1, '>>> 任务重置成功 <<<', '0.00');
$this->success('任务重置成功!', $queue->code);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (Exception $exception) {
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}

View File

@ -1,17 +1,17 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
namespace app\admin\controller;
@ -22,11 +22,10 @@ use think\admin\model\SystemAuth;
use think\admin\model\SystemBase;
use think\admin\model\SystemUser;
use think\admin\service\AdminService;
use think\model\Relation;
/**
* 系统用户管理
* Class User
* @class User
* @package app\admin\controller
*/
class User extends Controller
@ -41,9 +40,7 @@ class User extends Controller
*/
public function index()
{
$this->type = input('get.type', 'index');
// 创建快捷查询工具
$this->type = $this->get['type'] ?? 'index';
SystemUser::mQuery()->layTable(function () {
$this->title = '系统用户管理';
$this->bases = SystemBase::items('身份权限');
@ -53,8 +50,9 @@ class User extends Controller
$query->where(['is_deleted' => 0, 'status' => intval($this->type === 'index')]);
// 关联用户身份资料
$query->with(['userinfo' => function (Relation $relation) {
$relation->field('code,name,content');
/** @var \think\model\Relation|\think\db\Query $query */
$query->with(['userinfo' => static function ($query) {
$query->field('code,name,content');
}]);
// 数据列表搜索过滤
@ -100,6 +98,10 @@ class User extends Controller
]);
$user = SystemUser::mk()->findOrEmpty($data['id']);
if ($user->isExists() && $user->save(['password' => md5($data['password'])])) {
// 修改密码同步事件处理
$this->app->event->trigger('PluginAdminChangePassword', [
'uuid' => $data['id'], 'pass' => $data['password']
]);
sysoplog('系统用户管理', "修改用户[{$data['id']}]密码成功");
$this->success('密码修改成功,请使用新密码登录!', '');
} else {
@ -118,30 +120,30 @@ class User extends Controller
protected function _form_filter(array &$data)
{
if ($this->request->isPost()) {
// 账号权限绑定处理
// 检查资料是否完整
empty($data['username']) && $this->error('登录账号不能为空!');
if ($data['username'] !== AdminService::getSuperName()) {
empty($data['authorize']) && $this->error('未配置权限!');
}
// 处理上传的权限格式
$data['authorize'] = arr2str($data['authorize'] ?? []);
if (isset($data['id']) && $data['id'] > 0) {
unset($data['username']);
} else {
if (empty($data['id'])) {
// 检查账号是否重复
if (empty($data['username'])) {
$this->error('登录账号不能为空!');
}
$map = ['username' => $data['username'], 'is_deleted' => 0];
if (SystemUser::mk()->where($map)->count() > 0) {
$this->error("账号已经存在,请使用其它账号!");
}
// 新添加的用户密码与账号相同
$data['password'] = md5($data['username']);
} else {
unset($data['username']);
}
} else {
// 权限绑定处理
$data['authorize'] = str2arr($data['authorize'] ?? '');
// 用户身份数据
$this->auths = SystemAuth::items();
$this->bases = SystemBase::items('身份权限');
// 用户权限管理
$this->superName = AdminService::getSuperName();
$this->authorizes = SystemAuth::items();
$this->super = AdminService::getSuperName();
}
}

View File

@ -1,27 +1,30 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\service\AdminService;
use think\Response;
/**
* 通用插件管理
* Class Plugs
* 扩展插件管理
* @class Plugs
* @package app\admin\controller\api
*/
class Plugs extends Controller
@ -34,27 +37,54 @@ class Plugs extends Controller
public function icon()
{
$this->title = '图标选择器';
// 读取 layui 字体图标
if (empty($this->layuiIcons = $this->app->cache->get('LayuiIcons', []))) {
$style = file_get_contents(syspath('public/static/plugs/layui/css/layui.css'));
if (preg_match_all('#\.(layui-icon-[\w-]+):#', $style, $matches)) {
if (count($this->layuiIcons = $matches[1]) > 0) {
$this->app->cache->set('LayuiIcons', $this->layuiIcons, 60);
}
}
}
// 读取 ThinkAdmin 字体图标
if (empty($this->thinkIcons = $this->app->cache->get('ThinkAdminSelfIcons', []))) {
$style = file_get_contents(syspath('public/static/theme/css/iconfont.css'));
if (preg_match_all('#\.(iconfont-[\w-]+):#', $style, $matches)) {
if (count($this->thinkIcons = $matches[1]) > 0) {
$this->app->cache->set('ThinkAdminSelfIcons', $this->thinkIcons, 60);
}
}
}
// 读取 extra 自定义字体图标
if (empty($this->extraIcons = $this->app->cache->get('ThinkAdminExtraIcons', []))) {
$extraIconPath = syspath('public/static/extra/icon/iconfont.css');
if (file_exists($extraIconPath)) {
$style = file_get_contents($extraIconPath);
if (preg_match_all('#\.(iconfont-[\w-]+):#', $style, $matches)) {
if (count($this->extraIcons = $matches[1]) > 0) {
$this->app->cache->set('ThinkAdminExtraIcons', $this->extraIcons, 60);
}
}
}
}
$this->field = $this->app->request->get('field', 'icon');
$this->fetch(realpath(__DIR__ . '/../../view/api/icon.html'));
$this->fetch(dirname(__DIR__, 2) . '/view/api/icon.html');
}
/**
* 前端脚本变量
* @return \think\Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
public function script(): \think\Response
public function script(): Response
{
$taDebug = $this->app->isDebug() ? 'true' : 'false';
$taAdmin = sysuri('admin/index/index', [], false);
$taEditor = sysconf('base.editor') ?: 'ckeditor4';
return response(join("\n", [
"window.taDebug = {$taDebug};",
"window.taAdmin = '{$taAdmin}';",
"window.taEditor = '{$taEditor}';",
]))->contentType('application/x-javascript');
$token = $this->request->get('uptoken', '');
$domain = boolval(AdminService::withUploadUnid($token));
return response(join("\r\n", [
sprintf("window.taDebug = %s;", $this->app->isDebug() ? 'true' : 'false'),
sprintf("window.taAdmin = '%s';", sysuri('admin/index/index', [], false, $domain)),
sprintf("window.taEditor = '%s';", sysconf('base.editor|raw') ?: 'ckeditor4'),
]))->contentType('application/javascript');
}
/**
@ -67,7 +97,7 @@ class Plugs extends Controller
sysoplog('系统运维管理', '创建数据库优化任务');
$this->_queue('优化数据库所有数据表', 'xadmin:database optimize');
} else {
$this->error('只有超级管理员才能操作!');
$this->error('请使用超管账号操作!');
}
}
}

View File

@ -1,97 +1,90 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use Exception;
use Psr\Log\NullLogger;
use think\admin\Controller;
use think\admin\model\SystemQueue;
use think\admin\service\AdminService;
use think\admin\service\QueueService;
use think\exception\HttpResponseException;
/**
* 后台任务通用接口
* Class Queue
* 任务监听服务管理
* @class Queue
* @package app\admin\controller\api
*/
class Queue extends Controller
{
/**
* 任务进度查询
* @login true
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function progress()
{
$input = $this->_vali(['code.require' => '任务编号不能为空!']);
$queue = QueueService::instance()->initialize($input['code']);
$this->success('获取任务进度成功!', $queue->progress());
}
/**
* WIN停止监听进程
* 停止监听服务
* @login true
*/
public function stop()
{
try {
if (AdminService::isSuper()) try {
$message = $this->app->console->call('xadmin:queue', ['stop'])->fetch();
if (stripos($message, 'sent end signal to process')) {
sysoplog('系统运维管理', '尝试停止后台服务主进程');
$this->success('停止后台服务主进程成功!');
sysoplog('系统运维管理', '尝试停止任务监听服务');
$this->success('停止任务监听服务成功!');
} elseif (stripos($message, 'processes to stop')) {
$this->success('没有找到需要停止的进程');
$this->success('没有找到需要停止的服务!');
} else {
$this->error(nl2br($message));
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (Exception $exception) {
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* WIN创建监听进程
* 启动监听服务
* @login true
*/
public function start()
{
try {
if (AdminService::isSuper()) try {
$message = $this->app->console->call('xadmin:queue', ['start'])->fetch();
if (stripos($message, 'daemons started successfully for pid')) {
sysoplog('系统运维管理', '尝试启动后台服务主进程');
$this->success('后台服务主进程启动成功!');
sysoplog('系统运维管理', '尝试启动任务监听服务');
$this->success('任务监听服务启动成功!');
} elseif (stripos($message, 'daemons already exist for pid')) {
$this->success('后台服务主进程已经存在');
$this->success('任务监听服务已经启动');
} else {
$this->error(nl2br($message));
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (Exception $exception) {
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* 检查任务状态
* 检查监听服务
* @login true
*/
public function status()
@ -99,14 +92,27 @@ class Queue extends Controller
if (AdminService::isSuper()) try {
$message = $this->app->console->call('xadmin:queue', ['status'])->fetch();
if (preg_match('/process.*?\d+.*?running/', $message)) {
echo "<span class='color-green pointer' data-tips-text='{$message}'>已启动</span>";
echo "<span class='color-green pointer' data-tips-text='{$message}'>{$this->app->lang->get('已启动')}</span>";
} else {
echo "<span class='color-red pointer' data-tips-text='{$message}'>未启动</span>";
echo "<span class='color-red pointer' data-tips-text='{$message}'>{$this->app->lang->get('未启动')}</span>";
}
} catch (Exception $exception) {
echo "<span class='color-red pointer' data-tips-text='{$exception->getMessage()}'>异 常</span>";
} catch (\Error|\Exception $exception) {
echo "<span class='color-red pointer' data-tips-text='{$exception->getMessage()}'>{$this->app->lang->get('异 常')}</span>";
} else {
echo "<span class='color-red pointer' data-tips-text='只有超级管理员才能操作!'>无权限</span>";
$message = lang('只有超级管理员才能操作!');
echo "<span class='color-red pointer' data-tips-text='{$message}'>{$this->app->lang->get('无权限')}</span>";
}
}
/**
* 查询任务进度
* @login true
*/
public function progress()
{
$input = $this->_vali(['code.require' => '任务编号不能为空!']);
$this->app->db->setLog(new NullLogger()); /* 关闭数据库请求日志 */
$message = SystemQueue::mk()->where($input)->value('message', '');
$this->success('获取任务进度成功d', json_decode($message, true));
}
}

View File

@ -1,30 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\model\SystemConfig;
use think\admin\service\AdminService;
use think\admin\service\SystemService;
use think\admin\service\RuntimeService;
use think\exception\HttpResponseException;
/**
* 系统运行控制管理
* Class System
* 系统运行管理
* @class System
* @package app\admin\controller\api
*/
class System extends Controller
@ -37,16 +39,15 @@ class System extends Controller
public function push()
{
if (AdminService::isSuper()) try {
AdminService::clearCache();
SystemService::pushRuntime();
sysoplog('系统运维管理', '刷新创建路由缓存');
RuntimeService::push() && sysoplog('系统运维管理', '刷新发布运行缓存');
$this->success('网站缓存加速成功!', 'javascript:location.reload()');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('只有超级管理员才能操作!');
$this->error('请使用超管账号操作!');
}
}
@ -57,16 +58,15 @@ class System extends Controller
public function clear()
{
if (AdminService::isSuper()) try {
AdminService::clearCache();
SystemService::clearRuntime();
sysoplog('系统运维管理', '清理网站日志缓存');
RuntimeService::clear() && sysoplog('系统运维管理', '清理网站日志缓存');
$this->success('清空日志缓存成功!', 'javascript:location.reload()');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('只有超级管理员才能操作!');
$this->error('请使用超管账号操作!');
}
}
@ -77,24 +77,22 @@ class System extends Controller
public function debug()
{
if (AdminService::isSuper()) if (input('state')) {
SystemService::setRuntime('product');
RuntimeService::set('product');
sysoplog('系统运维管理', '开发模式切换为生产模式');
$this->success('已切换为生产模式!', 'javascript:location.reload()');
} else {
SystemService::setRuntime('debug');
RuntimeService::set('debug');
sysoplog('系统运维管理', '生产模式切换为开发模式');
$this->success('已切换为开发模式!', 'javascript:location.reload()');
} else {
$this->error('只有超级管理员才能操作!');
$this->error('请使用超管账号操作!');
}
}
/**
* 修改富文本编辑器
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
public function editor()
{
@ -104,7 +102,7 @@ class System extends Controller
sysoplog('系统运维管理', "切换编辑器为{$editor}");
$this->success('已切换后台编辑器!', 'javascript:location.reload()');
} else {
$this->error('只有超级管理员才能操作!');
$this->error('请使用超管账号操作!');
}
}
@ -122,7 +120,7 @@ class System extends Controller
foreach ($tmpdata as $type => $items) foreach ($items as $name => $value) {
$newdata[] = ['type' => $type, 'name' => $name, 'value' => $value];
}
$this->app->db->transaction(function () use ($newdata) {
$this->app->db->transaction(static function () use ($newdata) {
SystemConfig::mQuery()->empty()->insertAll($newdata);
});
$this->app->cache->delete('SystemConfig');
@ -131,9 +129,10 @@ class System extends Controller
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('只有超级管理员才能操作!');
$this->error('请使用超管账号操作!');
}
}
}

View File

@ -1,77 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免费声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// +----------------------------------------------------------------------
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\service\ModuleService;
use think\admin\service\SystemService;
/**
* 安装服务端支持
* Class Update
* @package app\admin\controller\api
*/
class Update extends Controller
{
/**
* 访问环境拦截
*/
protected function initialize()
{
if (!SystemService::checkRunMode()) {
$this->error('只允许访问本地或官方代码!');
}
}
/**
* 读取文件内容
*/
public function get()
{
$filename = decode(input('encode', '0'));
if (!ModuleService::checkAllowDownload($filename)) {
$this->error('下载的文件不在认证规则中!');
}
if (file_exists($realname = $this->app->getRootPath() . $filename)) {
$this->success('读取文件内容成功!', [
'content' => base64_encode(file_get_contents($realname)),
]);
} else {
$this->error('读取文件内容失败!');
}
}
/**
* 读取文件列表
*/
public function node()
{
$this->success('获取文件列表成功!', ModuleService::getChanges(
json_decode($this->request->post('rules', '[]', ''), true),
json_decode($this->request->post('ignore', '[]', ''), true)
));
}
/**
* 获取模块信息
*/
public function version()
{
$this->success('获取模块信息成功!', ModuleService::getModules());
}
}

View File

@ -1,19 +1,21 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use think\admin\Controller;
@ -22,6 +24,7 @@ use think\admin\model\SystemFile;
use think\admin\service\AdminService;
use think\admin\Storage;
use think\admin\storage\AliossStorage;
use think\admin\storage\AlistStorage;
use think\admin\storage\LocalStorage;
use think\admin\storage\QiniuStorage;
use think\admin\storage\TxcosStorage;
@ -32,105 +35,143 @@ use think\Response;
/**
* 文件上传接口
* Class Upload
* @class Upload
* @package app\admin\controller\api
*/
class Upload extends Controller
{
/**
* 文件上传脚本
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
public function index(): Response
{
$data = ['exts' => []];
foreach (str2arr(sysconf('storage.allow_exts')) as $ext) {
$data['exts'][$ext] = Storage::mime($ext);
}
$template = realpath(__DIR__ . '/../../view/api/upload.js');
[$uuid, $unid, $exts] = $this->initUnid(false);
$allows = str2arr(sysconf('storage.allow_exts|raw'));
if (empty($uuid) && $unid > 0) $allows = array_intersect($exts, $allows);
foreach ($allows as $ext) $data['exts'][$ext] = Storage::mime($ext);
$data['exts'] = json_encode($data['exts'], JSON_UNESCAPED_UNICODE);
$data['nameType'] = sysconf('storage.name_type') ?: 'xmd5';
return view($template, $data)->contentType('application/x-javascript');
$data['nameType'] = sysconf('storage.name_type|raw') ?: 'xmd5';
return view(dirname(__DIR__, 2) . '/view/api/upload.js', $data)->contentType('application/x-javascript');
}
/**
* 文件上传检查
* @login true
* @throws \think\admin\Exception
* 文件选择器
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function image()
{
[$uuid, $unid] = $this->initUnid();
SystemFile::mQuery()->layTable(function () {
$this->title = '文件选择器';
}, function (QueryHelper $query) use ($unid, $uuid) {
if ($unid && $uuid) $query->where(function ($query) use ($uuid, $unid) {
/** @var \think\db\Query $query */
$query->whereOr([['uuid', '=', $uuid], ['unid', '=', $unid]]);
}); else {
$query->where($unid ? ['unid' => $unid] : ['uuid' => $uuid]);
}
$query->where(['status' => 2, 'issafe' => 0])->in('xext#type');
$query->like('name,hash')->dateBetween('create_at')->order('id desc');
});
}
/**
* 文件上传检查
*/
public function state()
{
[$name, $safe] = [input('name'), $this->getSafe()];
$data = ['uptype' => $this->getType(), 'safe' => intval($safe), 'key' => input('key')];
$file = SystemFile::mk()->data($this->_vali([
'xkey.value' => $data['key'],
'type.value' => $this->getType(),
'uuid.value' => AdminService::getUserId(),
'name.require' => '名称不能为空!',
'hash.require' => '哈希不能为空!',
'xext.require' => '后缀不能为空!',
'size.require' => '大小不能为空!',
'mime.default' => '',
'status.value' => 1,
]));
if (empty($file['mime'])) $file['mime'] = Storage::mime($file['xext']);
$info = Storage::instance($data['uptype'])->info($data['key'], $safe, $name);
if (is_array($info) && isset($info['url']) && isset($info['key'])) {
$file->save(['xurl' => $info['url'], 'isfast' => 1, 'issafe' => $data['safe']]);
$extr = ['id' => $file->id ?? 0, 'url' => $info['url'], 'key' => $info['key']];
$this->success('文件已经上传', array_merge($data, $extr), 200);
} elseif ('local' === $data['uptype']) {
$data['url'] = LocalStorage::instance()->url($data['key'], $safe, $name);
$data['server'] = LocalStorage::instance()->upload();
} elseif ('qiniu' === $data['uptype']) {
$data['url'] = QiniuStorage::instance()->url($data['key'], $safe, $name);
$data['token'] = QiniuStorage::instance()->buildUploadToken($data['key'], 3600, $name);
$data['server'] = QiniuStorage::instance()->upload();
} elseif ('alioss' === $data['uptype']) {
$token = AliossStorage::instance()->buildUploadToken($data['key'], 3600, $name);
$data['url'] = $token['siteurl'];
$data['policy'] = $token['policy'];
$data['signature'] = $token['signature'];
$data['OSSAccessKeyId'] = $token['keyid'];
$data['server'] = AliossStorage::instance()->upload();
} elseif ('txcos' === $data['uptype']) {
$token = TxcosStorage::instance()->buildUploadToken($data['key'], 3600, $name);
$data['url'] = $token['siteurl'];
$data['q-ak'] = $token['q-ak'];
$data['policy'] = $token['policy'];
$data['q-key-time'] = $token['q-key-time'];
$data['q-signature'] = $token['q-signature'];
$data['q-sign-algorithm'] = $token['q-sign-algorithm'];
$data['server'] = TxcosStorage::instance()->upload();
} elseif ('upyun' === $data['uptype']) {
$token = UpyunStorage::instance()->buildUploadToken($data['key'], 3600, $name, input('size'), input('hash'));
$data['url'] = $token['siteurl'];
$data['policy'] = $token['policy'];
$data['authorization'] = $token['authorization'];
$data['server'] = UpyunStorage::instance()->upload();
try {
[$uuid, $unid] = $this->initUnid();
[$name, $safe] = [input('name'), $this->getSafe()];
$data = ['uptype' => $this->getType(), 'safe' => intval($safe), 'key' => input('key')];
$file = SystemFile::mk()->data($this->_vali([
'xkey.value' => $data['key'],
'type.value' => $this->getType(),
'uuid.value' => $uuid,
'unid.value' => $unid,
'name.require' => '名称不能为空!',
'hash.require' => '哈希不能为空!',
'xext.require' => '后缀不能为空!',
'size.require' => '大小不能为空!',
'mime.default' => '',
'status.value' => 1,
]));
$mime = $file->getAttr('mime');
if (empty($mime)) $file->setAttr('mime', Storage::mime($file->getAttr('xext')));
$info = Storage::instance($data['uptype'])->info($data['key'], $safe, $name);
if (isset($info['url']) && isset($info['key'])) {
$file->save(['xurl' => $info['url'], 'isfast' => 1, 'issafe' => $data['safe']]);
$extr = ['id' => $file->id ?? 0, 'url' => $info['url'], 'key' => $info['key']];
$this->success('文件已经上传', array_merge($data, $extr), 200);
} elseif ('local' === $data['uptype']) {
$local = LocalStorage::instance();
$data['url'] = $local->url($data['key'], $safe, $name);
$data['server'] = $local->upload();
} elseif ('qiniu' === $data['uptype']) {
$qiniu = QiniuStorage::instance();
$data['url'] = $qiniu->url($data['key'], $safe, $name);
$data['token'] = $qiniu->token($data['key'], 3600, $name);
$data['server'] = $qiniu->upload();
} elseif ('alioss' === $data['uptype']) {
$alioss = AliossStorage::instance();
$token = $alioss->token($data['key'], 3600, $name);
$data['url'] = $token['siteurl'];
$data['policy'] = $token['policy'];
$data['signature'] = $token['signature'];
$data['OSSAccessKeyId'] = $token['keyid'];
$data['server'] = $alioss->upload();
} elseif ('txcos' === $data['uptype']) {
$txcos = TxcosStorage::instance();
$token = $txcos->token($data['key'], 3600, $name);
$data['url'] = $token['siteurl'];
$data['q-ak'] = $token['q-ak'];
$data['policy'] = $token['policy'];
$data['q-key-time'] = $token['q-key-time'];
$data['q-signature'] = $token['q-signature'];
$data['q-sign-algorithm'] = $token['q-sign-algorithm'];
$data['server'] = $txcos->upload();
} elseif ('upyun' === $data['uptype']) {
$upyun = UpyunStorage::instance();
$token = $upyun->token($data['key'], 3600, $name, input('hash', ''));
$data['url'] = $token['siteurl'];
$data['policy'] = $token['policy'];
$data['server'] = $upyun->upload();
$data['authorization'] = $token['authorization'];
} elseif ('alist' === $data['uptype']) {
$alist = AlistStorage::instance();
$data['url'] = $alist->url($data['key']);
$data['server'] = $alist->upload();
$data['filepath'] = $alist->real($data['key']);
$data['authorization'] = $alist->token();
} else {
$this->error('未知的存储引擎!');
}
$file->save(['xurl' => $data['url'], 'isfast' => 0, 'issafe' => $data['safe']]);
$this->success('获取上传授权参数', array_merge($data, ['id' => $file->id ?? 0]), 404);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
$this->error($exception->getMessage());
}
$file->save(['xurl' => $data['url'], 'isfast' => 0, 'issafe' => $data['safe']]);
$this->success('获取上传授权参数', array_merge($data, ['id' => $file->id ?? 0]), 404);
}
/**
* 更新文件状态
* @login true
* @return void
*/
public function done()
{
[$uuid, $unid] = $this->initUnid();
$data = $this->_vali([
'id.require' => '编号不能为空!',
'hash.require' => '哈希不能为空!',
'uuid.value' => AdminService::getUserId(),
'uuid.value' => $uuid,
'unid.value' => $unid,
]);
$file = SystemFile::mk()->where($data)->findOrEmpty();
if ($file->isEmpty()) $this->error('文件不存在!');
@ -141,123 +182,76 @@ class Upload extends Controller
}
}
/**
* 文件选择器
* @login true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function image()
{
SystemFile::mQuery()->layTable(function () {
$this->title = '文件选择器';
}, function (QueryHelper $query) {
$query->where(['status' => 2, 'issafe' => 0, 'uuid' => AdminService::getUserId()]);
$query->like('name,hash')->in('xext#type')->dateBetween('create_at')->order('id desc');
});
}
/**
* 视频选择器
* @login true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function video()
{
SystemFile::mQuery()->layTable(function () {
$this->title = '文件选择器';
}, function (QueryHelper $query) {
$query->like('name,hash')->dateBetween('create_at')->order('id desc');
$query->where(['status' => 2, 'issafe' => 0, 'uuid' => AdminService::getUserId()]);
});
}
/**
* 文档选择器
* @login true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function document()
{
SystemFile::mQuery()->layTable(function () {
$this->title = '文件选择器';
}, function (QueryHelper $query) {
$query->like('name,hash')->dateBetween('create_at')->order('id desc');
$query->where(['status' => 2, 'issafe' => 0, 'uuid' => AdminService::getUserId()]);
});
}
/**
* 文件上传入口
* @login true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
public function file()
{
if (!($file = $this->getFile())->isValid()) {
$this->error('文件上传异常,文件过大或未上传!');
}
$safeMode = $this->getSafe();
[$uuid, $unid, $unexts] = $this->initUnid();
// 开始处理文件上传
$file = $this->getFile();
$extension = strtolower($file->getOriginalExtension());
$saveName = input('key') ?: Storage::name($file->getPathname(), $extension, '', 'md5_file');
$saveFileName = input('key') ?: Storage::name($file->getPathname(), $extension, '', 'md5_file');
// 检查文件名称是否合法
if (strpos($saveName, '../') !== false) {
if (strpos($saveFileName, '..') !== false) {
$this->error('文件路径不能出现跳级操作!');
}
// 检查文件后缀是否被恶意修改
if (strtolower(pathinfo(parse_url($saveName, PHP_URL_PATH), PATHINFO_EXTENSION)) !== $extension) {
if (strtolower(pathinfo(parse_url($saveFileName, PHP_URL_PATH), PATHINFO_EXTENSION)) !== $extension) {
$this->error('文件后缀异常,请重新上传文件!');
}
// 屏蔽禁止上传指定后缀的文件
if (!in_array($extension, str2arr(sysconf('storage.allow_exts')))) {
if (!in_array($extension, str2arr(sysconf('storage.allow_exts|raw')))) {
$this->error('文件类型受限,请在后台配置规则!');
}
// 前端用户上传后缀检查处理
if (empty($uuid) && $unid > 0 && !in_array($extension, $unexts)) {
$this->error('文件类型受限,请上传允许的文件类型!');
}
if (in_array($extension, ['sh', 'asp', 'bat', 'cmd', 'exe', 'php'])) {
$this->error('文件安全保护,禁止上传可执行文件!');
}
try {
if ($this->getType() === 'local') {
$safeMode = $this->getSafe();
if (($type = $this->getType()) === 'local') {
$local = LocalStorage::instance();
$distName = $local->path($saveName, $safeMode);
$file->move(dirname($distName), basename($distName));
$info = $local->info($saveName, $safeMode, $file->getOriginalName());
$distName = $local->path($saveFileName, $safeMode);
if (PHP_SAPI === 'cli') {
is_dir(dirname($distName)) || mkdir(dirname($distName), 0777, true);
rename($file->getPathname(), $distName);
} else {
$file->move(dirname($distName), basename($distName));
}
$info = $local->info($saveFileName, $safeMode, $file->getOriginalName());
if (in_array($extension, ['jpg', 'gif', 'png', 'bmp', 'jpeg', 'wbmp'])) {
if ($this->imgNotSafe($distName) && $local->del($saveName)) {
if ($this->imgNotSafe($distName) && $local->del($saveFileName)) {
$this->error('图片未通过安全检查!');
}
[$width, $height] = getimagesize($distName);
if (($width < 1 || $height < 1) && $local->del($saveName)) {
if (($width < 1 || $height < 1) && $local->del($saveFileName)) {
$this->error('读取图片的尺寸失败!');
}
}
} else {
$bina = file_get_contents($file->getPathname());
$info = Storage::instance($this->getType())->set($saveName, $bina, $safeMode, $file->getOriginalName());
$info = Storage::instance($type)->set($saveFileName, $bina, $safeMode, $file->getOriginalName());
}
if (isset($info['url'])) {
$this->success('文件上传成功!', ['url' => $safeMode ? $saveName : $info['url']]);
$this->success('文件上传成功!', ['url' => $safeMode ? $saveFileName : $info['url']]);
} else {
$this->error('文件处理失败,请稍候再试!');
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
/**
* 获取文件上传类型
* 获取上传类型
* @return boolean
*/
private function getSafe(): bool
@ -266,24 +260,22 @@ class Upload extends Controller
}
/**
* 获取文件上传方式
* 获取上传方式
* @return string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\admin\Exception
*/
private function getType(): string
{
$type = strtolower(input('uptype', ''));
if (in_array($type, ['local', 'qiniu', 'alioss', 'txcos', 'uptype'])) {
if (in_array($type, array_keys(Storage::types()))) {
return $type;
} else {
return strtolower(sysconf('storage.type'));
return strtolower(sysconf('storage.type|raw'));
}
}
/**
* 获取本地文件对象
* 获取文件对象
* @return UploadedFile|void
*/
private function getFile(): UploadedFile
@ -293,15 +285,32 @@ class Upload extends Controller
if ($file instanceof UploadedFile) {
return $file;
} else {
$this->error('未获取到上传的文件对象');
$this->error('读取临时文件失败');
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error(lang($exception->getMessage()));
}
}
/**
* 初始化用户状态
* @param boolean $check
* @return array
*/
private function initUnid(bool $check = true): array
{
$uuid = AdminService::getUserId();
[$unid, $exts] = AdminService::withUploadUnid();
if ($check && empty($uuid) && empty($unid)) {
$this->error('未登录,禁止使用文件上传!');
} else {
return [$uuid, $unid, $exts];
}
}
/**
* 检查图片是否安全
* @param string $filename
@ -321,6 +330,7 @@ class Upload extends Controller
$bins = hex2bin($hexs);
/* 匹配十六进制中的 <% ( ) %> 或 <? ( ) ?> 或 <script | /script> */
foreach (['<?php ', '<% ', '<script '] as $key) if (stripos($bins, $key) !== false) return true;
return preg_match("/(3c25.*?28.*?29.*?253e)|(3c3f.*?28.*?29.*?3f3e)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexs);
$result = preg_match("/(3c25.*?28.*?29.*?253e)|(3c3f.*?28.*?29.*?3f3e)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexs);
return $result === false || $result > 0;
}
}

200
app/admin/lang/en-us.php Normal file
View File

@ -0,0 +1,200 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
$extra = [];
$extra['开发人员或在功能调试时使用,系统异常时会显示详细的错误信息,同时还会记录操作日志及数据库 SQL 语句信息。'] = 'Developers may use it during functional debugging. When there are system exceptions, detailed error messages will be displayed, and operation logs and database SQL statement information will also be recorded.';
$extra['项目正式部署上线后使用,系统异常时统一显示 “%s”只记录重要的异常日志信息强烈推荐上线后使用此模式。'] = 'After the project is officially deployed and launched, it will be used. When there are system exceptions, " %s " will be displayed uniformly, and only important exception log information will be recorded. It is strongly recommended to use this mode after launch.';
$extra['旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。'] = 'The old version of the editor is compatible with browsers, but the content editing experience is slightly insufficient.';
$extra['新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。'] = 'The new version of the editor only supports the new feature browser and has a good experience in content editing. It is recommended to use it.';
$extra['优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。'] = 'Priority should be given to using the new version of the editor. If the browser does not support the new version, it will automatically be downgraded to the old version of the editor.';
$extra['文件上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。'] = 'Uploading files to the `static/upload` directory of the local server does not support uploading large files, occupying server disk space, and consuming server bandwidth traffic during access.';
$extra['文件上传到 Alist 存储的服务器或云存储空间,根据服务配置可支持大文件上传,不占用本身服务器空间及服务器带宽流量。'] = 'Files can be uploaded to the Alist storage server or Cloud storage space. According to the service configuration, large file upload can be supported without occupying the server space and server bandwidth traffic.';
$extra['文件上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。'] = 'Files can be uploaded to Qiniu Cloud storage space. It supports large file upload, does not occupy server space and server bandwidth traffic, and supports CDN accelerated access. It is recommended when there is a large amount of access.';
$extra['文件上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。'] = "Uploading files to Upyun Cloud's USS storage space supports large file uploads without occupying server space or bandwidth traffic. It supports CDN accelerated access and is recommended for high traffic.";
$extra['文件上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。'] = "Uploading files to Aliyun Cloud's OSS storage space supports large file uploads without occupying server space or bandwidth traffic. It supports CDN accelerated access and is recommended for high traffic.";
$extra['文件上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。'] = "Uploading files to Tencent Cloud's COS storage space supports large file uploads without occupying server space or bandwidth traffic. It supports CDN accelerated access and is recommended for high traffic.";
$extra['网站名称及网站图标,将显示在浏览器的标签上。'] = "The website name and icon will be displayed on the browser's label.";
$extra['管理程序名称,将显示在后台左上角标题。'] = 'The name of the management program will be displayed in the header in the upper left corner of the background.';
$extra['管理程序版本,将显示在后台左上角标题。'] = 'The management program version will be displayed in the top left corner of the background with a title.';
$extra['网站版权信息,在后台登录页面显示版本信息并链接到备案到信息备案管理系统。'] = 'Website copyright information is displayed on the backend login page and linked to the information filing management system.';
$extra['网站备案号,可以在 %s 查询获取,将显示在登录页面下面。'] = 'The website registration number can be found at %s and will be displayed below the login page.';
$extra['公安备案号,可以在 %s 查询获取,将在登录页面下面显示。'] = 'The public security registration number can be obtained by searching at %s and will be displayed below the login page.';
$extra['点击可复制【服务启动指令】'] = "Click to copy the 'Service Start Command'";
$extra['待处理 %s 个任务,处理中 %s 个任务,已完成 %s 个任务,已失败 %s 个任务。'] = 'There are %s tasks to be processed, %s tasks in progress, %s tasks completed, and %s tasks failed.';
$extra['确定要切换到生产模式运行吗?'] = 'Are you sure you want to switch to Production mode?';
$extra['确定要切换到开发模式运行吗?'] = 'Are you sure you want to switch to Development mode?';
$extra["超级管理员账号的密码未修改,建议立即<a data-modal='%s'>修改密码</a>"] = "The super administrator password has not been changed. Suggest <a data-modal='%s'>changing password</a>.";
$extra['等待处理'] = 'Pending';
$extra['正在处理'] = 'Processing';
$extra['处理完成'] = 'Completed';
$extra['处理失败'] = 'Failed';
$extra['条件搜索'] = 'Search';
$extra['批量删除'] = 'Batch Delete';
$extra['上传进度 %s'] = 'Upload progress %s';
$extra['文件上传出错!'] = 'File upload error.';
$extra['文件上传失败!'] = 'File upload failed.';
$extra['大小超出限制!'] = 'Size exceeds limit.';
$extra['文件秒传成功!'] = 'Successfully transmitted the file in seconds.';
$extra['上传接口异常!'] = 'Abnormal upload interface.';
$extra['文件上传成功!'] = 'File uploaded successfully.';
$extra['图片压缩失败!'] = 'Image compression failed.';
$extra['无效的文件上传对象!'] = 'Invalid file upload object.';
return array_merge($extra, [
// 系统操作
'基本资料' => 'Basic information',
'安全设置' => 'Security setting',
'缓存加速' => 'Cache acceleration',
'清理缓存' => 'Clean cache',
'配色方案' => 'Color scheme',
'立即登录' => 'Login',
'退出登录' => 'Logout',
'系统提示:' => 'System Notify: ',
'清空日志缓存成功!' => 'Successfully cleared the log cache.',
'获取任务进度成功!' => 'Successfully obtained task progress.',
'网站缓存加速成功!' => 'Website cache acceleration successful.',
'请使用超管账号操作!' => 'Please use a super managed account to operate.',
'停止任务监听服务成功!' => 'Successfully stopped task listening service.',
'任务监听服务启动成功!' => 'Task monitoring service started successfully.',
'任务监听服务已经启动!' => 'The task monitoring service has started.',
'没有找到需要停止的服务!' => 'No services found that need to be stopped.',
'已切换后台编辑器!' => 'Switched to background editor.',
// 其他搜索器提示
'请选择登录时间' => 'Please select the Login time',
'请选择创建时间' => 'Please select the creation time',
'请输入账号或名称' => 'Please enter an account or name',
'请输入权限名称' => 'Please enter the permission name',
'请输入数据编码' => 'Please enter the data code',
'请输入数据名称' => 'Please enter the data name',
'请输入文件名称' => 'Please enter the file name',
'请输入文件哈希' => 'Please enter the file hash',
'请输入操作节点' => 'Please enter the operate node',
'请输入操作内容' => 'Please enter the operate content',
'请输入访问地址' => 'Please enter the access Geoip',
// 系统配置
'运行模式' => 'Running Mode',
'生产模式' => 'Production mode',
'开发模式' => 'Development mode',
'以开发模式运行' => 'Running in Development mode',
'以生产模式运行' => 'Running in Production mode',
'清理无效配置' => 'Clean up Invalid Configurations',
'修改系统参数' => 'Modify System Parameters',
'清理系统配置成功!' => 'Successfully cleaned.',
'自适应模式' => 'Adaptive Mode',
'富编辑器' => 'RichText Editor',
'存储引擎' => 'Storage Engine',
'系统参数' => 'System Parameter',
'网站名称' => 'Site Name',
'管理程序名称' => 'Program Name',
'管理程序版本' => 'Program Version',
'公安备案号' => 'Public security registration number',
'网站备案号' => 'Website registration number',
'网站版权信息' => 'Website copyright information',
'系统信息' => 'System Information',
'应用插件' => 'Plugin Information',
'核心框架' => 'Core Framework',
'平台框架' => 'Platform Framework',
'操作系统' => 'Operating System',
'运行环境' => 'Runtime Environment',
'仅开发模式可见' => 'Visible only in Development mode',
'仅生产模式可见' => 'Visible only in Production mode',
'插件名称' => 'Plugin Name',
'应用名称' => 'App Name',
'插件包名' => 'Package Name',
'插件版本' => 'Plugin Version',
'授权协议' => 'License',
'文件默认存储方式' => 'Default storage method for file upload',
'当前系统配置参数' => 'Current system configuration parameters',
'仅超级管理员可配置' => 'Only super administrators can configure',
// 系统任务管理
'优化数据库' => 'Optimize Database',
'开启服务' => 'Start Service',
'关闭服务' => 'Shutdown Service',
'定时清理' => 'Regular cleaning',
'服务状态' => 'Service',
'任务统计' => 'Total',
'编号名称' => 'Name',
'任务指令' => 'Command',
'任务状态' => 'Status',
'计划时间' => 'scheduled time',
'任务名称' => 'Name',
'检查中' => 'Checking',
'任务计划' => 'Scheduled',
'重 置' => 'Reset',
'日 志' => 'Logs',
'异 常' => 'Abnormal',
'无权限' => 'Denied',
'已启动' => 'Started',
'未启动' => 'Stopped',
// 数据字典管理
'数据编码' => 'Code',
'数据名称' => 'Name',
'操作账号' => 'User',
'操作节点' => "Node",
'操作行为' => 'Action',
'操作内容' => "Content",
'访问地址' => 'Geo IP',
'网络服务商' => 'ISP.',
'日志清理成功!' => 'Logger Clear Complate.',
'成功清理所有日志' => 'Successfully cleared all logs.',
// 系统文件管理
'文件名称' => 'Name',
'文件哈希' => "HASH",
'文件大小' => "Size",
'文件后缀' => 'Exts',
'存储方式' => 'Storage Type',
'清理重复' => 'Clear Replace',
'上传方式' => 'Upload Type',
'查看文件' => 'View',
'文件链接' => 'Link',
'秒传' => 'Speedy',
'普通' => 'Normal',
// 系统菜单管理
'图 标' => "Icon",
'添加菜单' => 'Add',
'禁用菜单' => 'Forbid',
'激活菜单' => "Resume",
'系统菜单' => 'Menus',
'菜单名称' => 'Name',
'跳转链接' => 'Link',
'上级菜单' => 'Parent',
'菜单链接' => 'Link',
'链接参数' => 'Params',
'权限节点' => "Node",
'菜单图标' => 'Icon',
'选择图标' => 'Select Icon',
// 系统权限管理
"授 权" => 'Auth',
'添加权限' => 'Add',
'权限名称' => "Name",
'权限描述' => 'Description',
'请输入权限描述' => 'Please enter a permission description',
// 系统用户管理
'账号名称' => 'Username',
'添加用户' => 'Add User',
'最后登录' => "Last Login Time",
'头像' => "Head",
'登录账号' => 'Username',
'用户名称' => 'Nickname',
'登录次数' => 'Login Times',
'系统用户' => 'System User',
'密 码' => 'Password',
'系统用户管理' => 'Users',
]);

View File

@ -1,7 +0,0 @@
* 系统模块初始化成功
* 这次更新了许多内容哦
* 少量更新修复部分BUG
* 修正系统用户搜索
* 系统菜单支持外链
* 修复任意下载问题
* 优化模块管理机制

View File

@ -1,3 +0,0 @@
* 数据展示优化
* 后台UI升级到最新版本
* 后台文件上传机制优化

View File

@ -1,10 +0,0 @@
* 增加 .flex 及相关样式 class
* 升级 layui-2.7 测试版本并增加了 flex 样式
* 增加 .help-images 图片上传容器,支持单图或多图
* 增加 .help-checks 多项选择器容器,支持 raido,checkbox
* 修复 layui.form.checkbox 特殊情况下的异常
* 修改 ui 主布局的阴影生成方式,修改 NotSelect 使用
* 修改内容主容器(.think-page-body>.layui-body) 样式
* 其他细节调整见 _config.less 文件及 _layout.less 文件
* 修改系统任务管理界面,简化数据统计及状态检测数据展示
* 优化 admin.js 加载 loding 显示,首次加载等待成功后再显示界面

View File

@ -1,12 +0,0 @@
* 框架核心服务层静态化重构,增加后台皮肤样式选择与配置
* 升级 layui 版本到 2.7.4,引入 jszip 插件,引入图片压缩插件
* 重构文件上传机制,增加文件记录,增加进度 tips 提示,增加图片压缩
* 引入 ckeditor5 富文本编辑器,同时保存原 ckeditor4 版本,可后台切换
* 全局改写 layTable 动态数据表格,支持静态刷新,同时兼容原生 table 方式
* 增加 CORS 跨域规则配置,配置参数置放于 config/app.php需要更新 ThinkLibrary
* 修复 layui.table 导致基于 ThinkPHP 模板输出自动转义 XSS 过滤机制失效,需要更新 ThinkLibrary
* 修复在模板中使用 {:input(NAME)} 取值而产生的 XSS 问题,模板取值更换为 {$get.NAME|default=''}
* 修复 ckeditor4 配置文件,禁用所有标签的 on 事件,阻止 XSS 脚本注入,需要更新 ckeditor4/config.js
* 修复上传入口的后缀验证,读取真实文件后缀与配置对比,阻止不合法的文件上传并存储到本地服务器
* 修改 JsonRpc 接口异常处理机制,当服务端绑定 Exception 时,客户端将能收到 error 消息及异常数据
* 修改 location.hash 访问机制,禁止直接访问外部 URL 资源链接,防止外部 XSS 攻击读取本地缓存数据

View File

@ -1,51 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免费声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// +----------------------------------------------------------------------
return [
'name' => 'admin',
'vers' => '2022.03.06.01',
'user' => '广州楚才信息科技有限公司',
'link' => 'https://www.cuci.cc',
'desc' => '系统管理模块,提供系统配置及应用模块管理。',
'menu' => [
[
'name' => '应用管理',
'subs' => [],
],
[
'name' => '系统管理',
'subs' => [
[
'name' => '系统配置',
'subs' => [
['name' => '系统参数配置', 'icon' => 'layui-icon layui-icon-set', 'path' => 'admin/config/index'],
['name' => '系统任务管理', 'icon' => 'layui-icon layui-icon-log', 'path' => 'admin/queue/index'],
['name' => '系统日志管理', 'icon' => 'layui-icon layui-icon-tabs', 'path' => 'admin/oplog/index'],
['name' => '应用模块管理', 'icon' => 'layui-icon layui-icon-app', 'path' => 'admin/module/index'],
['name' => '数据字典管理', 'icon' => 'layui-icon layui-icon-read', 'path' => 'admin/base/index'],
],
],
[
'name' => '权限管理',
'subs' => [
['name' => '访问权限管理', 'icon' => 'layui-icon layui-icon-vercode', 'path' => 'admin/auth/index'],
['name' => '系统用户管理', 'icon' => 'layui-icon layui-icon-username', 'path' => 'admin/user/index'],
],
],
],
],
],
];

View File

@ -1,6 +0,0 @@
{
"name": "admin",
"author": "Anyon",
"version": "2022.07.11.01",
"content": "ThinkAdmin 系统基础模块"
}

View File

@ -1,59 +1,52 @@
<?php
// +----------------------------------------------------------------------
// | ThinkAdmin
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2022 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// | 免声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
use think\admin\Library;
use think\admin\service\SystemService;
/*! 非开发环境,清理限制文件 */
if (Library::$sapp->request->isGet() && !SystemService::checkRunMode()) {
@unlink(Library::$sapp->getBasePath() . 'admin/controller/api/Update.php');
@unlink(Library::$sapp->getBasePath() . 'admin/route/demo.php');
@rmdir(Library::$sapp->getBasePath() . 'admin/route');
}
use think\admin\service\RuntimeService;
/*! 演示环境禁止操作路由绑定 */
if (SystemService::checkRunMode('demo')) {
Library::$sapp->route->post('index/pass', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改用户密码!']);
if (RuntimeService::check('demo')) {
Library::$sapp->route->post('index/pass', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止修改用户密码!')]);
});
Library::$sapp->route->post('config/system', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改系统配置!']);
Library::$sapp->route->post('config/system', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止修改系统配置!')]);
});
Library::$sapp->route->post('config/storage', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改系统配置!']);
Library::$sapp->route->post('config/storage', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止修改系统配置!')]);
});
Library::$sapp->route->post('menu', function () {
return json(['code' => 0, 'info' => '演示环境禁止给菜单排序!']);
Library::$sapp->route->post('menu', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止给菜单排序!')]);
});
Library::$sapp->route->post('menu/index', function () {
return json(['code' => 0, 'info' => '演示环境禁止给菜单排序!']);
Library::$sapp->route->post('menu/index', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止给菜单排序!')]);
});
Library::$sapp->route->post('menu/add', function () {
return json(['code' => 0, 'info' => '演示环境禁止添加菜单!']);
Library::$sapp->route->post('menu/add', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止添加菜单!')]);
});
Library::$sapp->route->post('menu/edit', function () {
return json(['code' => 0, 'info' => '演示环境禁止编辑菜单!']);
Library::$sapp->route->post('menu/edit', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止编辑菜单!')]);
});
Library::$sapp->route->post('menu/state', function () {
return json(['code' => 0, 'info' => '演示环境禁止禁用菜单!']);
Library::$sapp->route->post('menu/state', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止禁用菜单!')]);
});
Library::$sapp->route->post('menu/remove', function () {
return json(['code' => 0, 'info' => '演示环境禁止删除菜单!']);
Library::$sapp->route->post('menu/remove', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止删除菜单!')]);
});
Library::$sapp->route->post('user/pass', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改用户密码!']);
Library::$sapp->route->post('user/pass', static function () {
return json(['code' => 0, 'info' => lang('演示环境禁止修改密码!')]);
});
}

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,10 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
allowMime = JSON.parse('{$exts|raw}');
function UploadAdapter(elem, done) {
return new (function (elem, done, that) {
return new (function (elem, done) {
let that = this;
/*! 初始化变量 */
that = this;
this.option = {elem: $(elem), exts: [], mimes: []};
this.option.size = this.option.elem.data('size') || 0;
this.option.safe = this.option.elem.data('safe') ? 1 : 0;
@ -20,9 +20,11 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
this.option.cutHeight = parseInt(this.option.elem.data('cut-height') || '0');
/*! 查找表单元素, 如果没有找到将不会自动写值 */
if (!this.option.elem.data('input') && this.option.elem.data('field')) {
this.$input = $('input[name="' + this.option.elem.data('field') + '"]:not([type=file])');
this.option.elem.data('input', this.$input.size() > 0 ? this.$input.get(0) : null);
if (this.option.elem.data('input')) {
this.option.input = $(this.option.elem.data('input'))
} else if (this.option.elem.data('field')) {
this.option.input = $('input[name="' + this.option.elem.data('field') + '"]:not([type=file])');
this.option.elem.data('input', this.option.input.length > 0 ? this.option.input.get(0) : null);
}
/*! 文件选择筛选,使用 MIME 规则过滤文件列表 */
@ -32,7 +34,7 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
/*! 初始化上传组件 */
this.adapter = new Adapter(this.option, layui.upload.render({
url: '{:url("admin/api.upload/file")}', auto: false, elem: elem, accept: 'file', multiple: this.option.mult, exts: this.option.exts.join('|'), acceptMime: this.option.mimes.join(','), choose: function (obj) {
url: '{:url("admin/api.upload/file",[],false,true)}', auto: false, elem: elem, accept: 'file', multiple: this.option.mult, exts: this.option.exts.join('|'), acceptMime: this.option.mimes.join(','), choose: function (obj) {
obj.items = [], obj.files = obj.pushFile();
layui.each(obj.files, function (idx, file) {
obj.items.push(file);
@ -69,30 +71,38 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
// 文件推送
Adapter.prototype.upload = function (files, done) {
var that = this.init();
let that = this.init();
layui.each(files, function (index, file) {
that.count.total++, file.index = index, that.files[index] = file;
if (that.option.size && file.size > that.option.size) {
that.event('upload.error', {file: file}, file, '大小超限');
} else if (!that.option.hide) {
if (!that.option.hide && !file.notify) {
file.notify = new NotifyExtend(file);
}
}), layui.each(files, function (index, file) {
if (that.option.size && file.size > that.option.size) {
that.event('upload.error', {file: file}, file, '{:lang("大小超出限制!")}');
}
});
layui.each(files, function (index, file) {
// 禁传异常状态文件
if (typeof file.xstate === 'number' && file.xstate === -1) return;
// 图片限宽限高压缩
if (/^image\//.test(file.type) && (file.maxWidth + file.maxHeight + file.cutWidth + file.cutHeight > 0 || file.quality !== 1)) {
let isGif = /^image\/gif/.test(file.type);
if (!isGif && /^image\//.test(file.type) && (file.maxWidth + file.maxHeight + file.cutWidth + file.cutHeight > 0 || file.quality !== 1)) {
require(['compressor'], function (Compressor) {
new Compressor(file, {
quality: file.quality, resize: 'cover', width: file.cutWidth || 0, height: file.cutHeight || 0, maxWidth: file.maxWidth, maxHeight: file.maxHeight, success(blob) {
let options = {quality: file.quality, resize: 'cover'};
if (file.cutWidth) options.width = file.cutWidth;
if (file.cutHeight) options.height = file.cutHeight;
if (file.maxWidth) options.maxWidth = file.maxWidth;
if (file.maxHeight) options.maxHeight = file.maxHeight;
new Compressor(file, Object.assign(options, {
success(blob) {
blob.index = file.index, blob.notify = file.notify, blob.path = file.path, files[index] = blob;
that.hash(files[index]).then(function (file) {
that.event('upload.hash', file).request(file, done);
});
}, error: function () {
that.event('upload.error', {file: file}, file, '压缩失败');
that.event('upload.error', {file: file}, file, '{:lang("图片压缩失败")}');
}
});
}));
});
} else {
that.hash(file).then(function (file) {
@ -104,21 +114,28 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
// 文件上传
Adapter.prototype.request = function (file, done) {
var that = this, data = {key: file.xkey, safe: that.option.safe, uptype: that.option.type};
let that = this, data = {key: file.xkey, safe: that.option.safe, uptype: that.option.type};
data.size = file.size, data.name = file.name, data.hash = file.xmd5, data.mime = file.type, data.xext = file.xext;
jQuery.ajax("{:url('admin/api.upload/state')}", {
jQuery.ajax("{:url('admin/api.upload/state',[],false,true)}", {
data: data, method: 'post', success: function (ret) {
file.id = ret.data.id || 0, file.xurl = ret.data.url;
file.xsafe = ret.data.safe, file.xpath = ret.data.key, file.xtype = ret.data.uptype;
if (parseInt(ret.code) === 404) {
var uploader = {};
let uploader = {};
uploader.uptype = ret.data.uptype;
uploader.url = ret.data.server;
uploader.head = {};
uploader.form = new FormData();
uploader.form.append('key', ret.data.key);
uploader.form.append('safe', ret.data.safe);
uploader.form.append('uptype', ret.data.uptype);
if (ret.data.uptype === 'qiniu') {
uploader.form.append('token', ret.data.token);
} else if (ret.data.uptype === 'alist') {
uploader.type = 'put';
uploader.head['file-path'] = ret.data['filepath'];
uploader.head['authorization'] = ret.data['authorization'];
uploader.form = new FormData();
} else if (ret.data.uptype === 'alioss') {
uploader.form.append('policy', ret.data['policy']);
uploader.form.append('signature', ret.data['signature']);
@ -143,14 +160,14 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
uploader.form.append('Content-Disposition', 'inline;filename=' + encodeURIComponent(file.name));
}
uploader.form.append('file', file, file.name), jQuery.ajax({
url: uploader.url, data: uploader.form, type: 'post', xhr: function (xhr) {
xhrFields: {withCredentials: ret.data.uptype === 'local'}, headers: uploader.head, url: uploader.url, data: uploader.form, type: uploader.type || 'post', xhr: function (xhr) {
xhr = new XMLHttpRequest();
return xhr.upload.addEventListener('progress', function (event) {
file.xtotal = event.total, file.xloaded = event.loaded || 0;
that.progress((file.xloaded / file.xtotal * 100).toFixed(2), file)
}), xhr;
}, contentType: false, error: function () {
that.event('upload.error', {file: file}, file, '接口异常');
that.event('upload.error', {file: file}, file, '{:lang("上传接口异常")}');
}, processData: false, success: function (ret) {
// 兼容数据格式
if (typeof ret === 'string' && ret.length > 0) try {
@ -159,21 +176,23 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
console.log(e)
}
if (typeof ret !== 'object') {
ret = {code: 1, url: file.xurl, info: '上传成功'};
ret = {code: 1, url: file.xurl, info: '{:lang("文件上传成功")}'};
}
/*! 检查单个文件上传返回的结果 */
if (typeof ret === 'object' && ret.code < 1) {
that.event('upload.error', {file: file}, file, ret.info || '上传失败');
that.event('upload.error', {file: file}, file, ret.info || '{:lang("文件上传失败!")}');
} else if (uploader.uptype === 'alist' && parseInt(ret.code) !== 200) {
that.event('upload.error', {file: file}, file, ret.message || '{:lang("文件上传失败!")}');
} else {
that.done(ret, file.index, file, done, '上传成功');
that.done(ret, file.index, file, done, '{:lang("文件上传成功")}');
}
}
});
} else if (parseInt(ret.code) === 200) {
(file.xurl = ret.data.url), that.progress('100.00', file);
that.done({code: 1, url: file.xurl, info: file.xstats}, file.index, file, done, '秒传成功');
that.done({code: 1, url: file.xurl, info: file.xstats, data: {code: 200, url: file.xurl}}, file.index, file, done, '{:lang("文件秒传成功")}');
} else {
that.event('upload.error', {file: file}, file, ret.info || ret.error.message || '上传出错!');
that.event('upload.error', {file: file}, file, ret.info || ret.error.message || '{:lang("文件上传出错!")}');
}
}
});
@ -188,9 +207,9 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
// 上传结果
Adapter.prototype.done = function (ret, idx, file, done, message) {
/*! 检查单个文件上传返回的结果 */
if (ret.code < 1) return $.msg.tips(ret.info || '文件上传失败!');
if (typeof file.xurl !== 'string') return $.msg.tips('无效的文件上传对象!');
jQuery.post("{:url('admin/api.upload/done')}", {id: file.id, hash: file.xmd5});
if (ret.code < 1) return $.msg.tips(ret.info || '{:lang("文件上传失败!")}');
if (typeof file.xurl !== 'string') return $.msg.tips('{:lang("无效的文件上传对象!")}');
jQuery.post("{:url('admin/api.upload/done',[],false,true)}", {id: file.id, hash: file.xmd5});
/*! 单个文件上传成功结果处理 */
if (typeof done === 'function') {
done.call(this.option.elem, file.xurl, this.files['id']);
@ -203,9 +222,9 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
if (this.count.success + this.count.error >= this.count.total) {
this.option.hide || $.msg.close(this.loader);
if (this.option.mult > 0 && this.option.elem.data('input')) {
var urls = this.option.elem.data('input').value || [];
let urls = this.option.elem.data('input').value || [];
if (typeof urls === 'string') urls = urls.split('|');
for (var i in this.files) urls.push(this.files[i].xurl);
for (let i in this.files) urls.push(this.files[i].xurl);
$(this.option.elem.data('input')).val(urls.join('|')).trigger('change', this.files);
}
this.event('upload.complete', {file: this.files}, file).init().uploader && this.uploader.reload();
@ -223,6 +242,7 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
}
if (this.option.elem) {
this.option.elem.triggerHandler(name, data);
if (this.option.input) this.option.input.triggerHandler(name, data);
}
return this;
};
@ -233,11 +253,11 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
* @return {Promise}
*/
Adapter.prototype.hash = function (file) {
var defer = jQuery.Deferred();
let defer = jQuery.Deferred();
file.xext = file.name.indexOf('.') > -1 ? file.name.split('.').pop() : 'tmp';
/*! 兼容不能计算文件 HASH 的情况 */
var IsDate = '{$nameType|default=""}'.indexOf('date') > -1;
let IsDate = '{$nameType|default=""}'.indexOf('date') > -1;
if (!window.FileReader || IsDate) return jQuery.when((function (xmd5, chars) {
while (xmd5.length < 32) xmd5 += chars.charAt(Math.floor(Math.random() * chars.length));
return SetFileXdata(file, xmd5, 6), defer.promise();
@ -254,8 +274,8 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
}
function LoadNextChunk(file) {
var that = this, reader = new FileReader(), spark = new SparkMD5.ArrayBuffer();
var slice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
let that = this, reader = new FileReader(), spark = new SparkMD5.ArrayBuffer();
let slice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
this.chunkIdx = 0, this.chunkSize = 2097152, this.chunkTotal = Math.ceil(file.size / this.chunkSize);
reader.onload = function (event) {
spark.append(event.target.result);
@ -280,8 +300,8 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
* @constructor
*/
function NotifyExtend(file) {
var that = this;
this.notify = Notify.notify({width: 260, title: file.name, showProgress: true, description: '上传进度 <span data-upload-progress>0%</span>', type: 'default', position: 'top-right', closeTimeout: 0});
let that = this, message = "{:lang('上传进度 %s', ['<span data-upload-progress>0%</span>'])}";
this.notify = Notify.notify({width: 260, title: file.name, showProgress: true, description: message, type: 'default', position: 'top-right', closeTimeout: 0});
this.$elem = $(this.notify.notification.nodes);
this.$elem.find('.growl-notification__progress').addClass('is-visible');
this.$elem.find('.growl-notification__progress-bar').addClass('transition');
@ -290,12 +310,12 @@ define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
this.$elem.find('.growl-notification__progress-bar').css({width: number + '%'});
return this;
}, this.setError = function (message) {
this.$elem.find('.growl-notification__desc').html(message || '文件上传失败!');
this.$elem.find('.growl-notification__desc').html(message || '{:lang("文件上传失败!")}');
this.$elem.removeClass('growl-notification--default').addClass('growl-notification--error')
return this.close();
}, this.setSuccess = function (message) {
this.setProgress('100.00');
this.$elem.find('.growl-notification__desc').html(message || '文件上传成功!');
this.$elem.find('.growl-notification__desc').html(message || '{:lang("文件上传成功!")}');
this.$elem.removeClass('growl-notification--default').addClass('growl-notification--success');
return this.close();
}, this.close = function (timeout) {

View File

@ -1,152 +0,0 @@
<div class="image-dialog" id="ImageDialog">
<div class="image-dialog-head">
<div class="pull-left flex">
<input class="layui-input margin-right-5" v-model="keys" style="height:30px;line-height:30px" placeholder="请输入搜索关键词">
<a class="layui-btn layui-btn-sm layui-btn-normal" @click="search"> </a>
</div>
<div class="pull-right">
<a class="layui-btn layui-btn-sm" @click="uploadImage">上传图片</a>
</div>
</div>
<div class="image-dialog-body">
<div class="image-dialog-item" v-for="x in items" v-if="show">
<div class="uploadimage" :style="x.style" @click="setValue(x.xurl)"></div>
<p class="image-dialog-item-name layui-elip" v-text="x.name"></p>
</div>
</div>
<div class="image-dialog-foot">
<div id="ImageDialogPage" class="image-dialog-page"></div>
</div>
</div>
<style>
.image-dialog-head {
clear: both;
height: 30px;
padding: 10px 12px;
}
.image-dialog-body {
height: 470px;
background: #efefef;
padding-top: 12px;
padding-left: 12px;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.image-dialog-item {
position: relative;
margin-right: 12px;
margin-bottom: 12px;
}
.image-dialog-item-name {
left: 0;
right: 0;
bottom: 0;
color: #fff;
padding: 3px 10px;
position: absolute;
text-align: center;
line-height: 1.5em;
white-space: nowrap;
background: rgba(0, 0, 0, 0.6);
}
.image-dialog-item .uploadimage {
margin: 0;
width: 145px;
height: 145px;
}
.image-dialog-foot {
padding: 0 12px;
}
.image-dialog-page {
height: 50px;
text-align: center;
}
.image-dialog-page .layui-laypage a,
.image-dialog-page .layui-laypage span {
margin-bottom: 0;
}
</style>
<script>
require(['vue'], function (vue) {
var app = new vue({
el: '#ImageDialog',
data: {
page: 1,
limit: 15,
show: false,
$btn: null,
keys: '',
items: [],
},
created: function () {
this.$btn = $('#{$get.id|default=""}');
this.loadPage();
},
methods: {
// 创建分页工具条
addPage: function (count) {
var that = this;
this.show = true;
layui.laypage.render({
curr: this.page, count: count, limit: that.limit,
layout: ['count', 'prev', 'page', 'next', 'refresh'],
elem: 'ImageDialogPage', jump: function (obj, first) {
if (!first) that.loadPage(that.page = obj.curr);
},
});
},
// 搜索刷新数据
search: function () {
this.page = 1;
this.loadPage();
},
// 设置选择数据
setValue: function (url) {
this.$btn.trigger('choose', url);
if (this.$btn.data('input')) $(this.$btn.data('input')).val(url).trigger('change', url);
$('#ImageDialog').parents('.layui-layer-content').next().find('.layui-layer-close').trigger('click');
},
// 加载页面数据
loadPage: function () {
var that = this;
this.params = {page: this.page, limit: this.limit, output: 'layui.table', name: this.keys || ''};
$.form.load('{:url("image")}', this.params, 'get', function (ret) {
that.addPage(ret.count);
that.items = ret.data;
that.items.forEach(function (item) {
item.style = 'background-image:url(' + item.xurl + ')';
});
return false;
});
},
// 上传图片文件
uploadImage: function () {
$('[data-field="image-dialog-upload-input"]').click();
$('[name="image-dialog-upload-input"]').one('change', function (e) {
app.setValue(e.currentTarget.value);
// e.currentTarget.value 取上传后的值
// app.page = 1;
// app.loadPage();
});
},
}
});
});
</script>
<label class="layui-hide">
<!-- 图片上传组件 开始 -->
<input class="layui-input" name="image-dialog-upload-input">
<button class="layui-btn" data-file data-type="png,jpg,jpeg,gif" data-field="image-dialog-upload-input"></button>
<!-- 图片上传组件 结束 -->
</label>

View File

@ -1,25 +1,31 @@
<div class="image-dialog" id="ImageDialog">
<div class="image-dialog-head">
<div class="pull-left flex">
<input class="layui-input margin-right-5" v-model="keys" style="height:30px;line-height:30px" placeholder="请输入搜索关键词">
<a class="layui-btn layui-btn-sm layui-btn-normal" @click="search"> </a>
</div>
<label class="pull-left flex">
<input class="layui-input margin-right-5" v-model="keys" style="height:30px;line-height:30px" placeholder="{:lang('请输入搜索关键词')}">
<a class="layui-btn layui-btn-sm layui-btn-normal" @click="search">{:lang('搜 索')}</a>
</label>
<div class="pull-right">
<a class="layui-btn layui-btn-sm layui-btn-normal" @click="uploadImage">上传图片</a>
<a class="layui-btn layui-btn-sm layui-btn-normal" @click="uploadImage">{:lang('上传图片')}</a>
</div>
</div>
<div class="image-dialog-body">
<div class="image-dialog-item" v-for="x in list" @click="setItem(x)" style="display:none" v-show="show" :class="{'image-dialog-checked':x.checked}">
<div class="uploadimage" :style="x.style"></div>
<p class="image-dialog-item-name layui-elip" v-text="x.name"></p>
<span class="image-dialog-item-size">{{formatSize(x.size)}}</span>
<span class="image-dialog-item-type">{{x.xext.toUpperCase()}}</span>
<div class="image-dialog-item-tool">
<span class="image-dialog-item-type">{{x.xext.toUpperCase()}}</span>
<span class="image-dialog-item-size">{{formatSize(x.size)}}</span>
{if auth('admin/file/remove')}
<span class="layui-icon layui-icon-close image-dialog-item-close" @click.stop="remove(x)"></span>
{/if}
</div>
</div>
</div>
<div class="image-dialog-foot">
<div id="ImageDialogPage" class="image-dialog-page"></div>
<div id="ImageDialogButton layui-hide" class="image-dialog-button layui-btn layui-btn-normal" v-if="data.length>0" @click="confirm">
已选 {{ data.length }} 张,确认
{php} $tag = '{{data.length}}'; {/php}
{:lang('已选 %s 张,确认', [$tag])}
</div>
</div>
</div>
@ -53,7 +59,17 @@
this.urls = [];
this.data.forEach(function (file) {
app.setValue(file.xurl);
}), this.setInput();
});
this.setInput();
},
// 删除指定的图片
remove: function (x) {
$.msg.confirm('确认要移除这张图片吗?', function () {
$.form.load('{:url("admin/file/remove")}', {id: x.id}, 'POST', function (ret) {
ret.code > 0 ? app.loadPage() : $.msg.error(ret.info);
return app.$forceUpdate(), false;
})
})
},
// 格式文件大小
formatSize: function (size) {
@ -78,7 +94,8 @@
this.list.forEach(function (item) {
item.checked = !!app.idxs[item.id]
item.style = 'background-image:url(' + item.xurl + ')';
}), this.addPage(count);
});
this.addPage(count);
},
// 设置选择数据
setValue: function (xurl) {
@ -107,7 +124,7 @@
loadPage: function () {
this.params = {page: this.page, limit: this.limit, output: 'layui.table', name: this.keys || ''};
this.params.type = '{$get.type|default="gif,png,jpg,jpeg"}';
$.form.load('{:url("image")}', this.params, 'get', function (ret) {
$.form.load('{:url("image",[],false,true)}', this.params, 'get', function (ret) {
return app.setList(ret.data, ret.count), false;
});
},
@ -128,9 +145,17 @@
<label class="layui-hide" id="ImageDialogUploadLayout">
<!-- 图片上传组件 开始 -->
{if isset($get.file) && $get.file eq 'image'}
<button class="layui-btn" data-file data-path="{$get.path|default=''}" data-type="png,jpg,jpeg,gif"></button>
<button data-file="one" data-type="{$get.type|default='gif,png,jpg,jpeg'}"
data-path="{$get.path|default=''}" data-size="{$get.size|default=0}"
data-cut-width="{$get.cutWidth|default=0}" data-cut-height="{$get.cutHeight|default=0}"
data-max-width="{$get.maxWidth|default=0}" data-max-height="{$get.maxHeight|default=0}"
></button>
{else}
<button class="layui-btn" data-file="mul" data-path="{$get.path|default=''}" data-type="png,jpg,jpeg,gif"></button>
<button data-file="mul" data-type="{$get.type|default='gif,png,jpg,jpeg'}"
data-path="{$get.path|default=''}" data-size="{$get.size|default=0}"
data-cut-width="{$get.cutWidth|default=0}" data-cut-height="{$get.cutHeight|default=0}"
data-max-width="{$get.maxWidth|default=0}" data-max-height="{$get.maxHeight|default=0}"
></button>
{/if}
<!-- 图片上传组件 结束 -->
</label>

View File

@ -1,152 +0,0 @@
<div class="image-dialog" id="ImageDialog">
<div class="image-dialog-head">
<div class="pull-left flex">
<input class="layui-input margin-right-5" v-model="keys" style="height:30px;line-height:30px" placeholder="请输入搜索关键词">
<a class="layui-btn layui-btn-sm layui-btn-normal" @click="search"> </a>
</div>
<div class="pull-right">
<a class="layui-btn layui-btn-sm" @click="uploadImage">上传图片</a>
</div>
</div>
<div class="image-dialog-body">
<div class="image-dialog-item" v-for="x in items" v-if="show">
<div class="uploadimage" :style="x.style" @click="setValue(x.xurl)"></div>
<p class="image-dialog-item-name layui-elip" v-text="x.name"></p>
</div>
</div>
<div class="image-dialog-foot">
<div id="ImageDialogPage" class="image-dialog-page"></div>
</div>
</div>
<style>
.image-dialog-head {
clear: both;
height: 30px;
padding: 10px 12px;
}
.image-dialog-body {
height: 470px;
background: #efefef;
padding-top: 12px;
padding-left: 12px;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.image-dialog-item {
position: relative;
margin-right: 12px;
margin-bottom: 12px;
}
.image-dialog-item-name {
left: 0;
right: 0;
bottom: 0;
color: #fff;
padding: 3px 10px;
position: absolute;
text-align: center;
line-height: 1.5em;
white-space: nowrap;
background: rgba(0, 0, 0, 0.6);
}
.image-dialog-item .uploadimage {
margin: 0;
width: 145px;
height: 145px;
}
.image-dialog-foot {
padding: 0 12px;
}
.image-dialog-page {
height: 50px;
text-align: center;
}
.image-dialog-page .layui-laypage a,
.image-dialog-page .layui-laypage span {
margin-bottom: 0;
}
</style>
<script>
require(['vue'], function (vue) {
var app = new vue({
el: '#ImageDialog',
data: {
page: 1,
limit: 15,
show: false,
$btn: null,
keys: '',
items: [],
},
created: function () {
this.$btn = $('#{$get.id|default=""}');
this.loadPage();
},
methods: {
// 创建分页工具条
addPage: function (count) {
var that = this;
this.show = true;
layui.laypage.render({
curr: this.page, count: count, limit: that.limit,
layout: ['count', 'prev', 'page', 'next', 'refresh'],
elem: 'ImageDialogPage', jump: function (obj, first) {
if (!first) that.loadPage(that.page = obj.curr);
},
});
},
// 搜索刷新数据
search: function () {
this.page = 1;
this.loadPage();
},
// 设置选择数据
setValue: function (url) {
this.$btn.trigger('choose', url);
if (this.$btn.data('input')) $(this.$btn.data('input')).val(url).trigger('change', url);
$('#ImageDialog').parents('.layui-layer-content').next().find('.layui-layer-close').trigger('click');
},
// 加载页面数据
loadPage: function () {
var that = this;
this.params = {page: this.page, limit: this.limit, output: 'layui.table', name: this.keys || ''};
$.form.load('{:url("image")}', this.params, 'get', function (ret) {
that.addPage(ret.count);
that.items = ret.data;
that.items.forEach(function (item) {
item.style = 'background-image:url(' + item.xurl + ')';
});
return false;
});
},
// 上传图片文件
uploadImage: function () {
$('[data-field="image-dialog-upload-input"]').click();
$('[name="image-dialog-upload-input"]').one('change', function (e) {
app.setValue(e.currentTarget.value);
// e.currentTarget.value 取上传后的值
// app.page = 1;
// app.loadPage();
});
},
}
});
});
</script>
<label class="layui-hide">
<!-- 图片上传组件 开始 -->
<input class="layui-input" name="image-dialog-upload-input">
<button class="layui-btn" data-file data-type="png,jpg,jpeg,gif" data-field="image-dialog-upload-input"></button>
<!-- 图片上传组件 结束 -->
</label>

View File

@ -1,104 +0,0 @@
{extend name='main'}
{block name="content"}
<div class="think-box-shadow">
<ul id="zTree" class="ztree notselect"></ul>
<div class="hr-line-dashed"></div>
<div class="layui-form-item text-center">
<button class="layui-btn" data-submit-role type='button'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' onclick="window.history.back()">取消编辑</button>
</div>
</div>
{/block}
{block name="script"}
<script>
require(['jquery.ztree'], function () {
new function () {
var that = this;
this.data = {}, this.ztree = null, this.setting = {
view: {showLine: false, showIcon: false, dblClickExpand: false},
check: {enable: true, nocheck: false, chkboxType: {"Y": "ps", "N": "ps"}}, callback: {
beforeClick: function (id, node) {
node.children.length < 1 ? that.ztree.checkNode(node, !node.checked, null, true) : that.ztree.expandNode(node);
return false;
}
}
};
this.renderChildren = function (list, level) {
var childrens = [];
for (var i in list) childrens.push({
open: true, node: list[i].node, name: list[i].title || list[i].node,
checked: list[i].checked || false, children: this.renderChildren(list[i]._sub_, level + 1)
});
return childrens;
};
this.getData = function () {
$.form.load('{:url("apply")}', {id: '{$vo.id}', action: 'get'}, 'post', function (ret) {
return (that.data = that.renderChildren(ret.data, 1)), that.showTree(), false;
});
};
this.showTree = function () {
this.ztree = $.fn.zTree.init($("#zTree"), this.setting, this.data);
while (true) {
var nodes = this.ztree.getNodesByFilter(function (node) {
return (!node.node && node.children.length < 1);
});
if (nodes.length < 1) break;
for (var i in nodes) this.ztree.removeNode(nodes[i]);
}
};
this.submit = function () {
var nodes = [], data = this.ztree.getCheckedNodes(true);
for (var i in data) if (data[i].node) nodes.push(data[i].node);
$.form.load('{:url("apply")}', {id: '{$vo.id}', action: 'save', nodes: nodes}, 'post');
};
// 刷新数据
this.getData();
// 提交表单
$('[data-submit-role]').on('click', function () {
that.submit();
});
};
});
</script>
{/block}
{block name="style"}
<style>
ul.ztree li {
white-space: normal !important;
}
ul.ztree li span.button.switch {
margin-right: 5px;
}
ul.ztree ul ul li {
display: inline-block;
white-space: normal;
}
ul.ztree > li {
padding: 15px 25px 15px 15px;
}
ul.ztree > li > ul {
margin-top: 12px;
border-top: 1px solid rgba(0, 0, 0, .1);
}
ul.ztree > li > ul > li {
padding: 5px;
}
ul.ztree > li > a > span {
font-weight: 700;
font-size: 15px;
}
ul.ztree .level2 .button.level2 {
background: 0 0;
}
</style>
{/block}

View File

@ -1,25 +1,143 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card" data-table-id="RoleTable">
{extend name='main'}
<div class="layui-card-body padding-left-40">
{block name="button"}
<button data-target-submit class='layui-btn layui-btn-sm'>{:lang('保存数据')}</button>
<button data-target-backup class="layui-btn layui-btn-sm layui-btn-danger">{:lang('取消编辑')}</button>
{/block}
<label class="layui-form-item relative block">
<span class="help-label"><b>权限名称</b>Permission Name</span>
<input maxlength="100" class="layui-input" name="title" value='{$vo.title|default=""}' required placeholder="请输入权限名称">
<span class="help-block">访问权限名称需要保持不重复,在给用户授权时需要根据名称选择!</span>
</label>
{block name="content"}
<div class="think-box-shadow">
<form method="post" id="RoleForm" class="layui-form layui-card">
<div class="layui-card-body">
<label class="layui-form-item relative block">
<span class="help-label"><b>{:lang('权限名称')}</b>Auth Name</span>
<input maxlength="100" class="layui-input" name="title" value='{$vo.title|default=""}' required vali-name="{:lang('权限名称')}" placeholder="{:lang('请输入权限名称')}">
<span class="help-block">{:lang('访问权限名称需要保持不重复,在给用户授权时需要根据名称选择!')}</span>
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>{:lang('权限描述')}</b>Auth Remark</span>
<textarea placeholder="{:lang('请输入权限描述')}" maxlength="200" class="layui-textarea" name="desc">{$vo.desc|default=""}</textarea>
</label>
<div class="layui-form-item">
<span class="help-label label-required-prev"><b>{:lang('功能节点')}</b>Auth Nodes</span>
<ul id="zTree" class="ztree notselect"></ul>
</div>
<div class="hr-line-dashed"></div>
{notempty name='vo.id'}<input name="id" value="{$vo.id}" type="hidden"/>{/notempty}
<div class="layui-form-item text-center">
<button data-target-submit class="layui-btn">{:lang('保存数据')}</button>
<button data-target-backup class="layui-btn layui-btn-danger" type="button">{:lang('取消编辑')}</button>
</div>
</div>
</form>
</div>
{/block}
<label class="layui-form-item relative block">
<span class="help-label"><b>权限描述</b>Permission Description</span>
<textarea placeholder="请输入权限描述" maxlength="200" class="layui-textarea" name="desc">{$vo.desc|default=""}</textarea>
</label>
{block name="script"}
<script>
require(['jquery.ztree'], function () {
new function () {
let that = this;
this.data = {}, this.ztree = null, this.setting = {
view: {showLine: false, showIcon: false, dblClickExpand: false},
check: {enable: true, nocheck: false, chkboxType: {"Y": "ps", "N": "ps"}}, callback: {
beforeClick: function (id, node) {
node.children.length < 1 ? that.ztree.checkNode(node, !node.checked, true, true) : that.ztree.expandNode(node);
return false;
}
}
};
this.renderChildren = function (list, level) {
let childrens = [];
for (let i in list) childrens.push({
open: true, node: list[i].node, name: list[i].title || list[i].node,
checked: list[i].checked || false, children: this.renderChildren(list[i]._sub_, level + 1)
});
return childrens;
};
this.syncData = function () {
$.form.load('{:sysuri()}', {id: '{$vo.id|default=0}', action: 'json'}, 'post', function (ret) {
return (that.data = that.renderChildren(ret.data, 1)), that.showTree(), false;
});
};
this.showTree = function () {
this.ztree = $.fn.zTree.init($("#zTree"), this.setting, this.data);
while (true) {
let nodes = this.ztree.getNodesByFilter(function (node) {
return (!node.node && node.children.length < 1);
});
if (nodes.length < 1) break;
for (let i in nodes) this.ztree.removeNode(nodes[i]);
}
};
// 刷新数据
this.syncData();
// 监听表单提交
$('#RoleForm').vali(function (form) {
let data = that.ztree.getCheckedNodes(true);
Object.assign(form, {nodes: [], action: 'save'})
for (let i in data) if (data[i].node) form.nodes.push(data[i].node);
$.form.load('{:sysuri()}', form, 'post');
});
};
});
</script>
{/block}
</div>
{block name="style"}
<style>
ul.ztree li {
line-height: 24px;
white-space: normal !important;
}
<div class="hr-line-dashed"></div>
{notempty name='vo.id'}<input type='hidden' value='{$vo.id}' name='id'>{/notempty}
ul.ztree li span.button.switch {
margin-right: 5px;
}
<div class="layui-form-item text-center">
<button class="layui-btn" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
</div>
</form>
ul.ztree ul ul li {
display: inline-block;
white-space: normal;
}
ul.ztree > li {
border: 1px solid rgba(0, 0, 0, 0.10);
padding: 15px;
background: rgba(0, 0, 0, 0.05);
border-radius: 3px;
margin-bottom: 10px;
}
ul.ztree > li > ul {
padding: 10px;
margin-top: 10px;
background: rgba(0, 0, 0, 0.05);
border-radius: 3px;
}
ul.ztree > li > ul > li {
padding: 5px 0;
}
ul.ztree > li > a > span {
font-size: 15px;
font-weight: 700;
}
ul.ztree .level2 .button.level2 {
width: 0;
}
ul.ztree li span.button.noline_open {
background-position-y: -73px;
}
ul.ztree li span.button.noline_close {
background-position-y: -73px;
}
ul.ztree .level1 > .node_name {
font-weight: bold;
}
</style>
{/block}

View File

@ -2,18 +2,18 @@
{block name="button"}
<!--{if auth("add")}-->
<button data-modal='{:url("add")}' data-width="600px" data-table-id="RoleTable" class='layui-btn layui-btn-sm layui-btn-primary'>添加权限</button>
<button data-open='{:url("add")}' data-table-id="RoleTable" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('添加权限')}</button>
<!--{/if}-->
<!--{if auth("remove")}-->
<button data-action='{:url("remove")}' data-table-id="RoleTable" data-rule="id#{id}" data-confirm="确定要批量删除权限吗?" class='layui-btn layui-btn-sm layui-btn-primary'>批量删除</button>
<button data-action='{:url("remove")}' data-rule="id#{id}" data-table-id="RoleTable" data-confirm="{:lang('确定要批量删除权限吗?')}" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('批量删除')}</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="think-box-shadow">
{include file='auth/index_search'}
<table id="RoleTable" data-url="{:sysuri()}" data-target-search="form.form-search"></table>
<table id="RoleTable" data-url="{:request()->url()}" data-target-search="form.form-search"></table>
</div>
{/block}
@ -26,18 +26,18 @@
sort: {field: 'sort desc,id', type: 'desc'},
cols: [[
{checkbox: true, fixed: true},
{field: 'sort', title: '排序权重', align: 'center', width: 100, sort: true, templet: '#SortInputRoleTableTpl'},
{field: 'title', title: '权限名称', align: 'center', minWidth: 140},
{field: 'desc', title: '权限描述', align: 'center', minWidth: 110, templet: '<div>{{d.desc||"-"}}</div>'},
{field: 'status', title: '权限状态', align: 'center', minWidth: 110, templet: '#StatusSwitchRoleTableTpl'},
{field: 'create_at', title: '创建时间', align: 'center', minWidth: 170, sort: true},
{toolbar: '#ToolbarRoleTableTpl', title: '操作面板', align: 'center', minWidth: 210, fixed: 'right'},
{field: 'sort', title: '{:lang("排序权重")}', align: 'center', width: 100, sort: true, templet: '#SortInputRoleTableTpl'},
{field: 'title', title: '{:lang("权限名称")}', align: 'center', minWidth: 140},
{field: 'desc', title: '{:lang("权限描述")}', align: 'center', minWidth: 110, templet: '<div>{{d.desc||"-"}}</div>'},
{field: 'status', title: '{:lang("使用状态")}', align: 'center', minWidth: 110, templet: '#StatusSwitchRoleTableTpl'},
{field: 'create_at', title: '{:lang("创建时间")}', align: 'center', minWidth: 170, sort: true},
{toolbar: '#ToolbarRoleTableTpl', title: '{:lang("操作面板")}', align: 'center', minWidth: 210, fixed: 'right'},
]]
});
// 数据状态切换操作
layui.form.on('switch(StatusSwitchRoleTable)', function (obj) {
var data = {id: obj.value, status: obj.elem.checked > 0 ? 1 : 0};
let data = {id: obj.value, status: obj.elem.checked > 0 ? 1 : 0};
$.form.load("{:url('state')}", data, 'post', function (ret) {
if (ret.code < 1) $.msg.error(ret.info, 3, function () {
$('#RoleTable').trigger('reload');
@ -51,30 +51,26 @@
<!-- 列表排序权重模板 -->
<script type="text/html" id="SortInputRoleTableTpl">
<input type="number" min="0" data-blur-number="0" data-action-blur="{:sysuri()}" data-value="id#{{d.id}};action#sort;sort#{value}" data-loading="false" value="{{d.sort}}" class="layui-input text-center">
<input type="number" min="0" data-blur-number="0" data-action-blur="{:request()->url()}" data-value="id#{{d.id}};action#sort;sort#{value}" data-loading="false" value="{{d.sort}}" class="layui-input text-center">
</script>
<!-- 数据状态切换模板 -->
<script type="text/html" id="StatusSwitchRoleTableTpl">
<!--{if auth("state")}-->
<input type="checkbox" value="{{d.id}}" lay-skin="switch" lay-text="已激活|已禁用" lay-filter="StatusSwitchRoleTable" {{-d.status>0?'checked':''}}>
<input type="checkbox" value="{{d.id}}" lay-skin="switch" lay-text="{:lang('已激活')}|{:lang('已禁用')}" lay-filter="StatusSwitchRoleTable" {{-d.status>0?'checked':''}}>
<!--{else}-->
{{-d.status ? '<b class="color-green">已启用</b>' : '<b class="color-red">已禁用</b>'}}
{{-d.status ? '<b class="color-green">{:lang("已启用")}</b>' : '<b class="color-red">{:lang("已禁用")}</b>'}}
<!--{/if}-->
</script>
<!-- 数据操作工具条模板 -->
<script type="text/html" id="ToolbarRoleTableTpl">
<!--{if auth('edit')}-->
<a class="layui-btn layui-btn-primary layui-btn-sm" data-event-dbclick data-width="600px" data-title="编辑权限" data-modal='{:url("edit")}?id={{d.id}}'> </a>
<!--{/if}-->
<!--{if auth("apply")}-->
<a class="layui-btn layui-btn-normal layui-btn-sm" data-open='{:url("apply")}?id={{d.id}}'> </a>
<a class="layui-btn layui-btn-primary layui-btn-sm" data-open='{:url("edit")}?id={{d.id}}'>{:lang("编 辑")}</a>
<!--{/if}-->
<!--{if auth("remove")}-->
<a class="layui-btn layui-btn-danger layui-btn-sm" data-confirm="确定要删除权限吗?" data-action="{:url('remove')}" data-value="id#{{d.id}}"> </a>
<a class="layui-btn layui-btn-danger layui-btn-sm" data-action="{:url('remove')}" data-value="id#{{d.id}}" data-confirm="{:lang('确定要删除权限吗?')}">{:lang("删 除")}</a>
<!--{/if}-->
</script>
{/block}

View File

@ -1,27 +1,27 @@
<fieldset>
<legend>条件搜索</legend>
<legend>{:lang('条件搜索')}</legend>
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">权限名称</label>
<label class="layui-form-label">{:lang('权限名称')}</label>
<div class="layui-input-inline">
<input name="title" value="{$get.title|default=''}" placeholder="请输入权限名称" class="layui-input">
<input name="title" value="{$get.title|default=''}" placeholder="{:lang('请输入权限名称')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">权限描述</label>
<label class="layui-form-label">{:lang('权限描述')}</label>
<div class="layui-input-inline">
<input name="desc" value="{$get.desc|default=''}" placeholder="请输入权限描述" class="layui-input">
<input name="desc" value="{$get.desc|default=''}" placeholder="{:lang('请输入权限描述')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">使用状态</label>
<label class="layui-form-label">{:lang('使用状态')}</label>
<div class="layui-input-inline">
<select class="layui-select" name="status">
<option value=''>-- 全部状态 --</option>
{foreach ['已禁用的权限','已激活的权限'] as $k=>$v}
<option value=''>-- {:lang('全部')} --</option>
{foreach [lang('已禁用记录'),lang('已激活记录')] as $k=>$v}
{if isset($get.status) and $get.status eq $k.""}
<option selected value="{$k}">{$v}</option>
{else}
@ -32,14 +32,14 @@
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">创建时间</label>
<label class="layui-form-label">{:lang('创建时间')}</label>
<div class="layui-input-inline">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="请选择创建时间" class="layui-input">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="{:lang('请选择创建时间')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> {:lang('搜 索')}</button>
</div>
</form>
</fieldset>

View File

@ -3,7 +3,7 @@
<div class="layui-card-body padding-left-40">
<div class="layui-form-item label-required-prev">
<div class="help-label"><b>数据类型</b>DataType</div>
<div class="help-label"><b>数据类型</b>Data Type</div>
{if isset($vo.type)}
<label><input readonly value="{$vo.type|default=''}" class="layui-input think-bg-gray"></label>
{else}
@ -29,29 +29,29 @@
{/if}
<p class="help-block">请选择数据类型,数据创建后不能再次修改哦 ~</p>
<div id="DataTypeInput" class="layui-hide relative">
<input class="layui-input" maxlength="20" name="type" required placeholder="请输入数据类型" value="{$vo.type|default=''}">
<input class="layui-input" maxlength="20" name="type" required vali-name="数据类型" placeholder="请输入数据类型" value="{$vo.type|default=''}">
<p class="help-block">请输入新的数据类型,数据创建后不能再次修改哦 ~</p>
</div>
</div>
<label class="layui-form-item relative block">
<span class="help-label"><b>数据编码</b>DataCode</span>
<span class="help-label"><b>数据编码</b>Data Code</span>
{if isset($vo.code)}
<input readonly maxlength="100" class="layui-input think-bg-gray" name="code" value='{$vo.code|default=""}' required placeholder="请输入数据编码">
{else}
<input maxlength="100" class="layui-input" name="code" value='{$vo.code|default=""}' required placeholder="请输入数据编码">
<input maxlength="100" class="layui-input" name="code" value='{$vo.code|default=""}' required vali-name="数据编码" placeholder="请输入数据编码">
{/if}
<span class="help-block">请输入新的数据编码,数据创建后不能再次修改,同种数据类型的数据编码不能出现重复 ~</span>
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>数据名称</b>DataName</span>
<input maxlength="500" class="layui-input" name="name" value='{$vo.name|default=""}' required placeholder="请输入数据名称">
<span class="help-label"><b>数据名称</b>Data Name</span>
<input maxlength="500" class="layui-input" name="name" value='{$vo.name|default=""}' required vali-name="数据名称" placeholder="请输入数据名称">
<span class="help-block">请输入当前数据名称,请尽量保持名称的唯一性,数据名称尽量不要出现重复 ~</span>
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>数据内容</b>DataContent</span>
<span class="help-label"><b>数据内容</b>Data Content</span>
<textarea name="content" class="layui-textarea" placeholder="请输入数据内容">{$vo.content|default=''}</textarea>
</label>

View File

@ -21,7 +21,7 @@
</ul>
<div class="layui-tab-content">
{include file='base/index_search'}
<table id="BaseTable" data-url="{:sysuri()}" data-target-search="form.form-search"></table>
<table id="BaseTable" data-url="{:request()->url()}" data-target-search="form.form-search"></table>
</div>
</div>
{/block}
@ -36,7 +36,7 @@
where: {type: '{$type|default=""}'},
cols: [[
{checkbox: true, fixed: true},
{field: 'sort', title: '排序权重', width: 100, align: 'center', sort: true, templet: '#SortInputTpl'},
{field: 'sort', title: '{:lang("排序权重")}', width: 100, align: 'center', sort: true, templet: '#SortInputTpl'},
// {field: 'type', title: '数据类型', minWidth: 140, align: 'center'},
{field: 'code', title: '数据编码', width: '20%', align: 'left'},
{field: 'name', title: '数据名称', width: '30%', align: 'left'},

View File

@ -1,25 +1,25 @@
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">数据编码</label>
<label class="layui-form-label">{:lang('数据编码')}</label>
<div class="layui-input-inline">
<input name="code" value="{$get.code|default=''}" placeholder="请输入数据编码" class="layui-input">
<input name="code" value="{$get.code|default=''}" placeholder="{:lang('请输入数据编码')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">数据名称</label>
<label class="layui-form-label">{:lang('数据名称')}</label>
<div class="layui-input-inline">
<input name="name" value="{$get.name|default=''}" placeholder="请输入数据名称" class="layui-input">
<input name="name" value="{$get.name|default=''}" placeholder="{:lang('请输入数据名称')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">使用状态</label>
<label class="layui-form-label">{:lang('使用状态')}</label>
<div class="layui-input-inline">
<select class="layui-select" name="status">
<option value=''>-- 状态 --</option>
{foreach ['已禁用的权限','已激活的权限'] as $k=>$v}
<option value=''>-- {:lang('全部')} --</option>
{foreach [lang('已禁用记录'),lang('已激活记录')] as $k=>$v}
{if isset($get.status) and $get.status eq $k.""}
<option selected value="{$k}">{$v}</option>
{else}
@ -30,13 +30,13 @@
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">创建时间</label>
<label class="layui-form-label">{:lang('创建时间')}</label>
<div class="layui-input-inline">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="请选择创建时间" class="layui-input">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="{:lang('请选择创建时间')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> {:lang('搜 索')}</button>
</div>
</form>

View File

@ -1,55 +1,60 @@
{extend name="main"}
{block name="button"}
<!--{if isset($super) and $super}-->
<a class="layui-btn layui-btn-sm layui-btn-primary" data-load="{:url('admin/api.system/config')}">清理无效配置</a>
<!--{if isset($issuper) and $issuper}-->
<a class="layui-btn layui-btn-sm layui-btn-primary" data-load="{:url('admin/api.system/config')}">{:lang('清理无效配置')}</a>
<!--{/if}-->
<!--{if auth('system')}-->
<a class="layui-btn layui-btn-sm layui-btn-primary" data-modal="{:url('system')}">修改系统参数</a>
<a class="layui-btn layui-btn-sm layui-btn-primary" data-modal="{:url('system')}">{:lang('修改系统参数')}</a>
<!--{/if}-->
{/block}
{block name="content"}
<!--{notempty name='super'}-->
<!--{notempty name='issuper'}-->
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<b>运行模式</b><span class="color-desc font-s12 padding-left-5">Run Mode</span>
<span class="help-label">
<b style="color:#333!important;">{:lang('运行模式')}</b>( {:lang('仅超级管理员可配置')} )
</span>
</div>
<div class="layui-card-body">
<div class="layui-btn-group shadow-mini nowrap">
<!--{if $app->isDebug()}-->
<a class="layui-btn layui-btn-sm layui-btn-active">以开发模式运行</a>
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="确定要切换到生产模式运行吗?" data-load="{:url('admin/api.system/debug')}?state=1">以生产模式运行</a>
<a class="layui-btn layui-btn-sm layui-btn-active">{:lang('以开发模式运行')}</a>
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="{:lang('确定要切换到生产模式运行吗?')}" data-load="{:url('admin/api.system/debug')}?state=1">{:lang('以生产模式运行')}</a>
<!--{else}-->
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="确定要切换到开发模式运行吗?" data-load="{:url('admin/api.system/debug')}?state=0">以开发模式运行</a>
<a class="layui-btn layui-btn-sm layui-btn-active">以生产模式运行</a>
<a class="layui-btn layui-btn-sm layui-btn-primary" data-confirm="{:lang('确定要切换到开发模式运行吗?')}" data-load="{:url('admin/api.system/debug')}?state=0">{:lang('以开发模式运行')}</a>
<a class="layui-btn layui-btn-sm layui-btn-active">{:lang('以生产模式运行')}</a>
<!--{/if}-->
</div>
<div class="margin-top-20">
<p><b>开发模式</b>开发人员或在功能调试时使用,系统异常时会显示详细的错误信息,同时还会记录操作日志及数据库 SQL 语句信息。</p>
<p><b>生产模式</b>项目正式部署上线后使用,系统异常时统一显示 {:config('app.error_message')}”,只记录重要的异常日志信息,强烈推荐上线后使用此模式。</p>
<p><b>{:lang('开发模式')}</b>{:lang('开发人员或在功能调试时使用,系统异常时会显示详细的错误信息,同时还会记录操作日志及数据库 SQL 语句信息。')}</p>
<p><b>{:lang('生产模式')}</b>{:lang('项目正式部署上线后使用,系统异常时统一显示 “%s”只记录重要的异常日志信息强烈推荐上线后使用此模式。',[config('app.error_message')])}</p>
</div>
</div>
</div>
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<b>富编辑器</b><span class="color-desc font-s12 padding-left-5">Rich Text Editor</span>
<span class="help-label">
<b style="color:#333!important;">{:lang('富编辑器')}</b>( {:lang('仅超级管理员可配置')} )
</span>
</div>
<div class="layui-card-body layui-clear">
<div class="layui-btn-group shadow-mini nowrap">
{if !in_array(sysconf('base.editor'),['ckeditor4','ckeditor5','auto'])}{php}sysconf('base.editor','ckeditor4');{/php}{/if}
{foreach ['ckeditor4'=>'CKEditor4','ckeditor5'=>'CKEditor5','auto'=>'自适应模式'] as $k => $v}{if sysconf('base.editor') eq $k}
<div class="layui-btn-group shadow-mini">
{if !in_array(sysconf('base.editor'),['ckeditor4','ckeditor5','wangEditor','auto'])}{php}sysconf('base.editor','ckeditor4');{/php}{/if}
{foreach ['ckeditor4'=>'CKEditor4','ckeditor5'=>'CKEditor5','wangEditor'=>'wangEditor','auto'=>lang('自适应模式')] as $k => $v}{if sysconf('base.editor') eq $k}
{if auth('storage')}<a data-title="配置{$v}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
{else}
{if auth('storage')}<a data-title="配置{$v}" data-action="{:url('admin/api.system/editor')}" data-value="editor#{$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
{/if}{/foreach}
</div>
<div class="margin-top-20 nowrap full-width pull-left">
<p><b>CKEditor4</b>:旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。</p>
<p><b>CKEditor5</b>:新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。</p>
<p><b>自适应模式</b>:优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。</p>
<div class="margin-top-20 full-width pull-left">
<p><b>CKEditor4</b>{:lang('旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。')}</p>
<p><b>CKEditor5</b>{:lang('新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。')}</p>
<p><b>wangEditor</b>{:lang('国产优质富文本编辑器对于小程序及App内容支持会更友好推荐使用。')}</p>
<p><b>{:lang('自适应模式')}</b>{:lang('优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。')}</p>
</div>
</div>
</div>
@ -57,84 +62,93 @@
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<b>存储引擎</b><span class="color-desc font-s12 padding-left-5">Storage Engine</span>
<span class="help-label">
<b style="color:#333!important;">{:lang('存储引擎')}</b>( {:lang('文件默认存储方式')} )
</span>
</div>
<!-- 初始化存储配置 -->
{if !sysconf('storage.type')}{php}sysconf('storage.type','local');{/php}{/if}
{if !sysconf('storage.link_type')}{php}sysconf('storage.link_type','none');{/php}{/if}
{if !sysconf('storage.name_type')}{php}sysconf('storage.name_type','xmd5');{/php}{/if}
{if !sysconf('storage.allow_exts')}{php}sysconf('storage.allow_exts','doc,gif,ico,jpg,mp3,mp4,p12,pem,png,rar,xls,xlsx');{/php}{/if}
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','http');{/php}{/if}
<div class="layui-card-body layui-clear">
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','follow');{/php}{/if}
<div class="layui-card-body">
<div class="layui-btn-group shadow-mini nowrap">
{foreach ['local'=>'本地服务器存储','qiniu'=>'七牛云对象存储','upyun'=>'又拍云USS存储','alioss'=>'阿里云OSS存储','txcos'=>'腾讯云COS存储'] as $k => $v}{if sysconf('storage.type') eq $k}
{foreach $files as $k => $v}{if sysconf('storage.type') eq $k}
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
{else}
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
{/if}{/foreach}
</div>
<div class="margin-top-20 nowrap full-width pull-left">
<p><b>本地服务器存储</b>:文件直接上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。</p>
<p><b>七牛云对象存储</b>:文件直接上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
<p><b>又拍云USS存储</b>:文件直接上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
<p><b>阿里云OSS存储</b>:文件直接上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
<p><b>腾讯云COS存储</b>:文件直接上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
<div class="margin-top-20 full-width">
<p><b>{:lang('本地服务器存储')}</b>{:lang('文件上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。')}</p>
<p><b>{:lang('自建Alist存储')}</b>{:lang('文件上传到 Alist 存储的服务器或云存储空间,根据服务配置可支持大文件上传,不占用本身服务器空间及服务器带宽流量。')}</p>
<p><b>{:lang('七牛云对象存储')}</b>{:lang('文件上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
<p><b>{:lang('又拍云USS存储')}</b>{:lang('文件上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
<p><b>{:lang('阿里云OSS存储')}</b>{:lang('文件上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
<p><b>{:lang('腾讯云COS存储')}</b>{:lang('文件上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}</p>
</div>
</div>
</div>
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<b>系统参数</b><span class="color-desc font-s12 padding-left-5">System Parameter</span>
<span class="help-label">
<b style="color:#333!important;">{:lang('系统参数')}</b>( {:lang('当前系统配置参数')} )
</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<div class="help-label"><b>网站名称</b>Website</div>
<div class="help-label"><b>{:lang('网站名称')}</b>Website</div>
<label class="relative block">
<input readonly value="{:sysconf('site_name')}" class="layui-input layui-bg-gray">
<a data-copy="{:sysconf('site_name')}" class="layui-icon layui-icon-release input-right-icon"></a>
</label>
<div class="help-block">网站名称及网站图标,将显示在浏览器的标签上。</div>
<div class="help-block">{:lang('网站名称及网站图标,将显示在浏览器的标签上。')}</div>
</div>
<div class="layui-form-item">
<div class="help-label"><b>管理程序名称</b>Name</div>
<div class="help-label"><b>{:lang('管理程序名称')}</b>Name</div>
<label class="relative block">
<input readonly value="{:sysconf('app_name')}" class="layui-input layui-bg-gray">
<a data-copy="{:sysconf('app_name')}" class="layui-icon layui-icon-release input-right-icon"></a>
</label>
<div class="help-block">管理程序名称,将显示在后台左上角标题。</div>
<div class="help-block">{:lang('管理程序名称,将显示在后台左上角标题。')}</div>
</div>
<div class="layui-form-item">
<div class="help-label"><b>管理程序版本</b>Version</div>
<div class="help-label"><b>{:lang('管理程序版本')}</b>Version</div>
<label class="relative block">
<input readonly value="{:sysconf('app_version')}" class="layui-input layui-bg-gray">
<a data-copy="{:sysconf('app_version')}" class="layui-icon layui-icon-release input-right-icon"></a>
</label>
<div class="help-block">管理程序版本,将显示在后台左上角标题。</div>
<div class="help-block">{:lang('管理程序版本,将显示在后台左上角标题。')}</div>
</div>
<div class="layui-form-item">
<div class="help-label"><b>公网备案号</b>Baian</div>
<div class="help-label"><b>{:lang('公安备案号')}</b>Beian</div>
<label class="relative block">
<input readonly value="{:sysconf('beian')?:'-'}" class="layui-input layui-bg-gray">
<a data-copy="{:sysconf('beian')}" class="layui-icon layui-icon-release input-right-icon"></a>
</label>
<p class="help-block">公网备案号,可以在 <a target="_blank" href="https://beian.miit.gov.cn">备案管理中心</a> 查询获取,将在登录页面下面显示。</p>
<p class="help-block">
{:lang('公安备案号,可以在 %s 查询获取,将在登录页面下面显示。',['<a target="_blank" href="https://www.beian.gov.cn/portal/registerSystemInfo">www.beian.gov.cn</a>'])}
</p>
</div>
<div class="layui-form-item">
<div class="help-label"><b>网站备案号</b>Miitbeian</div>
<div class="help-label"><b>{:lang('网站备案号')}</b>Miitbeian</div>
<label class="relative block">
<input readonly value="{:sysconf('miitbeian')?:'-'}" class="layui-input layui-bg-gray">
<a data-copy="{:sysconf('miitbeian')}" class="layui-icon layui-icon-release input-right-icon"></a>
</label>
<div class="help-block">网站备案号,可以在 <a target="_blank" href="https://beian.miit.gov.cn">备案管理中心</a> 查询获取,将显示在登录页面下面。</div>
<div class="help-block">
{:lang('网站备案号,可以在 %s 查询获取,将显示在登录页面下面。',['<a target="_blank" href="https://beian.miit.gov.cn">beian.miit.gov.cn</a>'])}
</div>
</div>
<div class="layui-form-item">
<div class="help-label"><b>网站版权信息</b>Copyright</div>
<div class="help-label"><b>{:lang('网站版权信息')}</b>Copyright</div>
<label class="relative block">
<input readonly value="{:sysconf('site_copy')}" class="layui-input layui-bg-gray">
<a data-copy="{:sysconf('site_copy')}" class="layui-icon layui-icon-release input-right-icon"></a>
</label>
<div class="help-block">网站版权信息,在后台登录页面显示版本信息并链接到备案到信息备案管理系统。</div>
<div class="help-block">{:lang('网站版权信息,在后台登录页面显示版本信息并链接到备案到信息备案管理系统。')}</div>
</div>
</div>
</div>
@ -142,38 +156,79 @@
<!--{if $app->isDebug()}-->
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<b>系统信息</b><span class="color-desc font-s12 padding-left-5">System Information</span>
<span class="help-label">
<b style="color:#333!important;">{:lang('系统信息')}</b>( {:lang('仅开发模式可见')} )
</span>
</div>
<div class="layui-card-body">
<table class="layui-table" lay-even>
<tbody>
<tr>
<th class="nowrap text-center">核心框架</th>
<td><a target="_blank" href="https://www.thinkphp.cn">ThinkPHP Version {$app->version()}</a></td>
<th class="nowrap text-center">{:lang('核心框架')}</th>
<td><a target="_blank" href="https://www.thinkphp.cn">ThinkPHP Version {$framework.version|default='None'}</a></td>
</tr>
<tr>
<th class="nowrap text-center">管理程序</th>
<td><a target="_blank" href="https://thinkadmin.top">ThinkAdmin Version {$version|default='6.0.0'}</a></td>
<th class="nowrap text-center">{:lang('平台框架')}</th>
<td><a target="_blank" href="https://thinkadmin.top">ThinkAdmin Version {$thinkadmin.version|default='6.0.0'}</a></td>
</tr>
<tr>
<th class="nowrap text-center">服务器信息</th>
<th class="nowrap text-center">{:lang('操作系统')}</th>
<td>{:php_uname()}</td>
</tr>
<tr>
<th class="nowrap text-center">服务器软件</th>
<td>{$request->server('SERVER_SOFTWARE',php_sapi_name())}</td>
<th class="nowrap text-center">{:lang('运行环境')}</th>
<td>{:ucfirst($request->server('SERVER_SOFTWARE',php_sapi_name()))} & PHP {$Think.const.PHP_VERSION} & {:ucfirst(app()->db->connect()->getConfig('type'))}</td>
</tr>
<!-- {notempty name='systemid'} -->
<tr>
<th class="nowrap text-center">PHP 版本</th>
<td>PHP Version {$Think.const.PHP_VERSION}</td>
</tr>
<tr>
<th class="nowrap text-center">MySQL 版本</th>
<td>MySQL Version {$app->db->query('SELECT VERSION()')[0]['VERSION()']}</td>
<th class="nowrap text-center">{:lang('系统序号')}</th>
<td>{$systemid|default=''}</td>
</tr>
<!-- {/notempty} -->
</tbody>
</table>
</div>
</div>
{notempty name='plugins'}
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<span class="help-label">
<b style="color:#333!important;">{:lang('应用插件')}</b>( {:lang('仅开发模式可见')} )
</span>
</div>
<div class="layui-card-body">
<table class="layui-table" lay-even>
<thead>
<tr>
<th class="nowrap text-center">{:lang('应用名称')}</th>
<th class="nowrap text-center">{:lang('插件名称')}</th>
<th class="nowrap text-left">{:lang('插件包名')}</th>
<th class="nowrap text-center">{:lang('插件版本')}</th>
<th class="nowrap text-center">{:lang('授权协议')}</th>
</tr>
</thead>
<tbody>
{foreach $plugins as $key=>$plugin}
<tr>
<td class="nowrap text-center">{$key}</td>
<td class="nowrap text-center">{$plugin.name|lang}</td>
<td class="nowrap text-left">
{if empty($plugin.install.document)}{$plugin.package}
{else}<a target="_blank" href="{$plugin.install.document}">{$plugin.package}</a>{/if}
</td>
<td class="nowrap text-center">{$plugin.install.version|default='unknow'}</td>
<td class="nowrap text-center">
{if empty($plugin.install.license)} -
{elseif is_array($plugin.install.license)}{$plugin.install.license|join='、',###}
{else}{$plugin.install.license|default='-'}{/if}
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
{/notempty}
<!--{/if}-->
{/block}

View File

@ -3,15 +3,17 @@
<b class="color-green">命名方式</b><br><span class="nowrap color-desc">NameType</span>
</label>
<div class="layui-input-block">
{foreach ['xmd5'=>'文件哈希值 ( 支持秒传 )','date'=>'日期+随机 ( 普通上传 )'] as $k=>$v}
<label class="think-radio notselect">
{if sysconf('storage.name_type') eq $k}
<input checked type="radio" name="storage.name_type" value="{$k}" lay-ignore> {$v}
{else}
<input type="radio" name="storage.name_type" value="{$k}" lay-ignore> {$v}
{/if}
</label>
{/foreach}
<div class="layui-input help-checks">
{foreach ['xmd5'=>'文件哈希值 ( 支持秒传 )','date'=>'日期+随机 ( 普通上传 )'] as $k=>$v}
<label class="think-radio notselect">
{if sysconf('storage.name_type') eq $k}
<input checked type="radio" name="storage.name_type" value="{$k}" lay-ignore> {$v}
{else}
<input type="radio" name="storage.name_type" value="{$k}" lay-ignore> {$v}
{/if}
</label>
{/foreach}
</div>
<p class="help-block">类型为“文件哈希”时可以实现文件秒传功能,同一个文件只需上传一次节省存储空间,推荐使用。</p>
</div>
</div>
@ -21,15 +23,17 @@
<b class="color-green">链接类型</b><br><span class="nowrap color-desc">LinkType</span>
</label>
<div class="layui-input-block">
{foreach ['none'=>'简洁链接','full'=>'完整链接','none+compress'=>'简洁并压缩图片','full+compress'=>'完整并压缩图片'] as $k=>$v}
<label class="think-radio notselect">
{if sysconf('storage.link_type') eq $k}
<input checked type="radio" name="storage.link_type" value="{$k}" lay-ignore> {$v}
{else}
<input type="radio" name="storage.link_type" value="{$k}" lay-ignore> {$v}
{/if}
</label>
{/foreach}
<div class="layui-input help-checks">
{foreach ['none'=>'简洁链接','full'=>'完整链接','none+compress'=>'简洁并压缩图片','full+compress'=>'完整并压缩图片'] as $k=>$v}
<label class="think-radio notselect">
{if sysconf('storage.link_type') eq $k}
<input checked type="radio" name="storage.link_type" value="{$k}" lay-ignore> {$v}
{else}
<input type="radio" name="storage.link_type" value="{$k}" lay-ignore> {$v}
{/if}
</label>
{/foreach}
</div>
<p class="help-block">类型为“简洁链接”时链接将只返回 hash 地址,而“完整链接”将携带参数保留文件名,图片压缩功能云平台会单独收费。</p>
</div>
</div>
@ -39,7 +43,7 @@
<b class="color-green">允许类型</b><br><span class="nowrap color-desc">AllowExts</span>
</label>
<div class="layui-input-block">
<input id="storage.allow_exts" type="text" name="storage.allow_exts" required value="{:sysconf('storage.allow_exts')}" placeholder="请输入系统文件上传后缀" class="layui-input">
<p class="help-block">设置系统允许上传文件后缀多个以英文逗号隔开如png,jpg,rar,doc设置允许上传的后缀</p>
<input id="storage.allow_exts" type="text" name="storage.allow_exts" value="{:sysconf('storage.allow_exts')}" required vali-name="文件后缀" placeholder="请输入系统文件上传后缀" class="layui-input">
<p class="help-block">设置系统允许上传文件后缀多个以英文逗号隔开如png,jpg,rar,doc包含在设置内的文件后缀将不被允许上传。</p>
</div>
</div>

View File

@ -14,15 +14,17 @@
</label>
<div class="layui-input-block">
{if !sysconf('storage.alioss_http_protocol')}{php}sysconf('storage.alioss_http_protocol','http');{/php}{/if}
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.alioss_http_protocol') eq $protocol}
<input checked type="radio" name="storage.alioss_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.alioss_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
<div class="layui-input help-checks">
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.alioss_http_protocol') eq $protocol}
<input checked type="radio" name="storage.alioss_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.alioss_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
</div>
<p class="help-block">阿里云OSS存储访问协议其中 HTTPS 需要配置证书才能使用AUTO 为相对协议)</p>
</div>
</div>
@ -49,7 +51,7 @@
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_bucket" type="text" name="storage.alioss_bucket" required value="{:sysconf('storage.alioss_bucket')}" placeholder="请输入阿里云OSS存储 Bucket (空间名称)" class="layui-input">
<input id="storage.alioss_bucket" type="text" name="storage.alioss_bucket" value="{:sysconf('storage.alioss_bucket')}" required vali-name="空间名称" placeholder="请输入阿里云OSS存储 Bucket (空间名称)" class="layui-input">
<p class="help-block">填写阿里云OSS存储空间名称think-admin-oss需要是全区唯一的值不存在时会自动创建</p>
</div>
</div>
@ -59,8 +61,8 @@
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_http_domain" type="text" name="storage.alioss_http_domain" required value="{:sysconf('storage.alioss_http_domain')}" placeholder="请输入阿里云OSS存储 Domain (访问域名)" class="layui-input">
<p class="help-block">填写阿里云OSS存储外部访问域名static.alioss.thinkadmin.top</p>
<input id="storage.alioss_http_domain" type="text" name="storage.alioss_http_domain" value="{:sysconf('storage.alioss_http_domain')}" required vali-name="访问域名" placeholder="请输入阿里云OSS存储 Domain (访问域名)" class="layui-input">
<p class="help-block">填写阿里云OSS存储外部访问域名不需要填写访问协议,static.alioss.thinkadmin.top</p>
</div>
</div>
@ -69,7 +71,7 @@
<b class="color-green">访问密钥</b><br><span class="nowrap color-desc">AccessKey</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_access_key" type="text" name="storage.alioss_access_key" required value="{:sysconf('storage.alioss_access_key')}" placeholder="请输入阿里云OSS存储 AccessKey (访问密钥)" class="layui-input">
<input id="storage.alioss_access_key" type="text" name="storage.alioss_access_key" value="{:sysconf('storage.alioss_access_key')}" required vali-name="访问密钥" placeholder="请输入阿里云OSS存储 AccessKey (访问密钥)" class="layui-input">
<p class="help-block">可以在 [ 阿里云 > 个人中心 ] 设置并获取到访问密钥</p>
</div>
</div>
@ -79,7 +81,7 @@
<b class="color-green">安全密钥</b><br><span class="nowrap color-desc">SecretKey</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_secret_key" type="text" name="storage.alioss_secret_key" required value="{:sysconf('storage.alioss_secret_key')}" maxlength="43" placeholder="请输入阿里云OSS存储 SecretKey (安全密钥)" class="layui-input">
<input id="storage.alioss_secret_key" type="text" name="storage.alioss_secret_key" value="{:sysconf('storage.alioss_secret_key')}" maxlength="43" required vali-name="安全密钥" placeholder="请输入阿里云OSS存储 SecretKey (安全密钥)" class="layui-input">
<p class="help-block">可以在 [ 阿里云 > 个人中心 ] 设置并获取到安全密钥</p>
</div>
</div>

View File

@ -0,0 +1,81 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
<div class="layui-card-body padding-top-20">
<div class="color-text layui-code text-center layui-bg-gray" style="border-left-width:1px;margin:0 0 15px 40px">
<p class="margin-bottom-5 font-w7">文件将上传到 <a target="_blank" href="https://alist.nn.ci/zh/">Alist</a> 自建存储,需要自行搭建 <a target="_blank" href="https://alist.nn.ci/zh/">Alist</a> 存储服务器。</p>
<p>Alist 是一个支持多种存储的文件列表程序,可将各种云盘及本地磁盘资源进行整合。</p>
<p>建议不要开放匿名用户访问,尽量使用独立账号管理,需要关闭 “签名所有” 让文件可以直接访问。</p>
</div>
{include file='config/storage-0'}
<div class="layui-form-item">
<label class="layui-form-label label-required">
<b class="color-green">访问协议</b><br><span class="nowrap color-desc">Protocol</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.alist_http_protocol')}{php}sysconf('storage.alist_http_protocol','http');{/php}{/if}
<div class="layui-input help-checks">
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.alist_http_protocol') eq $protocol}
<input checked type="radio" name="storage.alist_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.alist_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
</div>
<p class="help-block">请选择 Alist 存储访问协议,其中 HTTPS 需要配置证书才能使用( AUTO 为相对协议 </p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alist_http_domain">
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.alist_http_domain" type="text" name="storage.alist_http_domain" value="{:sysconf('storage.alist_http_domain')}" required vali-name="访问域名" placeholder="请输入 Alist 存储的访问域名" class="layui-input">
<p class="help-block">请填写 Alist 存储访问域名不需要填写访问协议storage.thinkadmin.top</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alist_savepath">
<b class="color-green">存储目录</b><br><span class="nowrap color-desc">Directory</span>
</label>
<div class="layui-input-block">
<input id="storage.alist_savepath" type="text" name="storage.alist_savepath" value="{:sysconf('storage.alist_savepath')}" required vali-name="存储目录" placeholder="请输入 Alist 存储目录" class="layui-input">
<p class="help-block">请填写 Alist 用户基本目录的相对存储位置,填写 / 表示用户基本目录( 需要拥有读写权限 </p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alist_username">
<b class="color-green">用户账号</b><br><span class="nowrap color-desc">Username</span>
</label>
<div class="layui-input-block">
<input id="storage.alist_username" name="storage.alist_username" value="{:sysconf('storage.alist_username')}" maxlength="100" required vali-name="用户账号" placeholder="请输入 Alist 存储的用户账号" class="layui-input">
<p class="help-block">请填写 Alist 用户账号,注意此账号需要拥有上面存储目录的访问权限。</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alist_password">
<b class="color-green">用户密码</b><br><span class="nowrap color-desc">Password</span>
</label>
<div class="layui-input-block">
<input id="storage.alist_password" name="storage.alist_password" value="{:sysconf('storage.alist_password')}" maxlength="100" required vali-name="用户密码" placeholder="请输入 Alist 存储的用户密码" class="layui-input">
<p class="help-block">请填写 Alist 用户登录密码,用于生成文件上传的接口认证令牌,如果填写错误将无法上传文件。</p>
</div>
</div>
<div class="hr-line-dashed margin-left-40"></div>
<input type="hidden" name="storage.type" value="alist">
<div class="layui-form-item text-center padding-left-40">
<button class="layui-btn" type="submit">保存配置</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消修改吗?" data-close>取消修改</button>
</div>
</div>
</form>

View File

@ -13,16 +13,18 @@
<b class="color-green">访问协议</b><br><span class="nowrap color-desc">Protocol</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','http');{/php}{/if}
{foreach ['follow'=>'FOLLOW','http'=>'HTTP','https'=>'HTTPS','path'=>'PATH','auto'=>'AUTO'] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.local_http_protocol') eq $protocol}
<input checked type="radio" name="storage.local_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.local_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','follow');{/php}{/if}
<div class="layui-input help-checks">
{foreach ['follow'=>'FOLLOW','http'=>'HTTP','https'=>'HTTPS','path'=>'PATH','auto'=>'AUTO'] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.local_http_protocol') eq $protocol}
<input checked type="radio" name="storage.local_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.local_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
</div>
<p class="help-block">本地存储访问协议,其中 HTTPS 需要配置证书才能使用( FOLLOW 跟随系统PATH 文件路径AUTO 相对协议 </p>
</div>
</div>
@ -33,7 +35,7 @@
</label>
<div class="layui-input-block">
<input id="storage.local_http_domain" type="text" name="storage.local_http_domain" value="{:sysconf('storage.local_http_domain')}" placeholder="请输入上传后的访问域名 (非必填项)" class="layui-input">
<p class="help-block">填写上传后的访问域名(不指定时根据当前访问地址自动计算),static.thinkadmin.top</p>
<p class="help-block">填写上传后的访问域名(不指定时根据当前访问地址自动计算),不需要填写访问协议,static.thinkadmin.top</p>
</div>
</div>

View File

@ -14,15 +14,17 @@
</label>
<div class="layui-input-block">
{if !sysconf('storage.qiniu_http_protocol')}{php}sysconf('storage.qiniu_http_protocol','http');{/php}{/if}
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.qiniu_http_protocol') eq $protocol}
<input checked type="radio" name="storage.qiniu_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.qiniu_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
<div class="layui-input help-checks">
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.qiniu_http_protocol') eq $protocol}
<input checked type="radio" name="storage.qiniu_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.qiniu_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
</div>
<p class="help-block">七牛云存储访问协议,其中 HTTPS 需要配置证书才能使用( AUTO 为相对协议 </p>
</div>
</div>
@ -49,7 +51,7 @@
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_bucket" type="text" name="storage.qiniu_bucket" required value="{:sysconf('storage.qiniu_bucket')}" placeholder="请输入七牛云存储 Bucket (空间名称)" class="layui-input">
<input id="storage.qiniu_bucket" type="text" name="storage.qiniu_bucket" value="{:sysconf('storage.qiniu_bucket')}" required vali-name="空间名称" placeholder="请输入七牛云存储 Bucket (空间名称)" class="layui-input">
<p class="help-block">填写七牛云存储空间名称static</p>
</div>
</div>
@ -59,8 +61,8 @@
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_http_domain" type="text" name="storage.qiniu_http_domain" required value="{:sysconf('storage.qiniu_http_domain')}" placeholder="请输入七牛云存储 Domain (访问域名)" class="layui-input">
<p class="help-block">填写七牛云存储访问域名,static.qiniu.thinkadmin.top</p>
<input id="storage.qiniu_http_domain" type="text" name="storage.qiniu_http_domain" value="{:sysconf('storage.qiniu_http_domain')}" required vali-name="访问域名" placeholder="请输入七牛云存储 Domain (访问域名)" class="layui-input">
<p class="help-block">填写七牛云存储访问域名,不需要填写访问协议,static.qiniu.thinkadmin.top</p>
</div>
</div>
@ -69,7 +71,7 @@
<b class="color-green">访问密钥</b><br><span class="nowrap color-desc">AccessKey</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_access_key" type="text" name="storage.qiniu_access_key" required value="{:sysconf('storage.qiniu_access_key')}" placeholder="请输入七牛云授权 AccessKey (访问密钥)" class="layui-input">
<input id="storage.qiniu_access_key" type="text" name="storage.qiniu_access_key" value="{:sysconf('storage.qiniu_access_key')}" required vali-name="访问密钥" placeholder="请输入七牛云授权 AccessKey (访问密钥)" class="layui-input">
<p class="help-block">可以在 [ 七牛云 > 个人中心 ] 设置并获取到访问密钥</p>
</div>
</div>
@ -79,7 +81,7 @@
<b class="color-green">安全密钥</b><br><span class="nowrap color-desc">SecretKey</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_secret_key" type="text" name="storage.qiniu_secret_key" required value="{:sysconf('storage.qiniu_secret_key')}" maxlength="43" placeholder="请输入七牛云授权 SecretKey (安全密钥)" class="layui-input">
<input id="storage.qiniu_secret_key" type="text" name="storage.qiniu_secret_key" value="{:sysconf('storage.qiniu_secret_key')}" maxlength="43" required vali-name="安全密钥" placeholder="请输入七牛云授权 SecretKey (安全密钥)" class="layui-input">
<p class="help-block">可以在 [ 七牛云 > 个人中心 ] 设置并获取到安全密钥</p>
</div>
</div>

View File

@ -49,7 +49,7 @@
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_bucket" type="text" name="storage.txcos_bucket" required value="{:sysconf('storage.txcos_bucket')}" placeholder="请输入腾讯云COS存储 Bucket" class="layui-input">
<input id="storage.txcos_bucket" type="text" name="storage.txcos_bucket" value="{:sysconf('storage.txcos_bucket')}" required vali-name="空间名称" placeholder="请输入腾讯云COS存储 Bucket" class="layui-input">
<p class="help-block">填写腾讯云COS存储空间名称thinkadmin-1251143395</p>
</div>
</div>
@ -59,8 +59,8 @@
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_http_domain" type="text" name="storage.txcos_http_domain" required value="{:sysconf('storage.txcos_http_domain')}" placeholder="请输入腾讯云COS存储 Domain" class="layui-input">
<p class="help-block">填写腾讯云COS存储外部访问域名static.txcos.thinkadmin.top</p>
<input id="storage.txcos_http_domain" type="text" name="storage.txcos_http_domain" value="{:sysconf('storage.txcos_http_domain')}" required vali-name="访问域名" placeholder="请输入腾讯云COS存储 Domain" class="layui-input">
<p class="help-block">填写腾讯云COS存储外部访问域名不需要填写访问协议,static.txcos.thinkadmin.top</p>
</div>
</div>
@ -69,7 +69,7 @@
<b class="color-green">访问密钥</b><br><span class="nowrap color-desc">AccessKey</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_access_key" type="text" name="storage.txcos_access_key" required value="{:sysconf('storage.txcos_access_key')}" placeholder="请输入腾讯云COS存储 AccessKey" class="layui-input">
<input id="storage.txcos_access_key" type="text" name="storage.txcos_access_key" value="{:sysconf('storage.txcos_access_key')}" required vali-name="访问密钥" placeholder="请输入腾讯云COS存储 AccessKey" class="layui-input">
<p class="help-block">可以在 [ 腾讯云 > 个人中心 ] 设置并获取到访问密钥</p>
</div>
</div>
@ -79,7 +79,7 @@
<b class="color-green">安全密钥</b><br><span class="nowrap color-desc">SecretKey</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_secret_key" type="text" name="storage.txcos_secret_key" required value="{:sysconf('storage.txcos_secret_key')}" maxlength="43" placeholder="请输入腾讯云COS存储 SecretKey" class="layui-input">
<input id="storage.txcos_secret_key" type="text" name="storage.txcos_secret_key" value="{:sysconf('storage.txcos_secret_key')}" maxlength="43" required vali-name="安全密钥" placeholder="请输入腾讯云COS存储 SecretKey" class="layui-input">
<p class="help-block">可以在 [ 腾讯云 > 个人中心 ] 设置并获取到安全密钥</p>
</div>
</div>

View File

@ -14,15 +14,17 @@
</label>
<div class="layui-input-block">
{if !sysconf('storage.upyun_http_protocol')}{php}sysconf('storage.upyun_http_protocol','http');{/php}{/if}
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.upyun_http_protocol') eq $protocol}
<input checked type="radio" name="storage.upyun_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.upyun_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
<div class="layui-input help-checks">
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.upyun_http_protocol') eq $protocol}
<input checked type="radio" name="storage.upyun_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.upyun_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
</div>
<p class="help-block">又拍云存储访问协议,其中 HTTPS 需要配置证书才能使用AUTO 为相对协议)</p>
</div>
</div>
@ -32,7 +34,7 @@
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_bucket" name="storage.upyun_bucket" required value="{:sysconf('storage.upyun_bucket')}" placeholder="请输入又拍云存储 Bucket (空间名称)" class="layui-input">
<input id="storage.upyun_bucket" name="storage.upyun_bucket" value="{:sysconf('storage.upyun_bucket')}" required vali-name="空间名称" placeholder="请输入又拍云存储 Bucket (空间名称)" class="layui-input">
<p class="help-block">填写又拍云存储空间名称think-admin-uss需要是全区唯一的值不存在时会自动创建</p>
</div>
</div>
@ -42,8 +44,8 @@
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_http_domain" name="storage.upyun_http_domain" required value="{:sysconf('storage.upyun_http_domain')}" placeholder="请输入又拍云存储 Domain (访问域名)" class="layui-input">
<p class="help-block">填写又拍云存储外部访问域名,static.uss.thinkadmin.top</p>
<input id="storage.upyun_http_domain" name="storage.upyun_http_domain" value="{:sysconf('storage.upyun_http_domain')}" required vali-name="访问域名" placeholder="请输入又拍云存储 Domain (访问域名)" class="layui-input">
<p class="help-block">填写又拍云存储外部访问域名,不需要填写访问协议,static.uss.thinkadmin.top</p>
</div>
</div>
@ -52,7 +54,7 @@
<b class="color-green">操作账号</b><br><span class="nowrap color-desc">Username</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_access_key" name="storage.upyun_access_key" required value="{:sysconf('storage.upyun_access_key')}" maxlength="100" placeholder="请输入又拍云存储 Username (操作员账号)" class="layui-input">
<input id="storage.upyun_access_key" name="storage.upyun_access_key" value="{:sysconf('storage.upyun_access_key')}" maxlength="100" required vali-name="操作员账号" placeholder="请输入又拍云存储 Username (操作员账号)" class="layui-input">
<p class="help-block">可以在 [ 账户管理 > 操作员 ] 设置操作员账号并将空间给予授权。</p>
</div>
</div>
@ -62,7 +64,7 @@
<b class="color-green">操作密码</b><br><span class="nowrap color-desc">Password</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_secret_key" name="storage.upyun_secret_key" required value="{:sysconf('storage.upyun_secret_key')}" maxlength="100" placeholder="请输入又拍云存储 Password (操作员密码)" class="layui-input">
<input id="storage.upyun_secret_key" name="storage.upyun_secret_key" value="{:sysconf('storage.upyun_secret_key')}" maxlength="100" required vali-name="操作员密码" placeholder="请输入又拍云存储 Password (操作员密码)" class="layui-input">
<p class="help-block">可以在 [ 账户管理 > 操作员 ] 设置操作员密码并将空间给予授权</p>
</div>
</div>

View File

@ -5,14 +5,14 @@
<div class="layui-col-xs4 padding-bottom-0">
<label class="relative block">
<span class="help-label"><b>登录表单标题</b>Login Name</span>
<input name="login_name" required placeholder="请输入登录页面的表单标题" value="{:sysconf('login_name')?:'系统管理'}" class="layui-input">
<input name="login_name" required placeholder="请输入登录页面的表单标题" vali-name="登录标题" value="{:sysconf('login_name')?:'系统管理'}" class="layui-input">
</label>
</div>
<div class="layui-col-xs4 padding-bottom-0">
<div class="help-label label-required-prev"><b>后台登录入口</b>Login Entry</div>
<label class="layui-input relative block nowrap label-required-null">
<span>{:sysuri('@',[],false,true)}</span>
<input autofocus required pattern="[a-zA-Z_][a-zA-Z0-9_]*" placeholder="请输入后台登录入口" class="layui-input inline-block padding-0 border-0" style="width:100px;background:none" value="{:substr(sysuri('admin/index/index',[],false), strlen(sysuri('@')))}" name="xpath">
<input autofocus required pattern="[a-zA-Z_][a-zA-Z0-9_]*" vali-name="登录入口" placeholder="请输入后台登录入口" class="layui-input inline-block padding-0 border-0" style="width:100px;background:none" value="{:substr(sysuri('admin/index/index',[],false), strlen(sysuri('@')))}" name="xpath">
</label>
</div>
<div class="layui-col-xs4 padding-bottom-0">
@ -38,11 +38,22 @@
</div>
<div class="layui-form-item margin-bottom-5">
<div class="help-label label-required-prev"><b>浏览器小图标</b>Browser Icon</div>
<div class="help-label label-required-prev"><b>JWT 接口密钥</b>Jwt Key</div>
<label class="relative block label-required-null">
<input class="layui-input" required pattern="^(http|/)" placeholder="请上传浏览器图标" value="{:sysconf('site_icon')}" name="site_icon">
<a class="input-right-icon layui-icon layui-icon-upload-drag" data-file="btn" data-type="png,jpg,jpeg" data-field="site_icon"></a>
<input class="layui-input" pattern=".{32}" maxlength="32" required vali-name="接口密钥" placeholder="请输入32位JWT接口密钥" name="data.jwtkey" value="{:sysconf('data.jwtkey')?:md5(uniqid(strval(rand(1000,9999)),true))}">
<a class="input-right-icon layui-icon layui-icon-refresh" id="RefreshJwtKey"></a>
</label>
<div class="help-block sub-span-blue">
请输入 <span>32</span> <span>JWT</span> 接口密钥,在使用 <span>JWT</span> 接口时需要使用此密钥进行加密及签名!
</div>
</div>
<div class="layui-form-item margin-bottom-5">
<div class="help-label label-required-prev"><b>浏览器小图标</b>Browser Icon</div>
<div class="relative block label-required-null">
<input class="layui-input" required pattern="url" vali-name="图标文件" data-tips-image data-tips-hover placeholder="请上传浏览器图标" value="{:sysconf('site_icon')}" name="site_icon">
<a class="input-right-icon layui-icon layui-icon-upload-drag" data-file="btn" data-type="png,jpg,jpeg" data-field="site_icon"></a>
</div>
<div class="help-block sub-span-blue">
建议上传 <span>128x128</span> <span>256x256</span> <span>JPG</span>,<span>PNG</span>,<span>JPEG</span> 图片,保存后会自动生成 <span>48x48</span> <span>ICO</span> 文件 ~
</div>
@ -52,14 +63,14 @@
<div class="layui-col-xs4 padding-bottom-0">
<label class="layui-form-item margin-bottom-5 relative block">
<span class="help-label"><b>网站名称</b>Site Name</span>
<input name="site_name" required placeholder="请输入网站名称" value="{:sysconf('site_name')}" class="layui-input">
<input name="site_name" required placeholder="请输入网站名称" vali-name="网站名称" value="{:sysconf('site_name')}" class="layui-input">
<span class="help-block">网站名称将显示在浏览器的标签上 ~</span>
</label>
</div>
<div class="layui-col-xs4 padding-bottom-0">
<label class="layui-form-item margin-bottom-5 relative block">
<span class="help-label"><b>后台程序名称</b>App Name</span>
<input name="app_name" required placeholder="请输入程序名称" value="{:sysconf('app_name')}" class="layui-input">
<input name="app_name" required placeholder="请输入程序名称" vali-name="程序名称" value="{:sysconf('app_name')}" class="layui-input">
<span class="help-block">管理程序名称显示在后台左上标题处 ~</span>
</label>
</div>
@ -72,8 +83,8 @@
</div>
<div class="layui-col-xs4 padding-top-0 padding-bottom-0">
<label class="relative block">
<span class="help-label"><b>网安备号</b>Baian</span>
<input name="beian" placeholder="请输入公安备号" value="{:sysconf('beian')}" class="layui-input">
<span class="help-label"><b>安安备号</b>Beian</span>
<input name="beian" placeholder="请输入公安备号" value="{:sysconf('beian')}" class="layui-input">
</label>
</div>
<div class="layui-col-xs4 padding-top-0 padding-bottom-0">
@ -85,7 +96,7 @@
<div class="layui-col-xs4 padding-top-0 padding-bottom-0">
<label class="relative block">
<span class="help-label"><b>网站版权信息</b>Copyright</span>
<input name="site_copy" required placeholder="请输入版权信息" value="{:sysconf('site_copy')}" class="layui-input">
<input name="site_copy" required placeholder="请输入版权信息" vali-name="版权信息" value="{:sysconf('site_copy')}" class="layui-input">
</label>
</div>
<div class="layui-col-xs12 help-block padding-top-0">
@ -103,6 +114,11 @@
<script>
$('[name=login_image]').uploadMultipleImage();
require(['md5'], function (md5) {
$('body').off('click', '#RefreshJwtKey').on('click', '#RefreshJwtKey', function () {
$(this).parent().find('input').val(md5.hash(Date.toString() + Math.random()));
});
});
layui.form.on('select(SiteTheme)', function (data) {
var alls = '', prox = 'layui-layout-theme-', curt = prox + data.value;
$(data.elem.options).map(function () {

View File

@ -0,0 +1,40 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card" data-table-id="FileTable">
<div class="layui-card-body padding-left-40">
<label class="layui-form-item relative block">
<span class="help-label"><b>{:lang('文件名称')}</b>Name</span>
<input maxlength="100" class="layui-input" name="name" value='{$vo.name|default=""}' required vali-name="文件名称" placeholder="请输入文件名称">
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>{:lang('文件大小')}</b>Size</span>
<input maxlength="100" class="layui-input layui-bg-gray" value='{$vo.size|default=0|format_bytes}' readonly>
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>{:lang('存储方式')}</b>Type</span>
<input maxlength="100" class="layui-input layui-bg-gray" value='{$types[$vo.type]??""}' readonly>
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>{:lang('文件哈希')}</b>Hash</span>
<input maxlength="100" class="layui-input layui-bg-gray" value='{$vo.hash|default=""}' readonly>
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>{:lang('文件链接')}</b>Link</span>
<input maxlength="100" class="layui-input layui-bg-gray" value='{$vo.xurl|default=""}' readonly>
</label>
</div>
<div class="hr-line-dashed"></div>
{notempty name='vo.id'}<input type='hidden' value='{$vo.id}' name='id'>{/notempty}
<div class="layui-form-item text-center">
<button class="layui-btn" type='submit'>{:lang('保存数据')}</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="{:lang('确定要取消编辑吗?')}" data-close>{:lang('取消编辑')}</button>
</div>
</form>

View File

@ -2,10 +2,10 @@
{block name="button"}
<!--{if auth("distinct")}-->
<a data-table-id="FileTable" data-load='{:url("distinct")}' class='layui-btn layui-btn-sm layui-btn-primary'>清理重复</a>
<a data-table-id="FileTable" data-load='{:url("distinct")}' class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('清理重复')}</a>
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="确定永久删除这些账号吗?" data-table-id="FileTable" data-action='{:url("remove")}' data-rule="id#{id}" class='layui-btn layui-btn-sm layui-btn-primary'>批量删除</a>
<a data-confirm="{:lang('确定删除这些记录吗?')}" data-table-id="FileTable" data-action='{:url("remove")}' data-rule="id#{id}" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('批量删除')}</a>
<!--{/if}-->
{/block}
@ -22,41 +22,43 @@
cols: [[
{checkbox: true, fixed: true},
{field: 'id', title: 'ID', width: 80, align: 'center', sort: true},
{field: 'name', title: '文件名称', width: '12%', align: 'center'},
{field: 'hash', title: '文件哈希', width: '15%', align: 'center', templet: '<div><code>{{d.hash}}</code></div>'},
{field: 'name', title: '{:lang("文件名称")}', width: '12%', align: 'center'},
{field: 'hash', title: '{:lang("文件哈希")}', width: '15%', align: 'center', templet: '<div><code>{{d.hash}}</code></div>'},
{field: 'size', title: '{:lang("文件大小")}', align: 'center', width: '7%', sort: true, templet: '<div>{{-$.formatFileSize(d.size)}}</div>'},
{field: 'xext', title: '{:lang("文件后缀")}', align: 'center', width: '7%', sort: true},
{
field: 'size', title: '文件大小', align: 'center', width: '8%', sort: true, templet: function (d) {
return $.formatFileSize(d.size)
}
},
{field: 'xext', title: '文件后缀', align: 'center', width: '8%', sort: true},
{
field: 'xurl', title: '查看文件', width: '8%', align: 'center', templet: function (d) {
field: 'xurl', title: '{:lang("查看文件")}', width: '7%', align: 'center', templet: function (d) {
if (typeof d.mime === 'string' && /^image\//.test(d.mime)) {
return laytpl('<div class="headimg headimg-no headimg-ss margin-0" data-tips-hover data-tips-image="{{d.xurl}}" style="background-image:url({{d.xurl}})"></div>').render(d)
} else if (typeof d.mime === 'string' && /^(video|audio)\//.test(d.mime)) {
return laytpl('<div><a target="_blank" data-iframe="{{d.xurl}}" data-title="查看媒体">查看</a></div>').render(d);
} else {
return laytpl('<div><a target="_blank" href="{{d.xurl}}">查看</a></div>').render(d);
return laytpl('<div><a target="_blank" data-tips-hover data-tips-image="{{d.xurl}}"><i class="layui-icon layui-icon-picture"></i></a></div>').render(d)
}
if (typeof d.mime === 'string' && /^video\//.test(d.mime)) {
return laytpl('<div><a target="_blank" data-video-player="{{d.xurl}}" data-tips-text="{:lang(\'播放视频\')}"><i class="layui-icon layui-icon-video"></i></a></div>').render(d);
}
if (typeof d.mime === 'string' && /^audio\//.test(d.mime)) {
return laytpl('<div><a target="_blank" data-video-player="{{d.xurl}}" data-tips-text="{:lang(\'播放音频\')}"><i class="layui-icon layui-icon-headset"></i></a></div>').render(d);
}
return laytpl('<div><a target="_blank" href="{{d.xurl}}" data-tips-text="{:lang(\'查看下载\')}"><i class="layui-icon layui-icon-file"></i></a></div>').render(d);
}
},
{
field: 'isfast', title: '上传方式', align: 'center', width: '8%', templet: function (d) {
return d.isfast ? '<b class="color-green">秒传</b>' : '<b class="color-blue">普通</b>';
field: 'isfast', title: '{:lang("上传方式")}', align: 'center', width: '8%', templet: function (d) {
return d.isfast ? '<b class="color-green">{:lang("秒传")}</b>' : '<b class="color-blue">{:lang("普通")}</b>';
}
},
{field: 'ctype', title: '存储方式', align: 'center', width: '10%'},
{field: 'create_at', title: '上传时间', align: 'center', width: '15%', sort: true},
{toolbar: '#toolbar', title: '操作面板', align: 'center', minWidth: 90, fixed: 'right'}
{field: 'ctype', title: '{:lang("存储方式")}', align: 'center', width: '10%'},
{field: 'create_at', title: '{:lang("创建时间")}', align: 'center', width: '15%', sort: true},
{toolbar: '#toolbar', title: '{:lang("操作面板")}', align: 'center', minWidth: 150, fixed: 'right'}
]]
});
});
</script>
<script type="text/html" id="toolbar">
<!--{if auth("edit")}-->
<a class="layui-btn layui-btn-sm" data-modal="{:url('edit')}?id={{d.id}}" data-title="编辑文件信息">{:lang("编 辑")}</a>
<!--{/if}-->
<!--{if auth("remove")}-->
<a class="layui-btn layui-btn-sm layui-btn-danger" data-action="{:url('remove')}" data-value="id#{{d.id}}"> </a>
<a class="layui-btn layui-btn-sm layui-btn-danger" data-action="{:url('remove')}" data-value="id#{{d.id}}">{:lang("删 除")}</a>
<!--{/if}-->
</script>
{/block}

View File

@ -1,26 +1,26 @@
<fieldset>
<legend>条件搜索</legend>
<legend>{:lang('条件搜索')}</legend>
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">文件名称</label>
<label class="layui-form-label">{:lang('文件名称')}</label>
<label class="layui-input-inline">
<input name="name" value="{$get.name|default=''}" placeholder="请输入文件名称" class="layui-input">
<input name="name" value="{$get.name|default=''}" placeholder="{:lang('请输入文件名称')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">文件哈希</label>
<label class="layui-form-label">{:lang('文件哈希')}</label>
<label class="layui-input-inline">
<input name="hash" value="{$get.hash|default=''}" placeholder="请输入文件哈希" class="layui-input">
<input name="hash" value="{$get.hash|default=''}" placeholder="{:lang('请输入文件哈希')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">文件后缀</label>
<label class="layui-form-label">{:lang('文件后缀')}</label>
<div class="layui-input-inline">
<select name="xext" lay-search class="layui-select">
<option value=''>-- 全部后缀 --</option>
<option value=''>-- {:lang('全部')} --</option>
{foreach $xexts as $v}{if isset($get.xext) and $k eq $get.xext}
<option selected value="{$v}">{$v}</option>
{else}
@ -31,10 +31,10 @@
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">存储方式</label>
<label class="layui-form-label">{:lang('存储方式')}</label>
<div class="layui-input-inline">
<select name="type" lay-search class="layui-select">
<option value=''>-- 全部方式 --</option>
<option value=''>-- {:lang('全部')} --</option>
{foreach $types as $k=>$v}{if isset($get.type) and $k eq $get.type}
<option selected value="{$k}">{$v}</option>
{else}
@ -45,14 +45,14 @@
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">上传时间</label>
<label class="layui-form-label">{:lang('创建时间')}</label>
<div class="layui-input-inline">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="请选择上传时间" class="layui-input">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="{:lang('请选择创建时间')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> {:lang('搜 索')}</button>
</div>
</form>
</fieldset>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=0.4">
@ -13,6 +13,9 @@
<link rel="stylesheet" href="__ROOT__/static/theme/css/iconfont.css?at={:date('md')}">
<link rel="stylesheet" href="__ROOT__/static/theme/css/console.css?at={:date('md')}">
<link rel="stylesheet" href="__ROOT__/static/extra/style.css?at={:date('md')}">
{if file_exists(syspath("public/static/extra/icon/iconfont.css"))}
<link rel="stylesheet" href="__ROOT__/static/extra/icon/iconfont.css?at={:date('md')}">
{/if}
{block name="style"}{/block}
<script src="__ROOT__/static/plugs/jquery/pace.min.js"></script>
<script src="{:url('admin/api.plugs/script',[],false,false)}"></script>

View File

@ -21,23 +21,23 @@
{if session('user.username')}
<li class="layui-nav-item">
<dl class="layui-nav-child">
<dd lay-unselect><a data-modal="{:sysuri('admin/index/info',['id'=>session('user.id')])}"><i class="layui-icon layui-icon-set-fill"></i> 基本资料</a></dd>
<dd lay-unselect><a data-modal="{:sysuri('admin/index/pass',['id'=>session('user.id')])}"><i class="layui-icon layui-icon-component"></i> 安全设置</a></dd>
<dd lay-unselect><a data-modal="{:sysuri('admin/index/info',['id'=>session('user.id')])}"><i class="layui-icon layui-icon-set-fill"></i> {:lang('基本资料')}</a></dd>
<dd lay-unselect><a data-modal="{:sysuri('admin/index/pass',['id'=>session('user.id')])}"><i class="layui-icon layui-icon-component"></i> {:lang('安全设置')}</a></dd>
{if isset($super) and $super}
<dd lay-unselect><a data-load="{:sysuri('admin/api.system/push')}"><i class="layui-icon layui-icon-template-1"></i> 缓存加速</a></dd>
<dd lay-unselect><a data-load="{:sysuri('admin/api.system/clear')}"><i class="layui-icon layui-icon-fonts-clear"></i> 清理缓存</a></dd>
<dd lay-unselect><a data-load="{:sysuri('admin/api.system/push')}"><i class="layui-icon layui-icon-template-1"></i> {:lang('缓存加速')}</a></dd>
<dd lay-unselect><a data-load="{:sysuri('admin/api.system/clear')}"><i class="layui-icon layui-icon-fonts-clear"></i> {:lang('清理缓存')}</a></dd>
{/if}
<dd lay-unselect><a data-width="520px" data-modal="{:sysuri('admin/index/theme')}"><i class="layui-icon layui-icon-theme"></i> 配色方案</a></dd>
<dd lay-unselect><a data-load="{:sysuri('admin/login/out')}" data-confirm="确定要退出登录吗?"><i class="layui-icon layui-icon-release"></i> 退出登录</a></dd>
<dd lay-unselect><a data-width="520px" data-modal="{:sysuri('admin/index/theme')}"><i class="layui-icon layui-icon-theme"></i> {:lang('配色方案')}</a></dd>
<dd lay-unselect><a data-load="{:sysuri('admin/login/out')}" data-confirm="{:lang('确定要退出登录吗?')}"><i class="layui-icon layui-icon-release"></i> {:lang('退出登录')}</a></dd>
</dl>
<a class="layui-elip">
<span class="headimg" data-lazy-src="{:htmlentities(session('user.headimg'))}"></span>
<span>{:htmlentities(session('user.nickname')?:session('user.username'))}</span>
<span>{:htmlentities(lang(session('user.nickname')?:session('user.username')))}</span>
</a>
</li>
{else}
<li class="layui-nav-item">
<a data-href="{:sysuri('admin/login/index')}"><i class="layui-icon layui-icon-username"></i> 立即登录</a>
<a data-href="{:sysuri('admin/login/index')}"><i class="layui-icon layui-icon-username"></i> {:lang('立即登录')}</a>
</li>
{/if}
</ul>

View File

@ -6,12 +6,15 @@
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=0.4">
<link rel="stylesheet" href="__ROOT__/static/plugs/layui/css/layui.css?at={:date('md')}">
<link rel="stylesheet" href="__ROOT__/static/theme/css/iconfont.css?at={:date('md')}">
{if file_exists(syspath("public/static/extra/icon/iconfont.css"))}
<link rel="stylesheet" href="__ROOT__/static/extra/icon/iconfont.css?at={:date('md')}">
{/if}
<link rel="stylesheet" href="__ROOT__/static/theme/css/console.css?at={:date('md')}">
<link rel="stylesheet" href="__ROOT__/static/extra/style.css?at={:date('md')}">
{block name="style"}{/block}

View File

@ -10,7 +10,7 @@
<div class="login-container" {$loginStyle|raw}>
<div class="header notselect layui-hide-xs">
<a href="{:url('@')}" class="title">{:sysconf('app_name')}<span>{:sysconf('app_version')}</span></a>
{notempty name='developMode'}
{notempty name='runtimeMode'}
<a class="pull-right layui-anim layui-anim-fadein" target="_blank" href='https://gitee.com/zoujingli/ThinkAdmin'>
<img src='https://gitee.com/zoujingli/ThinkAdmin/widgets/widget_1.svg' alt='Fork me on Gitee'>
</a>
@ -22,19 +22,19 @@
<li class="username">
<label class="label-required-null">
<i class="layui-icon layui-icon-username"></i>
<input class="layui-input" required pattern="^\S{4,}$" name="username" autofocus autocomplete="off" placeholder="登录账号" title="请输入登录账号">
<input class="layui-input" required pattern="^\S{4,}$" vali-name="登录账号" name="username" autofocus autocomplete="off" placeholder="登录账号">
</label>
</li>
<li class="password">
<label class="label-required-null">
<i class="layui-icon layui-icon-password"></i>
<input class="layui-input" required pattern="^\S{4,}$" name="password" maxlength="32" type="password" autocomplete="off" placeholder="登录密码" title="请输入登录密码">
<input class="layui-input" required pattern="^\S{4,}$" vali-name="登录密码" name="password" maxlength="32" type="password" autocomplete="off" placeholder="登录密码" lay-affix="eye">
</label>
</li>
<li class="verify layui-hide">
<label class="inline-block relative label-required-null">
<i class="layui-icon layui-icon-picture-fine"></i>
<input class="layui-input" required pattern="^\S{4,}$" name="verify" maxlength="4" autocomplete="off" placeholder="验证码" title="请输入验证码">
<input class="layui-input" required pattern="^\S{4,}$" name="verify" maxlength="4" autocomplete="off" vali-name="验证码" placeholder="验证码">
</label>
<label data-captcha="{:url('admin/login/captcha',[],false)}" data-field-verify="verify" data-field-uniqid="uniqid" data-captcha-type="{$captchaType}" data-captcha-token="{$captchaToken}"></label>
</li>
@ -46,7 +46,7 @@
<div class="footer notselect">
<p class="layui-hide-xs">推荐使用 <a target="_blank" href="https://www.google.cn/chrome">Google Chrome</a> <a target="_blank" href="https://www.microsoft.com/zh-cn/edge#platform">Microsoft Edge</a> 浏览器访问</p>
{:sysconf('site_copy')}
{if sysconf('beian')}<span class="padding-5">|</span><a target="_blank" href="https://beian.miit.gov.cn/">{:sysconf('beian')}</a>{/if}
{if sysconf('beian')}<span class="padding-5">|</span><a target="_blank" href="https://www.beian.gov.cn/portal/registerSystemInfo">{:sysconf('beian')}</a>{/if}
{if sysconf('miitbeian')}<span class="padding-5">|</span><a target="_blank" href="https://beian.miit.gov.cn/">{:sysconf('miitbeian')}</a>{/if}
</div>
</div>

View File

@ -3,7 +3,7 @@
{block name='header'}
{notempty name='title'}
<div class="layui-card-header">
<span class="layui-icon font-s10 color-desc margin-right-5">&#xe65b;</span>{$title|default=''}
<span class="layui-icon font-s10 color-desc margin-right-5">&#xe65b;</span>{$title|lang}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
@ -11,6 +11,11 @@
<div class="layui-card-line"></div>
<div class="layui-card-body">
<div class="layui-card-html">
{notempty name='showErrorMessage'}
<div class="think-box-notify" type="error">
<b>{:lang('系统提示:')}</b><span>{$showErrorMessage|raw}</span>
</div>
{/notempty}
{block name='content'}{/block}
</div>
</div>

View File

@ -3,7 +3,7 @@
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label label-required-next">上级菜单</label>
<label class="layui-form-label label-required-next">{:lang('上级菜单')}</label>
<div class="layui-input-block">
<select name='pid' class='layui-select' lay-search>
{foreach $menus as $menu}{eq name='menu.id' value='$vo.pid|default=0'}
@ -17,17 +17,17 @@
</div>
<div class="layui-form-item">
<label class="layui-form-label">菜单名称</label>
<label class="layui-form-label">{:lang('菜单名称')}</label>
<div class="layui-input-block">
<input name="title" value='{$vo.title|default=""}' required placeholder="请输入菜单名称" class="layui-input">
<input name="title" value='{$vo.title|default=""}' required vali-name="菜单名称" placeholder="请输入菜单名称" class="layui-input">
<p class="help-block"><b>必选</b>,请填写菜单名称 ( 如:系统管理 ),建议字符不要太长,一般 4-6 个汉字</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">菜单链接</label>
<label class="layui-form-label">{:lang('菜单链接')}</label>
<div class="layui-input-block">
<input onblur="this.value=this.value === ''?'#':this.value" name="url" required placeholder="请输入菜单链接" value="{$vo.url|default='#'}" class="layui-input">
<input onblur="this.value=this.value === ''?'#':this.value" name="url" required vali-name="菜单链接" placeholder="请输入菜单链接" value="{$vo.url|default='#'}" class="layui-input">
<p class="help-block">
<b>必选</b>,请填写链接地址或选择系统节点 ( https://domain.com/admin/user/index.html admin/user/index )
<br>当填写链接地址时,以下面的 “权限节点” 来判断菜单自动隐藏或显示,注意未填写 “权限节点” 时将不会隐藏该菜单哦
@ -36,7 +36,7 @@
</div>
<div class="layui-form-item">
<label class="layui-form-label">链接参数</label>
<label class="layui-form-label">{:lang('链接参数')}</label>
<div class="layui-input-block">
<input name="params" placeholder="请输入链接参数" value="{$vo.params|default=''}" class="layui-input">
<p class="help-block"><b>可选</b>,设置菜单链接的 GET 访问参数 ( name=1&age=3 )</p>
@ -44,7 +44,7 @@
</div>
<div class="layui-form-item">
<label class="layui-form-label">权限节点</label>
<label class="layui-form-label">{:lang('权限节点')}</label>
<div class="layui-input-block">
<input name="node" placeholder="请输入权限节点" value="{$vo.node|default=''}" class="layui-input">
<p class="help-block"><b>可选</b>,请填写系统权限节点 ( admin/user/index ),未填写时默认解释"菜单链接"判断是否拥有访问权限;</p>
@ -52,7 +52,7 @@
</div>
<div class="layui-form-item">
<label class="layui-form-label">菜单图标</label>
<label class="layui-form-label">{:lang('菜单图标')}</label>
<div class="layui-input-block">
<div class="layui-input-inline">
<input placeholder="请输入或选择图标" name="icon" value='{$vo.icon|default=""}' class="layui-input">
@ -60,7 +60,7 @@
<span style="padding:0 12px;min-width:45px" class='layui-btn layui-btn-primary'>
<i style="font-size:1.2em;margin:0;float:none" class='{$vo.icon|default=""}'></i>
</span>
<button data-icon='icon' type='button' class='layui-btn layui-btn-primary'>选择图标</button>
<button data-icon='icon' type='button' class='layui-btn layui-btn-primary'>{:lang('选择图标')}</button>
<p class="help-block"><b>可选</b>,设置菜单选项前置图标,目前支持 layui 字体图标及 iconfont 定制字体图标。</p>
</div>
</div>
@ -71,8 +71,8 @@
{notempty name='vo.id'}<input type='hidden' value='{$vo.id}' name='id'>{/notempty}
<div class="layui-form-item text-center">
<button class="layui-btn" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
<button class="layui-btn" type='submit'>{:lang('保存数据')}</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="{:lang('确定要取消编辑吗?')}" data-close>{:lang('取消编辑')}</button>
</div>
</form>

View File

@ -2,30 +2,35 @@
{block name="button"}
<!--{if $type eq 'index' and auth("add")}-->
<button data-modal='{:url("add")}' data-table-id="MenuTable" class='layui-btn layui-btn-sm layui-btn-primary'>添加菜单</button>
<button data-modal='{:url("add")}' data-table-id="MenuTable" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('添加菜单')}</button>
<!--{/if}-->
<!--{if $type eq 'index' and auth("state")}-->
<button data-action='{:url("state")}' data-table-id="MenuTable" data-rule="id#{sps};status#0" class='layui-btn layui-btn-sm layui-btn-primary'>禁用菜单</button>
<button data-action='{:url("state")}' data-table-id="MenuTable" data-rule="id#{sps};status#0" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('禁用菜单')}</button>
<!--{/if}-->
<!--{if $type eq 'recycle' and auth("state")}-->
<button data-action='{:url("state")}' data-table-id="MenuTable" data-rule="id#{spp};status#1" class='layui-btn layui-btn-sm layui-btn-primary'>激活菜单</button>
<button data-action='{:url("state")}' data-table-id="MenuTable" data-rule="id#{spp};status#1" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('激活菜单')}</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="layui-tab layui-tab-card">
<ul class="layui-tab-title">
{foreach ['index'=>'系统菜单','recycle'=>'回 收 站'] as $k=>$v}
{if isset($type) and $type eq $k}
{foreach ['index'=>lang('全部菜单'),'recycle'=>lang('回 收 站')] as $k=>$v}
{if isset($type) and $type eq $k and isset($pid) and $pid == ''}
<li class="layui-this" data-open="{:url('index')}?type={$k}">{$v}</li>
{else}
<li data-open="{:url('index')}?type={$k}">{$v}</li>
{/if}{/foreach}
<!--顶级菜单-->
<!--{foreach $menupList as $k=>$v}-->
<li data-open="{:url('index')}?type=index&pid={$v.id}" {if isset($pid) and $pid eq $v.id}class="layui-this"{/if} >{$v.title}</li>
<!--{/foreach}-->
</ul>
<div class="layui-tab-content">
<table id="MenuTable" data-url="{:sysuri()}" data-target-search="form.form-search"></table>
<table id="MenuTable" data-url="{:request()->url()}" data-target-search="form.form-search"></table>
</div>
</div>
@ -34,30 +39,21 @@
$('#MenuTable').layTable({
even: true, height: 'full', page: false,
sort: {field: 'sort desc,id', type: 'asc'},
where: {type: '{$type|default="index"}'},
where: {type: '{$type|default="index"}', pid: '{$pid|default=""}'},
filter: function (items) {
var type = this.where.type;
return items.filter(function (item) {
return !(type === 'index' && item.status === 0);
return !(type === 'index' && parseInt(item.status) === 0);
});
},
cols: [[
{checkbox: true, field: 'sps'},
{field: 'sort', title: '排序权重', width: 100, align: 'center', templet: '#SortInputTpl'},
{
field: 'icon', title: '图 标', width: 80, align: 'center', templet: function (d) {
return layui.laytpl('<i class="{{d.icon}} font-s18"></i>').render(d);
}
},
{
field: 'title', title: '菜单名称', minWidth: 220, templet: function (d) {
return layui.laytpl('<span class="color-desc">{{d.spl}}</span>{{d.title}}').render(d);
}
},
{field: 'url', title: '跳转链接', minWidth: 200},
{field: 'status', title: '菜单状态', minWidth: 120, align: 'center', templet: '#StatusSwitchTpl'},
// {field: 'create_at', title: '创建时间', minWidth: 170, align: 'center'},
{toolbar: '#toolbar', title: '操作面板', minWidth: 150, align: 'center', fixed: 'right'},
{field: 'sort', title: '{:lang("排序权重")}', width: 100, align: 'center', templet: '#SortInputTpl'},
{field: 'icon', title: '{:lang("图 标")}', width: 80, align: 'center', templet: '<div><i class="{{d.icon}} font-s18"></i></div>'},
{field: 'title', title: '{:lang("菜单名称")}', minWidth: 220, templet: '<div><span class="color-desc">{{d.spl}}</span>{{d.title}}</div>'},
{field: 'url', title: '{:lang("跳转链接")}', minWidth: 200},
{field: 'status', title: '{:lang("使用状态")}', minWidth: 120, align: 'center', templet: '#StatusSwitchTpl'},
{toolbar: '#toolbar', title: '{:lang("操作面板")}', minWidth: 150, align: 'center', fixed: 'right'},
]]
});
@ -81,12 +77,12 @@
<script type="text/html" id="StatusSwitchTpl">
<!--{if auth("state")}-->
{{# if( "{$type|default='index'}"==='index' || (d.spc<1 || d.status<1)){ }}
<input type="checkbox" value="{{d.sps}}|{{d.spp}}" lay-text="已激活|已禁用" lay-filter="StatusSwitch" lay-skin="switch" {{-d.status>0?'checked':''}}>
<input type="checkbox" value="{{d.sps}}|{{d.spp}}" lay-text="{:lang('已激活')}|{:lang('已禁用')}" lay-filter="StatusSwitch" lay-skin="switch" {{-d.status>0?'checked':''}}>
{{# }else{ }}
{{-d.status ? '<b class="color-green">已激活</b>' : '<b class="color-red">已禁用</b>'}}
{{-d.status ? '<b class="color-green">{:lang('已激活')}</b>' : '<b class="color-red">{:lang('已禁用')}</b>'}}
{{# } }}
<!--{else}-->
{{-d.status ? '<b class="color-green">已激活</b>' : '<b class="color-red">已禁用</b>'}}
{{-d.status ? '<b class="color-green">{:lang('已激活')}</b>' : '<b class="color-red">{:lang('已禁用')}</b>'}}
<!--{/if}-->
</script>
@ -101,13 +97,13 @@
<!-- {if isset($type) and $type eq 'index'} -->
<!-- {if auth('add')} -->
{{# if(d.spt<2){ }}
<a class="layui-btn layui-btn-sm layui-btn-primary" data-title="添加系统菜单" data-modal='{:url("add")}?pid={{d.id}}'> </a>
<a class="layui-btn layui-btn-sm layui-btn-primary" data-title="添加系统菜单" data-modal='{:url("add")}?pid={{d.id}}'>{:lang('添 加')}</a>
{{# }else{ }}
<a class="layui-btn layui-btn-sm layui-btn-disabled"> </a>
<a class="layui-btn layui-btn-sm layui-btn-disabled">{:lang('添 加')}</a>
{{# } }}
<!-- {/if} -->
{if auth('edit')}
<a class="layui-btn layui-btn-sm" data-event-dbclick data-title="编辑系统菜单" data-modal='{:url("edit")}?id={{d.id}}'> </a>
<a class="layui-btn layui-btn-sm" data-event-dbclick data-title="编辑系统菜单" data-modal='{:url("edit")}?id={{d.id}}'>{:lang('编 辑')}</a>
{/if}
<!-- {else} -->
{if auth('remove')}

View File

@ -1,44 +0,0 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
<div class="layui-card-body padding-left-40">
<div class="layui-textarea border-0 font-s14 layui-bg-gray padding-top-20 padding-left-20">
<div class="layui-row margin-bottom-15">
<div class="layui-col-xs6">模块名称:<b class="color-green">{$module.name}</b></div>
<div class="layui-col-xs6">开发作者:<b class="color-green">{$module.author}</b></div>
</div>
<div class="layui-row margin-bottom-15">
<div class="layui-col-xs6">最新版本:<b class="color-green">{$module.version}</b></div>
<div class="layui-col-xs6">{if isset($current.version)}已安装版本:<b class="color-green">{$current.version}</b>{/if}</div>
</div>
<div class="margin-bottom-15">模块描述:{$module.content}</div>
</div>
<ul class="layui-timeline margin-top-30 margin-bottom-30" style="max-height:400px;overflow:auto">
{foreach $module.change as $version=>$change}
<li class="layui-timeline-item">
{if isset($current.version) and $current.version eq $version}
<i class="layui-icon layui-timeline-axis">&#xe756;</i>
{else}
<i class="layui-icon layui-timeline-axis">&#xe63f;</i>
{/if}
<div class="layui-timeline-content layui-text">
<h3 class="layui-timeline-title">
版本号 {$version} <span class="margin-left-10 font-s13 color-desc">{$change.version|default=''}</span>
</h3>
<div data-marked>
{$change.content|default=''|raw}
</div>
</div>
</li>
{/foreach}
<li class="layui-timeline-item"></li>
</ul>
</div>
<script>
require(['marked'], function (marked) {
$('[data-marked]').map(function () {
if (this.marked) return; else this.marked = true;
this.innerHTML = marked.parse(this.innerHTML);
});
});
</script>
</form>

View File

@ -1,42 +0,0 @@
{extend name='main'}
{block name="content"}
<div class="think-box-shadow">
<table class="layui-table margin-top-15" lay-skin="line">
{notempty name='modules'}
<thead>
<tr>
<th class='text-left nowrap'>模块名称</th>
<th class='text-left nowrap'>模块描述</th>
<th class='text-left nowrap'>当前版本</th>
<th></th>
</tr>
</thead>
{/notempty}
<tbody>
{foreach $modules as $key=>$vo}
<tr>
<td class='text-left nowrap'>{$vo.name}</td>
<td class='text-left nowrap'>{$vo.content}</td>
<td class='text-left nowrap'>
<div class="inline-block pointer" data-title="查看模块版本" data-modal="{:url('change')}?name={$vo.name}">
{if isset($vo.local)} {$vo.local.version}
{if $vo.version > $vo.local.version}
<span class="color-red margin-left-5">{$vo.version}</span>
{else}
<span class="color-desc margin-left-5">{$vo.type_desc}</span>
{/if}
{else}<span class="color-desc margin-left-5">{$vo.type_desc}</span>{/if}
</div>
</td>
<td class="text-left">
{if $vo.type_code eq 1} <a class="layui-btn layui-btn-xs" data-action="{:url('install')}" data-value="name#{$vo.name}"> </a>{/if}
{if $vo.type_code eq 2} <a class="layui-btn layui-btn-xs" data-action="{:url('install')}" data-value="name#{$vo.name}"> </a>{/if}
</td>
</tr>
{/foreach}
</tbody>
</table>
{empty name='modules'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
</div>
{/block}

View File

@ -1,58 +0,0 @@
<fieldset>
<legend>条件搜索</legend>
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">任务编号</label>
<div class="layui-input-inline">
<input name="code" value="{$get.code|default=''}" placeholder="请输入任务编号" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">任务名称</label>
<div class="layui-input-inline">
<input name="title" value="{$get.title|default=''}" placeholder="请输入任务名称" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">任务指令</label>
<div class="layui-input-inline">
<input name="command" value="{$get.command|default=''}" placeholder="请输入任务指令" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">任务状态</label>
<div class="layui-input-inline">
<select name="status" class="layui-select">
{foreach [''=>'-- 全部状态 --','1'=>'待处理','2'=>'处理中','3'=>'处理完成','4'=>'处理失败'] as $k=>$v}
{if isset($get.status) and $get.status eq $k}
<option selected value="{$k}">{$v}</option>
{else}
<option value="{$k}">{$v}</option>
{/if}
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">计划时间</label>
<div class="layui-input-inline">
<input data-date-range name="exec_time" value="{$get.exec_time|default=''}" placeholder="请选择计划时间" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">执行时间</label>
<div class="layui-input-inline">
<input data-date-range name="enter_time" value="{$get.enter_time|default=''}" placeholder="请选择执行时间" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">创建时间</label>
<div class="layui-input-inline">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="请选择创建时间" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
</div>
</form>
</fieldset>

View File

@ -2,18 +2,18 @@
{block name="button"}
<!--{if auth("remove")}-->
<button data-table-id="OplogTable" data-action='{:url("remove")}' data-rule="id#{id}" data-confirm="确定要删除选中的日志吗?" class='layui-btn layui-btn-sm layui-btn-primary'>批量删除</button>
<button data-table-id="OplogTable" data-action='{:url("remove")}' data-rule="id#{id}" data-confirm="确定要删除选中的日志吗?" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('批量删除')}</button>
<!--{/if}-->
<!--{if auth("clear")}-->
<button data-table-id="OplogTable" data-load='{:url("clear")}' data-confirm="确定要清空所有日志吗?" class='layui-btn layui-btn-sm layui-btn-primary'>清空日志</button>
<button data-table-id="OplogTable" data-load='{:url("clear")}' data-confirm="确定要清空所有日志吗?" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('清空数据')}</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="think-box-shadow">
{include file='oplog/index_search'}
<table id="OplogTable" data-url="{:sysuri()}" data-target-search="form.form-search"></table>
<table id="OplogTable" data-url="{:request()->url()}" data-target-search="form.form-search"></table>
</div>
{/block}
@ -26,14 +26,14 @@
cols: [[
{checkbox: true},
{field: 'id', title: 'ID', width: 80, sort: true, align: 'center'},
{field: 'username', title: '操作账号', minWidth: 100, sort: true, align: 'center'},
{field: 'node', title: '操作节点', minWidth: 120},
{field: 'action', title: '操作行为', minWidth: 120},
{field: 'content', title: '操作描述', minWidth: 150},
{field: 'geoip', title: '访问地址', minWidth: 100},
{field: 'geoisp', title: '网络服务商', minWidth: 100},
{field: 'create_at', title: '操作时间', minWidth: 170, align: 'center', sort: true},
{toolbar: '#toolbar', title: '操作面板', align: 'center', minWidth: 80, fixed: 'right'}
{field: 'username', title: '{:lang("操作账号")}', minWidth: 100, width: '8%', sort: true, align: 'center'},
{field: 'node', title: '{:lang("操作节点")}', minWidth: 120},
{field: 'action', title: '{:lang("操作行为")}', minWidth: 120},
{field: 'content', title: '{:lang("操作内容")}', minWidth: 150},
{field: 'geoip', title: '{:lang("访问地址")}', minWidth: 100, width: '10%'},
{field: 'geoisp', title: '{:lang("网络服务商")}', minWidth: 100},
{field: 'create_at', title: '{:lang("创建时间")}', minWidth: 170, align: 'center', sort: true},
{toolbar: '#toolbar', title: '{:lang("操作面板")}', align: 'center', minWidth: 80, width: '8%', fixed: 'right'}
]]
});
});
@ -41,7 +41,7 @@
<script type="text/html" id="toolbar">
<!--{if auth('remove')}-->
<a data-action='{:url("remove")}' data-value="id#{{d.id}}" data-confirm="确认要删除这条记录吗?" class="layui-btn layui-btn-sm layui-btn-danger"> </a>
<a data-action='{:url("remove")}' data-value="id#{{d.id}}" data-confirm="确认要删除这条记录吗?" class="layui-btn layui-btn-sm layui-btn-danger">{:lang('删 除')}</a>
<!--{/if}-->
</script>
{/block}

View File

@ -1,12 +1,12 @@
<fieldset>
<legend>条件搜索</legend>
<legend>{:lang('条件搜索')}</legend>
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">操作账号</label>
<label class="layui-form-label">{:lang('操作账号')}</label>
<div class="layui-input-inline">
<select name='username' lay-search class="layui-select">
<option value=''>-- 全部账号 --</option>
<option value=''>-- {:lang('全部')} --</option>
{foreach $users as $user}{if $user eq input('get.username')}
<option selected value="{$user}">{$user}</option>
{else}
@ -17,10 +17,10 @@
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">操作行为</label>
<label class="layui-form-label">{:lang('操作行为')}</label>
<div class="layui-input-inline">
<select name="action" lay-search class="layui-select">
<option value=''>-- 全部行为 --</option>
<option value=''>-- {:lang('全部')} --</option>
{foreach $actions as $action}{if $action eq input('get.action')}
<option selected value="{$action}">{$action}</option>
{else}
@ -31,37 +31,37 @@
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">操作节点</label>
<label class="layui-form-label">{:lang('操作节点')}</label>
<label class="layui-input-inline">
<input name="node" value="{$get.node|default=''}" placeholder="请输入操作内容" class="layui-input">
<input name="node" value="{$get.node|default=''}" placeholder="{:lang('请输入操作节点')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">操作描述</label>
<label class="layui-form-label">{:lang('操作内容')}</label>
<label class="layui-input-inline">
<input name="content" value="{$get.content|default=''}" placeholder="请输入操作内容" class="layui-input">
<input name="content" value="{$get.content|default=''}" placeholder="{:lang('请输入操作内容')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">访问地址</label>
<label class="layui-form-label">{:lang('访问地址')}</label>
<label class="layui-input-inline">
<input name="geoip" value="{$get.geoip|default=''}" placeholder="请输入访问地址" class="layui-input">
<input name="geoip" value="{$get.geoip|default=''}" placeholder="{:lang('请输入访问地址')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">操作时间</label>
<label class="layui-form-label">{:lang('创建时间')}</label>
<label class="layui-input-inline">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="请选择操作时间" class="layui-input">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="{:lang('请选择创建时间')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<button type="submit" class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
<button type="submit" class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> {:lang('搜 索')}</button>
<button type="button" data-form-export="{:url('index')}?type={$type|default=''}" class="layui-btn layui-btn-primary">
<i class="layui-icon layui-icon-export"></i>
<i class="layui-icon layui-icon-export"></i> {:lang('导 出')}
</button>
</div>
</form>
@ -77,53 +77,11 @@
});
// 设置表头内容
data.unshift(['ID', '操作账号', '访问节点', '访问IP地址', '访问地理区域', '访问操作', '操作内容', '操作时间']);
data.unshift(['ID', '{:lang("操作账号")}', '{:lang("操作节点")}', '{:lang("访问地址")}', '{:lang("网络服务商")}', '{:lang("操作行为")}', '{:lang("操作内容")}', '{:lang("创建时间")}']);
// 自动计算列序号
var lastCol = layui.excel.numToTitle(data[0].length || 0);
// 应用表格样式
return this.withStyle(data, {A: 60, B: 80, C: 99, E: 120, G: 120});
// 设置表头样式
layui.excel.setExportCellStyle(data, 'A1:' + lastCol + '1', {
s: {
font: {sz: 12, bold: true, color: {rgb: "FFFFFF"}, name: '微软雅黑', shadow: true},
fill: {bgColor: {indexed: 64}, fgColor: {rgb: "5FB878"}},
alignment: {vertical: 'center', horizontal: 'center'}
}
});
// 设置内容样式
var style1 = {
font: {sz: 10, shadow: true, name: '微软雅黑'},
fill: {bgColor: {indexed: 64}, fgColor: {rgb: "EAEAEA"}},
alignment: {vertical: 'center', horizontal: 'center'}
}, style2 = {
font: {sz: 10, shadow: true, name: '微软雅黑'},
fill: {bgColor: {indexed: 64}, fgColor: {rgb: "FFFFFF"}},
alignment: {vertical: 'center', horizontal: 'center'}
};
// 动态应用样式
layui.excel.setExportCellStyle(data, 'A2:' + lastCol + data.length, {s: style1}, function (raw, cell, list, conf, rows, cols) {
// @var raw 原有单元格数据
// @var cell 新的单元格数据
// @var list 所在行数据列表
// @var conf 当前样式配置
// @var rows 当前行的标号
// @var cols 当前列的标号
return (rows % 2 === 0) ? cell : (Object.assign({}, cell, {s: style2}));
});
// 设置表格行宽高,需要设置最后的行或列宽高,否则部分不生效
var rowsC = {1: 33}, colsC = {A: 60, B: 80, C: 99, E: 80, G: 120};
rowsC[data.length] = 28, colsC[lastCol] = 160;
this.options.extend = {
'!rows': layui.excel.makeRowConfig(rowsC, 28), // 设置每行高度,默认 33
'!cols': layui.excel.makeColConfig(colsC, 99), // 设置每行宽度,默认 99
};
// 其他更多样式,可以配置 this.options.extend 参数,每次执行 bind 会被重置
// 在线文档http://excel.wj2015.com/_book/docs/%E5%87%BD%E6%95%B0%E5%88%97%E8%A1%A8/%E6%A0%B7%E5%BC%8F%E8%AE%BE%E7%BD%AE%E7%9B%B8%E5%85%B3%E5%87%BD%E6%95%B0.html
return data;
}, '操作日志' + layui.util.toDateString(Date.now(), '_yyyyMMdd_HHmmss'));
}, '{:lang("操作日志")}' + layui.util.toDateString(Date.now(), '_yyyyMMdd_HHmmss'));
});
</script>

View File

@ -1,36 +1,49 @@
{extend name='table'}
{block name="button"}
<!--{if isset($super) and $super}-->
<a data-table-id="QueueTable" class="layui-btn layui-btn-sm layui-btn-primary" data-queue="{:url('admin/api.plugs/optimize')}">优化数据库</a>
<!--{/if}-->
<!--{if isset($super) and $super and $iswin}-->
<button data-load='{:url("admin/api.queue/start")}' class='layui-btn layui-btn-sm layui-btn-primary'>开启服务</button>
<button data-load='{:url("admin/api.queue/stop")}' class='layui-btn layui-btn-sm layui-btn-primary'>关闭服务</button>
<!--{/if}-->
{if isset($super) and $super}
<!--{if auth("clean")}-->
<button data-table-id="QueueTable" data-queue='{:url("clean")}' class='layui-btn layui-btn-sm layui-btn-primary'>定时清理</button>
<!--{/if}-->
<a data-table-id="QueueTable" class="layui-btn layui-btn-sm layui-btn-primary" data-queue="{:url('admin/api.plugs/optimize')}">{:lang('优化数据库')}</a>
{if isset($iswin) and ($iswin or php_sapi_name() eq 'cli')}
<button data-load='{:url("admin/api.queue/start")}' class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('开启服务')}</button>
<button data-load='{:url("admin/api.queue/stop")}' class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('关闭服务')}</button>
{/if}
{if auth("clean")}
<button data-table-id="QueueTable" data-queue='{:url("clean")}' class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('定时清理')}</button>
{/if}
{/if}
{if auth("remove")}
<button data-table-id="QueueTable" data-action='{:url("remove")}' data-rule="id#{id}" data-confirm="{:lang('确定批量删除记录吗?')}" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('批量删除')}</button>
{/if}
<!--{if auth("remove")}-->
<button data-table-id="QueueTable" data-action='{:url("remove")}' data-rule="id#{id}" data-confirm="确定批量删除记录吗?" class='layui-btn layui-btn-sm layui-btn-primary'>批量删除</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="think-box-notify">
<div class="think-box-notify" type="info">
<!--{if isset($super) and $super}-->
<b>服务状态:</b><b class="margin-right-5" data-queue-message><span class="color-desc">检查中</span></b>
<b data-tips-text="点击可复制【服务启动指令】" class="layui-icon pointer margin-right-20" data-copy="{$command|default=''}">&#xe60e;</b>
<b>{:lang('服务状态')}</b><b class="margin-right-5" data-queue-message><span class="color-desc">{:lang('检查中')}</span></b>
<b data-tips-text="{:lang('点击可复制【服务启动指令】')}" class="layui-icon pointer margin-right-20" data-copy="{$command|default=''}">&#xe633;</b>
<script>$('[data-queue-message]').load('{:sysuri("admin/api.queue/status")}');</script>
<!--{/if}-->
<b>任务统计:</b>待处理 <b class="color-text" data-extra="pre">{$total.pre|default=0}</b> 个任务,处理中 <b class="color-blue" data-extra="dos">{$total.dos|default=0}</b> 个任务,已完成 <b class="color-green" data-extra="oks">{$total.oks|default=0}</b> 个任务,已失败 <b class="color-red" data-extra="ers">{$total.ers|default=0}</b> 个任务。
<b>{:lang('任务统计')}</b>{:lang('待处理 %s 个任务,处理中 %s 个任务,已完成 %s 个任务,已失败 %s 个任务。', [
'<b class="color-text" data-extra="pre">..</b>',
'<b class="color-blue" data-extra="dos">..</b>',
'<b class="color-green" data-extra="oks">..</b>',
'<b class="color-red" data-extra="ers">..</b>'
])}
</div>
<div class="think-box-shadow">
{include file='queue/index_search'}
<table id="QueueTable" data-line="2" data-url="{:sysuri()}" data-target-search="form.form-search"></table>
<table id="QueueTable" data-line="2" data-url="{:request()->url()}" data-target-search="form.form-search"></table>
</div>
{/block}
@ -49,13 +62,13 @@
cols: [[
{checkbox: true, fixed: 'left'},
{
field: 'id', title: '任务名称', width: '25%', sort: true, templet: function (d) {
field: 'id', title: '{:lang("任务名称")}', width: '25%', sort: true, templet: function (d) {
if (d.loops_time > 0) {
d.one = '<span class="layui-badge think-bg-blue">循</span>';
} else {
d.one = '<span class="layui-badge think-bg-red">次</span>';
}
if (d.rscript === 1) {
if (parseInt(d.rscript) === 1) {
d.two = '<span class="layui-badge layui-bg-green">复</span>';
} else {
d.two = '<span class="layui-badge think-bg-violet">单</span>';
@ -64,7 +77,7 @@
}
},
{
field: 'exec_time', title: '任务计划', width: '25%', templet: function (d) {
field: 'exec_time', title: '{:lang("任务计划")}', width: '25%', templet: function (d) {
d.html = '执行指令:' + d.command + '<br>计划执行:' + d.exec_time;
if (d.loops_time > 0) {
return d.html + ' ( 每 <b class="color-blue">' + d.loops_time + '</b> 秒 ) ';
@ -74,7 +87,7 @@
}
},
{
field: 'loops_time', title: '执行状态', width: '30%', templet: function (d) {
field: 'loops_time', title: '{:lang("任务状态")}', width: '30%', templet: function (d) {
d.html = ([
'<span class="pull-left layui-badge layui-badge-middle layui-bg-gray">未知</span>',
'<span class="pull-left layui-badge layui-badge-middle layui-bg-black">等待</span>',
@ -82,9 +95,10 @@
'<span class="pull-left layui-badge layui-badge-middle layui-bg-green">完成</span>',
'<span class="pull-left layui-badge layui-badge-middle layui-bg-red">失败</span>',
][d.status] || '') + '执行时间:';
d.enter_time = d.enter_time || '', d.outer_time = d.outer_time || '0.0000';
d.enter_time = d.enter_time || '';
d.outer_time = d.outer_time || '0.0000';
if (d.enter_time.length > 12) {
d.html += d.enter_time.substring(12) + '<span class="color-desc"> ( 耗时 ' + d.outer_time + ' ) </span>';
d.html += d.enter_time.substring(12) + '<span class="color-desc"> ( ' + d.outer_time + ' ) </span>';
d.html += ' 已执行 <b class="color-blue">' + (d.attempts || 0) + '</b> 次';
} else {
d.html += '<span class="color-desc">任务未执行</span>'
@ -92,7 +106,7 @@
return d.html + '<br>执行结果:<span class="color-blue">' + (d.exec_desc || '<span class="color-desc">未获取到执行结果</span>') + '</span>';
}
},
{toolbar: '#toolbar', title: '操作面板', align: 'center', minWidth: 210, fixed: 'right'}
{toolbar: '#toolbar', title: '{:lang("操作面板")}', align: 'center', minWidth: 210, fixed: 'right'}
]]
});
});
@ -102,16 +116,16 @@
<!--{if auth('redo')}-->
{{# if(d.status===4||d.status===3){ }}
<a class="layui-btn layui-btn-sm" data-confirm="确定要重置该任务吗?" data-queue="{:url('redo')}?code={{d.code}}"> </a>
<a class="layui-btn layui-btn-sm" data-confirm="确定要重置该任务吗?" data-queue="{:url('redo')}?code={{d.code}}">{:lang('重 置')}</a>
{{# }else{ }}
<a class="layui-btn layui-btn-sm layui-btn-disabled"> </a>
<a class="layui-btn layui-btn-sm layui-btn-disabled">{:lang('重 置')}</a>
{{# } }}
<!--{/if}-->
<!--{if auth('remove')}-->
<a class='layui-btn layui-btn-sm layui-btn-danger' data-confirm="确定要删除该任务吗?" data-action='{:url("remove")}' data-value="id#{{d.id}}"> </a>
<a class='layui-btn layui-btn-sm layui-btn-danger' data-confirm="{:lang('确定要删除该记录吗?')}" data-action='{:url("remove")}' data-value="id#{{d.id}}">{:lang('删 除')}</a>
<!--{/if}-->
<a class='layui-btn layui-btn-sm layui-btn-normal' onclick="$.loadQueue('{{d.code}}',false,this)"> </a>
<a class='layui-btn layui-btn-sm layui-btn-normal' onclick="$.loadQueue('{{d.code}}',false,this)">{:lang('日 志')}</a>
</script>
{/block}

View File

@ -1,27 +1,27 @@
<fieldset>
<legend>条件搜索</legend>
<legend>{:lang('条件搜索')}</legend>
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">编号名称</label>
<label class="layui-form-label">{:lang('编号名称')}</label>
<label class="layui-input-inline">
<input name="title" value="{$get.title|default=''}" placeholder="请输入名称或编号" class="layui-input">
<input name="title" value="{$get.title|default=''}" placeholder="{:lang('请输入名称或编号')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">任务指令</label>
<label class="layui-form-label">{:lang('任务指令')}</label>
<label class="layui-input-inline">
<input name="command" value="{$get.command|default=''}" placeholder="请输入任务指令" class="layui-input">
<input name="command" value="{$get.command|default=''}" placeholder="{:lang('请输入任务指令')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">任务状态</label>
<label class="layui-form-label">{:lang('任务状态')}</label>
<label class="layui-input-inline">
<select name="status" class="layui-select">
<option value=''>-- 全部任务 --</option>
{foreach ['1'=>'等待处理','2'=>'正在处理','3'=>'处理完成','4'=>'处理失败'] as $k=>$v}
<option value=''>-- {:lang('全部')} --</option>
{foreach ['1'=>lang('等待处理'),'2'=>lang('正在处理'),'3'=>lang('处理完成'),'4'=>lang('处理失败')] as $k=>$v}
{if isset($get.status) and $get.status eq $k}
<option selected value="{$k}">{$v}</option>
{else}
@ -32,28 +32,14 @@
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">计划时间</label>
<label class="layui-form-label">{:lang('计划时间')}</label>
<label class="layui-input-inline">
<input data-date-range name="exec_time" value="{$get.exec_time|default=''}" placeholder="请选择计划时间" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline layui-hide">
<label class="layui-form-label">执行时间</label>
<label class="layui-input-inline">
<input data-date-range name="enter_time" value="{$get.enter_time|default=''}" placeholder="请选择执行时间" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline layui-hide">
<label class="layui-form-label">创建时间</label>
<label class="layui-input-inline">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="请选择创建时间" class="layui-input">
<input data-date-range name="exec_time" value="{$get.exec_time|default=''}" placeholder="{:lang('请选择计划时间')}" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> {:lang('搜 索')}</button>
</div>
</form>
</fieldset>

View File

@ -3,7 +3,7 @@
{block name='header'}
{notempty name='title'}
<div class="layui-card-header">
<span class="layui-icon font-s10 color-desc margin-right-5">&#xe65b;</span>{$title|default=''}
<span class="layui-icon font-s10 color-desc margin-right-5">&#xe65b;</span>{$title|lang}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
@ -11,6 +11,11 @@
<div class="layui-card-line"></div>
<div class="layui-card-body">
<div class="layui-card-table">
{notempty name='showErrorMessage'}
<div class="think-box-notify" type="error">
<b>{:lang('系统提示:')}</b><span>{$showErrorMessage|raw}</span>
</div>
{/notempty}
{block name='content'}{/block}
</div>
</div>

View File

@ -13,25 +13,25 @@
<label class="block relative">
<span class="help-label"><b>登录账号</b>User Name</span>
{if isset($vo) and isset($vo.username)}
<input disabled value='{$vo.username|default=""}' required class="layui-input think-bg-gray">
<input name="username" value='{$vo.username|default=""}' required readonly class="layui-input think-bg-gray">
{else}
<input name="username" value='{$vo.username|default=""}' required pattern="^.{4,}$" placeholder="请输入登录账号" class="layui-input">
<input name="username" value='{$vo.username|default=""}' required pattern="^.{4,}$" vali-name="登录账号" placeholder="请输入登录账号" class="layui-input">
{/if}
<span class="help-block">登录账号不能重复并且创建后不能再次修改哦。</span>
<span class="help-block">登录账号不能少于4位字符创建后不能再次修改.</span>
</label>
</div>
<div class="layui-col-xs5">
<label class="block relative">
<span class="help-label"><b>用户名称</b>Nick Name</span>
<input name="nickname" value='{$vo.nickname|default=""}' required placeholder="请输入用户名称" class="layui-input">
<span class="help-block">用于区分用户数据的用户名称,请尽量不要重复</span>
<input name="nickname" value='{$vo.nickname|default=""}' required vali-name="用户名称" placeholder="请输入用户名称" class="layui-input">
<span class="help-block">用于区分用户数据的用户名称,请尽量不要重复.</span>
</label>
</div>
</div>
</fieldset>
{if !empty($bases) || !empty($authorizes)}
{if !empty($bases) || !empty($auths)}
<fieldset class="layui-bg-gray">
<legend><b class="layui-badge think-bg-violet">用户权限</b></legend>
{if !empty($bases)}
@ -50,13 +50,13 @@
</div>
</div>
{/if}
{if !empty($authorizes)}
{if !empty($auths)}
<div class="layui-form-item">
<div class="help-label"><b>访问权限</b>Role Permission</div>
<div class="layui-textarea help-checks">
{if isset($vo.username) and $vo.username eq $superName}
{if isset($vo.username) and $vo.username eq $super}
<span class="color-desc padding-left-5">超级用户拥所有访问权限,不需要配置权限。</span>
{else}{foreach $authorizes as $authorize}
{else}{foreach $auths as $authorize}
<label class="think-checkbox">
{if in_array($authorize.id, $vo.authorize)}
<input type="checkbox" checked name="authorize[]" value="{$authorize.id}" lay-ignore>{$authorize.title}
@ -77,21 +77,21 @@
<div class="layui-col-xs4">
<label class="relative block">
<span class="help-label"><b>联系邮箱</b>Contact Email</span>
<input name="contact_mail" pattern="^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$" value='{$vo.contact_mail|default=""}' placeholder="请输入联系电子邮箱" class="layui-input">
<input name="contact_mail" value='{$vo.contact_mail|default=""}' pattern="email" vali-name="联系邮箱" placeholder="请输入联系电子邮箱" class="layui-input">
<span class="color-desc">可选,请填写用户常用的电子邮箱</span>
</label>
</div>
<div class="layui-col-xs4">
<label class="relative block">
<span class="help-label"><b>联系手机</b>Contact Mobile</span>
<input type="tel" maxlength="11" name="contact_phone" value='{$vo.contact_phone|default=""}' pattern="^1[3-9][0-9]{9}$" placeholder="请输入用户联系手机" class="layui-input">
<input type="tel" maxlength="11" name="contact_phone" value='{$vo.contact_phone|default=""}' pattern="phone" vali-name="联系手机" placeholder="请输入用户联系手机" class="layui-input">
<span class="color-desc">可选,请填写用户常用的联系手机号</span>
</label>
</div>
<div class="layui-col-xs4">
<label class="relative block">
<span class="help-label"><b>联系QQ</b>Contact QQ</span>
<input name="contact_qq" pattern="^\d{6,}$" value='{$vo.contact_qq|default=""}' placeholder="请输入常用的联系QQ" class="layui-input">
<input name="contact_qq" maxlength="11" value='{$vo.contact_qq|default=""}' pattern="qq" vali-name="联系QQ" placeholder="请输入常用的联系QQ" class="layui-input">
<span class="color-desc">可选,请填写用户常用的联系QQ号</span>
</label>
</div>

View File

@ -3,17 +3,17 @@
{block name="button"}
{if isset($type) and $type eq 'index'}
<!--{if auth("add")}-->
<button data-modal='{:url("add")}' data-title="添加用户" class='layui-btn layui-btn-sm layui-btn-primary'>添加用户</button>
<button data-modal='{:url("add")}' data-title="{:lang('添加用户')}" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('添加用户')}</button>
<!--{/if}-->
<!--{if auth("state")}-->
<a data-confirm="确定要启用该用户吗?" data-table-id="UserTable" data-action="{:url('state')}" data-rule="id#{id};status#0" class='layui-btn layui-btn-sm layui-btn-primary'>批量禁用</a>
<a data-confirm="确定要禁用这些用户吗?" data-table-id="UserTable" data-action="{:url('state')}" data-rule="id#{id};status#0" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('批量禁用')}</a>
<!--{/if}-->
{else}
<!--{if auth("state")}-->
<a data-confirm="确定要恢复这些账号吗?" data-table-id="UserTable" data-action="{:url('state')}" data-rule="id#{id};status#1" class='layui-btn layui-btn-sm layui-btn-primary'>批量恢复</a>
<a data-confirm="确定要恢复这些账号吗?" data-table-id="UserTable" data-action="{:url('state')}" data-rule="id#{id};status#1" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('批量恢复')}</a>
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="确定永久删除这些账号吗?" data-table-id="UserTable" data-action='{:url("remove")}' data-rule="id#{id}" class='layui-btn layui-btn-sm layui-btn-primary'>批量删除</a>
<a data-confirm="确定永久删除这些账号吗?" data-table-id="UserTable" data-action='{:url("remove")}' data-rule="id#{id}" class='layui-btn layui-btn-sm layui-btn-primary'>{:lang('批量删除')}</a>
<!--{/if}-->
{/if}
{/block}
@ -21,7 +21,7 @@
{block name="content"}
<div class="layui-tab layui-tab-card">
<ul class="layui-tab-title">
{foreach ['index'=>'系统用户','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k}
{foreach ['index'=>lang('系统用户'),'recycle'=>lang('回 收 站')] as $k=>$v}{if isset($type) and $type eq $k}
<li data-open="{:url('index')}?type={$k}" class="layui-this">{$v}</li>
{else}
<li data-open="{:url('index')}?type={$k}">{$v}</li>
@ -40,18 +40,18 @@
where: {type: '{$type|default="index"}'},
cols: [[
{checkbox: true, fixed: true},
{field: 'sort', title: '排序权重', width: 100, align: 'center', sort: true, templet: '#SortInputTpl'},
{field: 'sort', title: '{:lang("排序权重")}', width: 100, align: 'center', sort: true, templet: '#SortInputTpl'},
{
field: 'headimg', title: '头像', width: 60, align: 'center', templet: function (d) {
field: 'headimg', title: '{:lang("头像")}', width: 60, align: 'center', templet: function (d) {
if (!d.headimg) return '-';
return layui.laytpl('<div class="headimg headimg-ss shadow-inset margin-0" data-tips-image data-tips-hover data-lazy-src="{{d.headimg}}"></div>').render(d);
}
},
{field: 'username', title: '登录账号', minWidth: 100, align: 'center', templet: '<div>{{d.username||"-"}}</div>'},
{field: 'nickname', title: '用户名称', minWidth: 100, align: 'center', templet: '<div>{{d.nickname||"-"}}</div>'},
{field: 'username', title: '{:lang("登录账号")}', minWidth: 100, align: 'center', templet: '<div>{{d.username||"-"}}</div>'},
{field: 'nickname', title: '{:lang("用户名称")}', minWidth: 100, align: 'center', templet: '<div>{{d.nickname||"-"}}</div>'},
/* {notempty name='bases'} */
{
field: 'usertype', title: '角色身份', minWidth: 100, align: 'center', templet: function (d) {
field: 'usertype', title: '{:lang("角色身份")}', minWidth: 100, align: 'center', templet: function (d) {
d.userinfo = d.userinfo || {};
return d.userinfo.code ? (d.userinfo.name + ' ( ' + d.userinfo.code + ' ) ') : '-';
}
@ -59,11 +59,11 @@
/* {/notempty} */
// {field: 'contact_mail', title: '联系邮箱', minWidth: 80, templet: '<div>{{d.contact_mail||"-"}}</div>'},
// {field: 'contact_phone', title: '联系电话', minWidth: 80, templet: '<div>{{d.contact_phone||"-"}}</div>'},
{field: 'status', title: '账号状态', align: 'center', minWidth: 110, templet: '#StatusSwitchTpl'},
{field: 'login_num', title: '登录次数', align: 'center', minWidth: 100, sort: true},
{field: 'login_at', title: '最后登录', align: 'center', minWidth: 170, sort: true},
{field: 'create_at', title: '创建时间', align: 'center', minWidth: 170, sort: true},
{toolbar: '#toolbar', title: '操作面板', align: 'center', minWidth: 150, fixed: 'right'}
{field: 'status', title: '{:lang("使用状态")}', align: 'center', minWidth: 110, templet: '#StatusSwitchTpl'},
{field: 'login_num', title: '{:lang("登录次数")}', align: 'center', minWidth: 100, sort: true},
{field: 'login_at', title: '{:lang("最后登录")}', align: 'center', minWidth: 170, sort: true},
{field: 'create_at', title: '{:lang("创建时间")}', align: 'center', minWidth: 170, sort: true},
{toolbar: '#toolbar', title: '{:lang("操作面板")}', align: 'center', minWidth: 180, fixed: 'right'}
]]
});
@ -85,9 +85,9 @@
<!-- 数据状态切换模板 -->
<script type="text/html" id="StatusSwitchTpl">
<!--{if auth("state")}-->
<input type="checkbox" value="{{d.id}}" lay-skin="switch" lay-text="已激活|已禁用" lay-filter="StatusSwitch" {{-d.status>0?'checked':''}}>
<input type="checkbox" value="{{d.id}}" lay-skin="switch" lay-text="{:lang('已激活')}|{:lang('已禁用')}" lay-filter="StatusSwitch" {{-d.status>0?'checked':''}}>
<!--{else}-->
{{-d.status ? '<b class="color-green">已启用</b>' : '<b class="color-red">已禁用</b>'}}
{{-d.status ? '<b class="color-green">{:lang("已激活")}</b>' : '<b class="color-red">{:lang("已禁用")}</b>'}}
<!--{/if}-->
</script>
@ -99,17 +99,17 @@
<script type="text/html" id="toolbar">
{if isset($type) and $type eq 'index'}
<!--{if auth("edit")}-->
<a class="layui-btn layui-btn-sm" data-event-dbclick data-title="编辑用户" data-modal='{:url("edit")}?id={{d.id}}'> </a>
<a class="layui-btn layui-btn-sm" data-event-dbclick data-title="{:lang('编辑用户')}" data-modal='{:url("edit")}?id={{d.id}}'>{:lang('编 辑')}</a>
<!--{/if}-->
<!--{if auth("pass")}-->
<a class="layui-btn layui-btn-sm layui-btn-normal" data-title="设置密码" data-modal='{:url("pass")}?id={{d.id}}'> </a>
<a class="layui-btn layui-btn-sm layui-btn-normal" data-title="{:lang('设置密码')}" data-modal='{:url("pass")}?id={{d.id}}'>{:lang('密 码')}</a>
<!--{/if}-->
{else}
<!--{if auth("edit")}-->
<a class="layui-btn layui-btn-sm" data-event-dbclick data-title="编辑用户" data-modal='{:url("edit")}?id={{d.id}}'> </a>
<a class="layui-btn layui-btn-sm" data-event-dbclick data-title="{:lang('编辑用户')}" data-modal='{:url("edit")}?id={{d.id}}'>{:lang('编 辑')}</a>
<!--{/if}-->
<!--{if auth("remove")}-->
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确定要永久删除此账号吗?" data-action="{:url('remove')}" data-value="id#{{d.id}}"> </a>
<a class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="{:lang('确定要永久删除吗?')}" data-action="{:url('remove')}" data-value="id#{{d.id}}">{:lang('删 除')}</a>
<!--{/if}-->
{/if}
</script>

View File

@ -1,18 +1,18 @@
<form class="layui-form layui-form-pane form-search" action="{:sysuri()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">账号名称</label>
<label class="layui-form-label">{:lang('账号名称')}</label>
<label class="layui-input-inline">
<input name="username" value="{$get.username|default=''}" placeholder="请输入账号或名称" class="layui-input">
<input name="username" value="{$get.username|default=''}" placeholder="{:lang('请输入账号或名称')}" class="layui-input">
</label>
</div>
<!--{notempty name='bases'}-->
<div class="layui-form-item layui-inline">
<label class="layui-form-label">角色身份</label>
<label class="layui-form-label">{:lang('角色身份')}</label>
<div class="layui-input-inline">
<select name="usertype" lay-search class="layui-select">
<option value=''>-- 全部 --</option>
<option value=''>-- {:lang('全部')} --</option>
{foreach $bases as $base}{if $base.code eq input('get.usertype')}
<option selected value="{$base.code|default=''}">{$base.name|default=''} ( {$base.code|default=''} )</option>
{else}
@ -23,36 +23,22 @@
</div>
<!--{/notempty}-->
<div class="layui-form-item layui-inline layui-hide">
<label class="layui-form-label">联系手机</label>
<label class="layui-input-inline">
<input name="phone" value="{$get.phone|default=''}" placeholder="请输入联系手机" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline layui-hide">
<label class="layui-form-label">联系邮箱</label>
<label class="layui-input-inline">
<input name="mail" value="{$get.mail|default=''}" placeholder="请输入联系邮箱" class="layui-input">
</label>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">最后登录</label>
<label class="layui-form-label">{:lang('最后登录')}</label>
<div class="layui-input-inline">
<input data-date-range name="login_at" value="{$get.login_at|default=''}" placeholder="请选择登录时间" class="layui-input">
<input data-date-range name="login_at" value="{$get.login_at|default=''}" placeholder="{:lang('请选择登录时间')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">创建时间</label>
<label class="layui-form-label">{:lang('创建时间')}</label>
<div class="layui-input-inline">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="请选择创建时间" class="layui-input">
<input data-date-range name="create_at" value="{$get.create_at|default=''}" placeholder="{:lang('请选择创建时间')}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<input type="hidden" name="type" value="{$type|default='index'}">
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> 搜 索</button>
<button class="layui-btn layui-btn-primary"><i class="layui-icon">&#xe615;</i> {:lang('搜 索')}</button>
</div>
</form>

View File

@ -1,43 +1,33 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card" data-table-id="UserTable">
<div class="layui-card-body padding-left-40">
<div class="layui-form-item">
<label class="relative block">
<span class="help-label"><b>登录用户账号</b>Username</span>
<!--{if isset($vo) and isset($vo.username)}-->
<input disabled value='{$vo.username|default=""}' class="layui-input think-bg-gray">
<!--{else}-->
<input name="username" value='{$vo.username|default=""}' required pattern="^.{4,}$" placeholder="请输入4位及以上字符登录用户账号" class="layui-input">
<!--{/if}-->
</label>
<p class="help-block">登录用户账号创建后,不允许再次修改。</p>
</div>
<label class="layui-form-item relative block">
<span class="help-label"><b>登录用户账号</b>Username</span>
<input disabled value='{$vo.username|default=""}' class="layui-input think-bg-gray">
<span class="help-block">登录用户账号创建后,不允许再次修改。</span>
</label>
<!--{if $verify}-->
<div class="layui-form-item">
<label class="relative block">
<span class="help-label"><b>旧的登录密码</b>Old Password</span>
<input type="password" autofocus name="oldpassword" value='' pattern="^\S{1,}$" required placeholder="请输入旧的登录密码" class="layui-input">
</label>
<p class="color-desc">请输入旧密码来验证修改权限,旧密码不限制格式。</p>
</div>
<label class="layui-form-item relative block">
<span class="help-label"><b>旧的登录密码</b>Old Password</span>
<input type="password" autofocus name="oldpassword" value='' pattern="^\S{1,}$" required vali-name="验证密码" placeholder="请输入旧的登录密码" class="layui-input">
<span class="color-desc">请输入旧密码来验证修改权限,旧密码不限制格式。</span>
</label>
<!--{/if}-->
<div class="layui-form-item">
<label class="relative block">
<span class="help-label"><b>新的登录密码</b>New Password</span>
<input type="password" name="password" maxlength="32" pattern="^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,32}$" required placeholder="请输入新的登录密码" class="layui-input">
</label>
<p class="color-desc">密码必须包含大小写字母、数字、符号的任意两者组合。</p>
</div>
<label class="layui-form-item relative block">
<span class="help-label"><b>新的登录密码</b>New Password</span>
<input type="password" name="password" maxlength="32" pattern="^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,32}$" required vali-name="登录密码" placeholder="请输入新的登录密码" class="layui-input">
<span class="color-desc">密码必须包含大小写字母、数字、符号的任意两者组合。</span>
</label>
<label class="layui-form-item relative block">
<span class="help-label"><b>重复登录密码</b>Repeat Password</span>
<input type="password" name="repassword" maxlength="32" pattern="^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,32}$" required vali-name="重复密码" placeholder="请重复输入登录密码" class="layui-input">
<span class="color-desc">密码必须包含大小写字母、数字、符号的任意两者组合。</span>
</label>
<div class="layui-form-item">
<label class="relative block">
<span class="help-label"><b>重复登录密码</b>Repeat Password</span>
<input type="password" name="repassword" maxlength="32" pattern="^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,32}$" required placeholder="请重复输入登录密码" class="layui-input">
</label>
<p class="color-desc">密码必须包含大小写字母、数字、符号的任意两者组合。</p>
</div>
</div>
<div class="hr-line-dashed"></div>

View File

@ -1,81 +0,0 @@
<?php
namespace app\data\command;
use app\data\model\ShopOrder;
use app\data\model\ShopOrderItem;
use app\data\service\OrderService;
use think\admin\Command;
use think\admin\Exception;
use think\console\Input;
use think\console\Output;
use think\Model;
/**
* 商城订单自动清理
* Class OrderClean
* @package app\data\command
*/
class OrderClean extends Command
{
protected function configure()
{
$this->setName('xdata:OrderClean');
$this->setDescription('批量清理商城订单数据');
}
/**
* 业务指令执行
* @param Input $input
* @param Output $output
* @return void
* @throws Exception
*/
protected function execute(Input $input, Output $output)
{
$this->_autoCancelOrder();
$this->_autoRemoveOrder();
}
/**
* 自动取消30分钟未支付的订单
* @throws Exception
*/
private function _autoCancelOrder()
{
try {
$map = [['status', '<', 3], ['payment_status', '=', 0]];
$map[] = ['create_at', '<', date('Y-m-d H:i:s', strtotime('-30 minutes'))];
[$count, $total] = [0, ($result = ShopOrder::mk()->where($map)->select())->count()];
$result->map(function (Model $item) use ($total, &$count) {
$this->queue->message($total, ++$count, "开始取消未支付的订单 {$item['order_no']}");
$item->save(['status' => 0, 'cancel_status' => 1, 'cancel_datetime' => date('Y-m-d H:i:s'), 'cancel_remark' => '自动取消30分钟未完成支付']);
OrderService::stock($item['order_no']);
$this->queue->message($total, $count, "完成取消未支付的订单 {$item['order_no']}", 1);
});
} catch (\Exception $exception) {
$this->queue->error($exception->getMessage());
}
}
/**
* 自动清理已经取消的订单
* @throws Exception
*/
private function _autoRemoveOrder()
{
try {
$map = [['status', '=', 0], ['payment_status', '=', 0]];
$map[] = ['create_at', '<', date('Y-m-d H:i:s', strtotime('-3 days'))];
[$count, $total] = [0, ($result = ShopOrder::mk()->where($map)->select())->count()];
$result->map(function (Model $item) use ($total, &$count) {
$this->queue->message($total, ++$count, "开始清理已取消的订单 {$item['order_no']}");
ShopOrder::mk()->where(['order_no' => $item['order_no']])->delete();
ShopOrderItem::mk()->where(['order_no' => $item['order_no']])->delete();
$this->queue->message($total, $count, "完成清理已取消的订单 {$item['order_no']}", 1);
});
} catch (\Exception $exception) {
$this->queue->error($exception->getMessage());
}
}
}

View File

@ -1,79 +0,0 @@
<?php
namespace app\data\command;
use app\data\model\DataUser;
use app\data\service\UserUpgradeService;
use think\admin\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
/**
* 更新用户代理关系
* Class UserAgent
* @package app\data\command
*/
class UserAgent extends Command
{
protected function configure()
{
$this->setName('xdata:UserAgent');
$this->addArgument('uuid', Argument::OPTIONAL, '目标用户', '');
$this->addArgument('puid', Argument::OPTIONAL, '上级代理', '');
$this->setDescription('重新设置用户上级代理, 参数UUID PUID');
}
/**
* @param Input $input
* @param Output $output
* @return void
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
protected function execute(Input $input, Output $output)
{
[$uuid, $puid] = [$input->getArgument('uuid'), $input->getArgument('puid')];
if (empty($uuid)) $this->setQueueError("参数UID无效请传入正确的参数!");
if (empty($puid)) $this->setQueueError("参数PID无效请传入正确的参数!");
// 检查当前用户资料
$user = DataUser::mk()->where(['id' => $uuid])->find();
if (empty($user)) $this->setQueueError("读取用户数据失败!");
// 检查上级代理用户
$parant = DataUser::mk()->where(['id' => $puid])->find();
if (empty($parant)) $this->setQueueError('读取代理数据失败!');
// 检查异常关系处理
if (stripos($parant['path'], "-{$user['id']}-")) {
$this->setQueueError('不能把下级设置为代理!');
}
// 更新自己的代理关系
$path1 = rtrim($parant['path'] ?: '-', '-') . "-{$parant['id']}-";
DataUser::mk()->where(['id' => $user['id']])->update([
'path' => $path1, 'layer' => substr_count($path1, '-'),
'pid0' => $parant['id'], 'pid1' => $parant['id'], 'pid2' => $parant['pid1'],
]);
UserUpgradeService::upgrade($user['id'], true);
$this->setQueueMessage(1, 1, "更新指定用户[{$user['id']}]代理绑定成功!");
// 更新下级的代理关系
$path2 = "{$user['path']}{$user['id']}-";
[$total, $count] = [DataUser::mk()->whereLike('path', "{$path2}%")->count(), 0];
foreach (DataUser::mk()->whereLike('path', "{$path2}%")->order('layer desc')->select() as $vo) {
$this->setQueueMessage($total, ++$count, "开始更新下级用户[{$vo['id']}]代理绑定!");
// 更新下级用户代理数据
$path3 = preg_replace("#^{$path2}#", "{$path1}{$user['id']}-", $vo['path']);
$attrs = array_reverse(str2arr($path3, '-'));
DataUser::mk()->where(['id' => $vo['id']])->update([
'path' => $path3, 'layer' => substr_count($path3, '-'),
'pid0' => $attrs[0] ?? 0, 'pid1' => $attrs[0] ?? 0, 'pid2' => $attrs[1] ?? 0,
]);
$this->setQueueMessage($total, $count, "完成更新下级用户[{$vo['id']}]代理绑定!", 1);
}
}
}

View File

@ -1,45 +0,0 @@
<?php
namespace app\data\command;
use app\data\model\DataUser;
use app\data\service\UserBalanceService;
use app\data\service\UserRebateService;
use think\admin\Command;
use think\admin\Exception;
use think\console\Input;
use think\console\Output;
/**
* 用户余额及返利重算处理
* Class UserBalance
* @package app\data\command
*/
class UserAmount extends Command
{
protected function configure()
{
$this->setName('xdata:UserAmount');
$this->setDescription('批量重新计算余额返利');
}
/**
* @param Input $input
* @param Output $output
* @return void
* @throws Exception
*/
protected function execute(Input $input, Output $output)
{
[$total, $count, $error] = [DataUser::mk()->count(), 0, 0];
foreach (DataUser::mk()->field('id')->cursor() as $user) try {
$this->queue->message($total, ++$count, "刷新用户 [{$user['id']}] 余额及返利开始");
UserRebateService::amount($user['id']) && UserBalanceService::amount($user['id']);
$this->queue->message($total, $count, "刷新用户 [{$user['id']}] 余额及返利完成", 1);
} catch (\Exception $exception) {
$error++;
$this->queue->message($total, $count, "刷新用户 [{$user['id']}] 余额及返利失败, {$exception->getMessage()}", 1);
}
$this->setQueueSuccess("此次共处理 {$total} 个刷新操作, 其中有 {$error} 个刷新失败。");
}
}

View File

@ -1,262 +0,0 @@
<?php
namespace app\data\command;
use app\data\model\DataUser;
use app\data\model\DataUserTransfer;
use app\data\service\UserRebateService;
use think\admin\Command;
use think\admin\Exception;
use think\admin\storage\LocalStorage;
use think\console\Input;
use think\console\Output;
use WePay\Transfers;
use WePay\TransfersBank;
/**
* 用户提现处理
* Class UserTransfer
* @package app\data\command
*/
class UserTransfer extends Command
{
protected function configure()
{
$this->setName('xdata:UserTransfer');
$this->setDescription('批量执行线上打款操作');
}
/**
* 执行微信提现操作
* @param Input $input
* @param Output $output
* @return void
* @throws \think\admin\Exception
*/
protected function execute(Input $input, Output $output)
{
$map = [['type', 'in', ['wechat_banks', 'wechat_wallet']], ['status', 'in', [3, 4]]];
[$total, $count, $error] = [DataUserTransfer::mk()->where($map)->count(), 0, 0];
foreach (DataUserTransfer::mk()->where($map)->cursor() as $vo) try {
$this->queue->message($total, ++$count, "开始处理订单 {$vo['code']} 提现");
if ($vo['status'] === 3) {
$this->queue->message($total, $count, "尝试处理订单 {$vo['code']} 打款", 1);
if ($vo['type'] === 'wechat_banks') {
[$config, $result] = $this->createTransferBank($vo);
} else {
[$config, $result] = $this->createTransferWallet($vo);
}
if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') {
DataUserTransfer::mk()->where(['code' => $vo['code']])->update([
'status' => 4,
'appid' => $config['appid'],
'openid' => $config['openid'],
'trade_no' => $result['partner_trade_no'],
'trade_time' => $result['payment_time'] ?? date('Y-m-d H:i:s'),
'change_time' => date('Y-m-d H:i:s'),
'change_desc' => '创建微信提现成功',
]);
} else {
DataUserTransfer::mk()->where(['code' => $vo['code']])->update([
'change_time' => date('Y-m-d H:i:s'), 'change_desc' => $result['err_code_des'] ?? '线上提现失败',
]);
}
} elseif ($vo['status'] === 4) {
$this->queue->message($total, $count, "刷新提现订单 {$vo['code']} 状态", 1);
if ($vo['type'] === 'wechat_banks') {
$this->queryTransferBank($vo);
} else {
$this->queryTransferWallet($vo);
}
}
} catch (\Exception $exception) {
$error++;
$this->queue->message($total, $count, "处理提现订单 {$vo['code']} 失败, {$exception->getMessage()}", 1);
DataUserTransfer::mk()->where(['code' => $vo['code']])->update([
'change_time' => date('Y-m-d H:i:s'), 'change_desc' => $exception->getMessage(),
]);
}
$this->setQueueSuccess("此次共处理 {$total} 笔提现操作, 其中有 {$error} 笔处理失败。");
}
/**
* 尝试提现转账到银行卡
* @param array $item
* @return array [config, result]
* @throws \WeChat\Exceptions\InvalidDecryptException
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function createTransferBank(array $item): array
{
$config = $this->getConfig($item['uuid']);
return [$config, TransfersBank::instance($config)->create([
'partner_trade_no' => $item['code'],
'enc_bank_no' => $item['bank_code'],
'enc_true_name' => $item['bank_user'],
'bank_code' => $item['bank_wseq'],
'amount' => intval($item['amount'] - $item['charge_amount']) * 100,
'desc' => '微信银行卡提现',
])];
}
/**
* 获取微信提现参数
* @param int $uuid
* @return array
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function getConfig(int $uuid): array
{
$data = sysdata('TransferWxpay');
if (empty($data)) throw new Exception('未配置微信提现商户');
// 商户证书文件处理
$local = LocalStorage::instance();
if (!$local->has($file1 = "{$data['wechat_mch_id']}_key.pem", true)) {
$local->set($file1, $data['wechat_mch_key_text'], true);
}
if (!$local->has($file2 = "{$data['wechat_mch_id']}_cert.pem", true)) {
$local->set($file2, $data['wechat_mch_cert_text'], true);
}
// 获取用户支付信息
$result = $this->getWechatInfo($uuid, $data['wechat_type']);
if (empty($result)) throw new Exception('无法读取打款数据');
return [
'appid' => $result[0],
'openid' => $result[1],
'mch_id' => $data['wechat_mch_id'],
'mch_key' => $data['wechat_mch_key'],
'ssl_key' => $local->path($file1, true),
'ssl_cer' => $local->path($file2, true),
'cache_path' => $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . 'wechat',
];
}
/**
* 根据配置获取用户OPENID
* @param int $uuid
* @param string $type
* @return mixed|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function getWechatInfo(int $uuid, string $type): ?array
{
$user = DataUser::mk()->where(['id' => $uuid])->find();
if (empty($user)) return null;
$appid1 = sysconf('data.wxapp_appid');
if (strtolower(sysconf('wechat.type')) === 'api') {
$appid2 = sysconf('wechat.appid');
} else {
$appid2 = sysconf('wechat.thr_appid');
}
if ($type === 'normal') {
if (!empty($user['openid1'])) return [$appid1, $user['openid1']];
if (!empty($user['openid2'])) return [$appid2, $user['openid2']];
}
if ($type === 'wxapp' && !empty($user['openid1'])) {
return [$appid1, $user['openid1']];
}
if ($type === 'wechat' && !empty($user['openid2'])) {
return [$appid2, $user['openid2']];
}
return null;
}
/**
* 尝试提现转账到微信钱包
* @param array $item
* @return array [config, result]
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function createTransferWallet(array $item): array
{
$config = $this->getConfig($item['uuid']);
return [$config, Transfers::instance($config)->create([
'openid' => $config['openid'],
'amount' => intval($item['amount'] - $item['charge_amount']) * 100,
'partner_trade_no' => $item['code'],
'spbill_create_ip' => '127.0.0.1',
'check_name' => 'NO_CHECK',
'desc' => '微信余额提现',
])];
}
/**
* 查询更新提现打款状态
* @param array $item
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function queryTransferBank(array $item)
{
$config = $this->getConfig($item['uuid']);
[$config['appid'], $config['openid']] = [$item['appid'], $item['openid']];
$result = TransfersBank::instance($config)->query($item['trade_no']);
if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') {
if ($result['status'] === 'SUCCESS') {
DataUserTransfer::mk()->where(['code' => $item['code']])->update([
'status' => 5,
'appid' => $config['appid'],
'openid' => $config['openid'],
'trade_time' => $result['pay_succ_time'] ?: date('Y-m-d H:i:s'),
'change_time' => date('Y-m-d H:i:s'),
'change_desc' => '微信提现打款成功',
]);
}
if (in_array($result['status'], ['FAILED', 'BANK_FAIL'])) {
DataUserTransfer::mk()->where(['code' => $item['code']])->update([
'status' => 0,
'change_time' => date('Y-m-d H:i:s'),
'change_desc' => '微信提现打款失败',
]);
// 刷新用户可提现余额
UserRebateService::amount($item['uuid']);
}
}
}
/**
* 查询更新提现打款状态
* @param array $item
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function queryTransferWallet(array $item)
{
$config = $this->getConfig($item['uuid']);
[$config['appid'], $config['openid']] = [$item['appid'], $item['openid']];
$result = Transfers::instance($config)->query($item['trade_no']);
if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') {
DataUserTransfer::mk()->where(['code' => $item['code']])->update([
'status' => 5,
'appid' => $config['appid'],
'openid' => $config['openid'],
'trade_time' => $result['payment_time'],
'change_time' => date('Y-m-d H:i:s'),
'change_desc' => '微信提现打款成功',
]);
}
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace app\data\command;
use app\data\model\DataUser;
use app\data\service\UserUpgradeService;
use think\admin\Command;
use think\admin\Exception;
use think\console\Input;
use think\console\Output;
/**
* 用户等级重算处理
* Class UserLevel
* @package app\data\command
*/
class UserUpgrade extends Command
{
protected function configure()
{
$this->setName('xdata:UserUpgrade');
$this->setDescription('批量重新计算用户等级');
}
/**
* @param Input $input
* @param Output $output
* @return void
* @throws Exception
*/
protected function execute(Input $input, Output $output)
{
try {
[$total, $count] = [DataUser::mk()->count(), 0];
foreach (DataUser::mk()->field('id')->cursor() as $user) {
$this->queue->message($total, ++$count, "正在计算用户 [{$user['id']}] 的等级");
UserUpgradeService::upgrade($user['id']);
$this->queue->message($total, $count, "完成计算用户 [{$user['id']}] 的等级", 1);
}
$this->setQueueSuccess("此次共重新计算 {$total} 个用户等级。");
} catch (Exception $exception) {
throw $exception;
} catch (\Exception $exception) {
$this->setQueueError($exception->getMessage());
}
}
}

View File

@ -1,95 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\service\UserAdminService;
use app\data\service\UserTokenService;
use think\admin\Controller;
use think\exception\HttpResponseException;
/**
* 接口授权认证基类
* Class Auth
* @package app\data\controller\api
*/
abstract class Auth extends Controller
{
/**
* 当前接口请求终端类型
* >>>>>>>>>>>>>>>>>>>>>>
* >>> api-name 接口类型
* >>> api-token 接口认证
* >>>>>>>>>>>>>>>>>>>>>>
* --- 手机浏览器访问 wap
* --- 电脑浏览器访问 web
* --- 微信小程序访问 wxapp
* --- 微信服务号访问 wechat
* --- 苹果应用接口访问 isoapp
* --- 安卓应用接口访问 android
* @var string
*/
protected $type;
/**
* 当前用户编号
* @var integer
*/
protected $uuid;
/**
* 当前用户数据
* @var array
*/
protected $user;
/**
* 控制器初始化
*/
protected function initialize()
{
// 检查接口类型
$this->type = $this->request->header('api-name');
if (empty($this->type)) $this->error("接口类型异常!");
if (!isset(UserAdminService::TYPES[$this->type])) {
$this->error("接口类型[{$this->type}]未定义!");
}
// 读取用户数据
$this->user = $this->getUser();
$this->uuid = $this->user['id'] ?? '';
if (empty($this->uuid)) {
$this->error('用户登录失败!', '{-null-}', 401);
}
}
/**
* 获取用户数据
* @return array
*/
protected function getUser(): array
{
try {
if (empty($this->uuid)) {
$token = $this->request->header('api-token');
if (empty($token)) $this->error('登录认证不能为空!');
[$state, $info, $this->uuid] = UserTokenService::check($this->type, $token);
if (empty($state)) $this->error($info, '{-null-}', 401);
}
return UserAdminService::get($this->uuid, $this->type);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
/**
* 显示用户禁用提示
*/
protected function checkUserStatus()
{
if (empty($this->user['status'])) {
$this->error('账户已被冻结!');
}
}
}

View File

@ -1,64 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\model\BaseUserMessage;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemBase;
/**
* 基础数据接口
* Class Data
* @package app\data\controller\api
*/
class Data extends Controller
{
/**
* 获取指定数据
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getData()
{
$data = $this->_vali(['name.require' => '数据名称不能为空!']);
$extra = ['about', 'slider', 'agreement', 'cropper']; // 其他数据
if (in_array($data['name'], $extra) || isset(SystemBase::items('页面内容')[$data['name']])) {
$this->success('获取数据对象', sysdata($data['name']));
} else {
$this->error('获取数据失败', []);
}
}
/**
* 图片内容数据
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSlider()
{
$this->keys = input('keys', '首页图片');
if (isset(SystemBase::items('图片内容')[$this->keys])) {
$this->success('获取图片内容', sysdata($this->keys));
} else {
$this->error('获取图片失败', []);
}
}
/**
* 系统通知数据
*/
public function getNotify()
{
BaseUserMessage::mQuery(null, function (QueryHelper $query) {
if (($id = input('id')) > 0) {
BaseUserMessage::mk()->where(['id' => $id])->inc('num_read')->update([]);
}
$query->equal('id')->where(['status' => 1, 'deleted' => 0]);
$this->success('获取系统通知', $query->order('sort desc,id desc')->page(true, false, false, 20));
});
}
}

View File

@ -1,63 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\model\ShopGoods;
use app\data\model\ShopGoodsCate;
use app\data\model\ShopGoodsMark;
use app\data\service\ExpressService;
use app\data\service\GoodsService;
use think\admin\Controller;
/**
* 商品数据接口
* Class Goods
* @package app\data\controller\api
*/
class Goods extends Controller
{
/**
* 获取分类数据
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getCate()
{
$this->success('获取分类成功', ShopGoodsCate::treeData());
}
/**
* 获取标签数据
*/
public function getMark()
{
$this->success('获取标签成功', ShopGoodsMark::items());
}
/**
* 获取商品数据
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getGoods()
{
// 更新访问统计
$map = $this->_vali(['code.default' => '']);
if ($map['code']) ShopGoods::mk()->where($map)->inc('num_read')->update([]);
// 商品数据处理
$query = ShopGoods::mQuery()->like('name,marks,cateids,payment')->equal('code,vip_entry');
$result = $query->where(['deleted' => 0, 'status' => 1])->order('sort desc,id desc')->page(true, false, false, 10);
if (count($result['list']) > 0) GoodsService::bindData($result['list']);
$this->success('获取商品数据', $result);
}
/**
* 获取配送区域
*/
public function getRegion()
{
$this->success('获取区域成功', ExpressService::region(3, 1));
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\model\DataUser;
use app\data\service\MessageService;
use app\data\service\UserAdminService;
use think\admin\Controller;
/**
* 用户登录注册接口
* Class Login
* @package app\data\controller\api
*/
class Login extends Controller
{
/**
* 接口认证类型
* @var string
*/
private $type;
/**
* 控制器初始化
*/
protected function initialize()
{
// 接收接口类型
$this->type = $this->request->request('api');
$this->type = $this->type ?: $this->request->header('api-name');
$this->type = $this->type ?: $this->request->header('api-type');
$this->type = $this->type ?: UserAdminService::API_TYPE_WAP;
if (empty(UserAdminService::TYPES[$this->type])) {
$this->error("接口支付[{$this->type}]未定义规则!");
}
}
/**
* 用户登录接口
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function in()
{
$data = $this->_vali([
'phone.mobile' => '手机号码格式错误!',
'phone.require' => '手机号码不能为空!',
'password.require' => '登录密码不能为空!',
]);
$map = ['deleted' => 0, 'phone' => $data['phone']];
$user = DataUser::mk()->where($map)->findOrEmpty();
if ($user->isEmpty()) $this->error('该手机号还没有注册哦!');
if (empty($user['status'])) $this->error('该用户账号状态异常!');
if (md5($data['password']) === $user['password']) {
$this->success('手机登录成功!', UserAdminService::set($map, [], $this->type, true));
} else {
$this->error('账号登录失败,请稍候再试!');
}
}
/**
* 用户统一注册入口
* @throws \think\admin\Exception
* @throws \think\db\exception\DbException
*/
public function register()
{
$data = $this->_vali([
'region_province.default' => '',
'region_city.default' => '',
'region_area.default' => '',
'username.default' => '',
'phone.mobile' => '手机格式错误!',
'phone.require' => '手机不能为空!',
'verify.require' => '验证码不能为空!',
'password.require' => '登录密码不能为空!',
]);
if (!MessageService::instance()->checkVerifyCode($data['verify'], $data['phone'])) {
$this->error('手机短信验证失败!');
}
$map = ['phone' => $data['phone'], 'deleted' => 0];
if (DataUser::mk()->where($map)->count() > 0) {
$this->error('手机号已注册,请使用其它手机号!');
}
$data['password'] = md5($data['password']);
$user = UserAdminService::set($map, $data, $this->type, true);
empty($user) ? $this->error('手机注册失败!') : $this->success('用户注册成功!', $user);
}
/**
* 发送短信验证码
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function sendsms()
{
$data = $this->_vali([
'phone.mobile' => '手机号格式错误!',
'phone.require' => '手机号不能为空!',
'secure.require' => '安全码不能为空!',
]);
if ($data['secure'] !== sysconf('zt.secure_code')) $this->error('接口安全码错误!');
[$state, $message, $data] = MessageService::instance()->sendVerifyCode($data['phone']);
$state ? $this->success($message, $data) : $this->error($message, $data);
}
}

View File

@ -1,68 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\model\DataNewsItem;
use app\data\model\DataNewsMark;
use app\data\model\DataNewsXCollect;
use app\data\service\NewsService;
use think\admin\Controller;
/**
* 文章接口控制器
* Class News
* @package app\data\controller\api
*/
class News extends Controller
{
/**
* 获取文章标签列表
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getMark()
{
$query = DataNewsMark::mQuery()->like('name');
$query->where(['status' => 1, 'deleted' => 0])->withoutField('sort,status,deleted');
$this->success('获取文章标签', $query->order('sort desc,id desc')->page(false, false));
}
/**
* 获取文章内容列表
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getItem()
{
if ($code = input('code', '')) {
DataNewsItem::mk()->where(['code' => $code])->inc('num_read')->update([]);
if (($uuid = input('uuid', 0)) > 0) {
$data = ['uuid' => $uuid, 'code' => $code, 'type' => 3, 'status' => 2];
DataNewsXCollect::mk()->where($data)->delete();
DataNewsXCollect::mk()->insert($data);
}
}
$query = DataNewsItem::mQuery()->like('name,mark')->equal('id,code');
$query->where(['deleted' => 0, 'status' => 1])->withoutField('sort,status,deleted');
$result = $query->order('sort desc,id desc')->page(true, false, false, 15);
NewsService::buildData($result['list'], input('uuid', 0));
$this->success('获取文章内容', $result);
}
/**
* 获取文章评论
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getComment()
{
$map = $this->_vali(['code.require' => '文章不能为空!']);
$query = DataNewsXCollect::mQuery()->where(['type' => 4, 'status' => 2]);
$result = $query->where($map)->order('id desc')->page(true, false, false, 15);
NewsService::buildListByUidAndCode($result['list']);
$this->success('获取评论成功', $result);
}
}

View File

@ -1,64 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\service\payment\AlipayPaymentService;
use app\data\service\payment\JoinpayPaymentService;
use app\data\service\payment\WechatPaymentService;
use think\admin\Controller;
/**
* 异步通知处理
* Class Notify
* @package app\data\controller\api
*/
class Notify extends Controller
{
/**
* 微信支付通知
* @param string $scene 支付场景
* @param string $param 支付参数
* @return string
* @throws \think\admin\Exception
*/
public function wxpay(string $scene = 'order', string $param = ''): string
{
if (strtolower($scene) === 'order') {
return WechatPaymentService::instance($param)->notify();
} else {
return 'success';
}
}
/**
* 支付宝支付通知
* @param string $scene 支付场景
* @param string $param 支付参数
* @return string
* @throws \think\admin\Exception
*/
public function alipay(string $scene = 'order', string $param = ''): string
{
if (strtolower($scene) === 'order') {
return AlipayPaymentService::instance($param)->notify();
} else {
return 'success';
}
}
/**
* 汇聚支付通知
* @param string $scene 支付场景
* @param string $param 支付参数
* @return string
* @throws \think\admin\Exception
*/
public function joinpay(string $scene = 'order', string $param = ''): string
{
if (strtolower($scene) === 'order') {
return JoinpayPaymentService::instance($param)->notify();
} else {
return 'success';
}
}
}

View File

@ -1,137 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\service\UserAdminService;
use app\wechat\service\WechatService;
use think\admin\Controller;
use think\Response;
/**
* 微信服务号入口
* Class Wechat
* @package app\data\controller\api
* @example 域名请修改为自己的地址,放到网页代码合适位置
* <meta name="referrer" content="always">
* <script referrerpolicy="unsafe-url" src="https://your.domain.com/data/api.wechat/oauth?mode=1"></script>
*
* 授权模式支持两种模块,参数 mode=0 时为静默授权mode=1 时为完整授权
* 注意:回跳地址默认从 Header 中的 http_referer 获取,也可以传 source 参数
*/
class Wechat extends Controller
{
/**
* 接口认证类型
* @var string
*/
private $type = UserAdminService::API_TYPE_WECHAT;
/**
* 唯一绑定字段
* @var string
*/
private $field;
/**
* 控制器初始化
* @return $this
*/
protected function initialize(): Wechat
{
if (empty(UserAdminService::TYPES[$this->type]['auth'])) {
$this->error("接口类型[{$this->type}]没有定义规则");
} else {
$this->field = UserAdminService::TYPES[$this->type]['auth'];
}
return $this;
}
/**
* 获取 JSSDK 签名
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function jssdk()
{
$url = input('source') ?: $this->request->server('http_referer');
$this->success('获取签名参数', WechatService::instance()->getWebJssdkSign($url));
}
/**
* 加载网页授权数据
* @return Response
* @throws \WeChat\Exceptions\InvalidResponseException
* @throws \WeChat\Exceptions\LocalCacheException
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function oauth(): Response
{
$source = input('source') ?: $this->request->server('http_referer');
[$mode, $script, $wechat] = [input('mode', 1), [], WechatService::instance()];
$result = $wechat->getWebOauthInfo($source ?: $this->request->url(true), $mode, false);
if (empty($result['openid'])) {
$script[] = 'alert("Wechat WebOauth failed.")';
} else {
$data = $result['fansinfo'] ?? [];
$data[$this->field] = $data['openid'];
$data['base_sex'] = ['未知', '男', '女'][$data['sex']] ?? '未知';
if (isset($result['unionid'])) $data['unionid'] = $result['unionid'];
if (isset($data['headimgurl'])) $data['headimg'] = $data['headimgurl'];
$map = UserAdminService::getUserUniMap($this->field, $data[$this->field], $data['unionid'] ?? '');
$result['userinfo'] = UserAdminService::set($map, array_merge($map, $data), $this->type, true);
$script[] = "window.WeChatOpenid='{$result['openid']}'";
$script[] = 'window.WeChatFansInfo=' . json_encode($result['fansinfo'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$script[] = 'window.WeChatUserInfo=' . json_encode($result['userinfo'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
$script[] = '';
return Response::create(join(";\n", $script))->contentType('application/x-javascript');
}
/**
* 网页授权测试
* 使用网页直接访问此链接
* @return string
*/
public function otest(): string
{
return <<<EOL
<html lang="zh">
<head>
<meta charset="utf-8">
<title>微信网页授权测试</title>
<meta name="referrer" content="always">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
<style>pre{padding:20px;overflow:auto;margin-top:10px;background:#ccc;border-radius:6px;}</style>
</head>
<body>
<div>当前链接</div>
<pre>{$this->request->scheme()}://{$this->request->host()}/data/api.wechat/oauth?mode=1</pre>
<div style="margin-top:30px">粉丝数据</div>
<pre id="fansdata">待网页授权,加载粉丝数据...</pre>
<div style="margin-top:30px">用户数据</div>
<pre id="userdata">待网页授权,加载用户数据...</pre>
<script referrerpolicy="unsafe-url" src="//{$this->request->host()}/data/api.wechat/oauth?mode=1"></script>
<script>
if(typeof window.WeChatFansInfo === 'object'){
document.getElementById('fansdata').innerText = JSON.stringify(window.WeChatFansInfo, null, 2);
}
if(typeof window.WeChatUserInfo === 'object'){
document.getElementById('userdata').innerText = JSON.stringify(window.WeChatUserInfo, null, 2);
}
</script>
</body>
</html>
EOL;
}
}

View File

@ -1,200 +0,0 @@
<?php
namespace app\data\controller\api;
use app\data\service\UserAdminService;
use think\admin\Controller;
use think\exception\HttpResponseException;
use think\Response;
use WeMini\Crypt;
use WeMini\Live;
use WeMini\Qrcode;
/**
* 微信小程序入口
* Class Wxapp
* @package app\data\controller\api
*/
class Wxapp extends Controller
{
/**
* 接口认证类型
* @var string
*/
private $type = UserAdminService::API_TYPE_WXAPP;
/**
* 唯一绑定字段
* @var string
*/
private $field;
/**
* 小程序配置参数
* @var array
*/
private $cfg;
/**
* 接口服务初始化
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
protected function initialize()
{
$opt = sysdata('wxapp');
$this->cfg = [
'appid' => $opt['appid'] ?? '',
'appsecret' => $opt['appkey'] ?? '',
'cache_path' => $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . 'wechat',
];
if (empty(UserAdminService::TYPES[$this->type]['auth'])) {
$this->error("接口类型[{$this->type}]没有定义规则");
} else {
$this->field = UserAdminService::TYPES[$this->type]['auth'];
}
}
/**
* 授权Code换取会话信息
* @throws \think\admin\Exception
* @throws \think\db\exception\DbException
*/
public function session()
{
$input = $this->_vali(['code.require' => '登录凭证CODE不能为空']);
[$openid, $unionid, $session] = $this->applySessionKey($input['code']);
$map = UserAdminService::getUserUniMap($this->field, $openid, $unionid);
$data = [$this->field => $openid, 'session_key' => $session];
if (!empty($unionid)) $data['unionid'] = $unionid;
$this->success('授权换取成功!', UserAdminService::set($map, $data, $this->type, true));
}
/**
* 小程序数据解密
*/
public function decode()
{
try {
$input = $this->_vali([
'iv.require' => '解密向量不能为空!',
'code.require' => '授权CODE不能为空',
'encrypted.require' => '加密内容不能为空!',
]);
[$openid, $unionid, $input['session_key']] = $this->applySessionKey($input['code']);
$result = Crypt::instance($this->cfg)->decode($input['iv'], $input['session_key'], $input['encrypted']);
if (is_array($result) && isset($result['avatarUrl']) && isset($result['nickName'])) {
$data = [$this->field => $openid, 'nickname' => $result['nickName'], 'headimg' => $result['avatarUrl']];
$data['base_sex'] = ['-', '男', '女'][$result['gender']] ?? '-';
if (!empty($unionid)) $data['unionid'] = $unionid;
$map = UserAdminService::getUserUniMap($this->field, $openid, $unionid);
$this->success('数据解密成功!', UserAdminService::set($map, $data, $this->type, true));
} elseif (is_array($result)) {
$this->success('数据解密成功!', $result);
} else {
$this->error('数据处理失败,请稍候再试!');
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error("数据处理失败,{$exception->getMessage()}");
}
}
/**
* 授权CODE换取会话信息
* @param string $code 换取授权CODE
* @return array [openid, sessionkey]
*/
private function applySessionKey(string $code): array
{
try {
$cache = $this->app->cache->get($code, []);
if (isset($cache['openid']) && isset($cache['session_key'])) {
return [$cache['openid'], $cache['unionid'] ?? '', $cache['session_key']];
}
$result = Crypt::instance($this->cfg)->session($code);
if (isset($result['openid']) && isset($result['session_key'])) {
$this->app->cache->set($code, $result, 60);
return [$result['openid'], $result['unionid'] ?? '', $result['session_key']];
} elseif (isset($result['errmsg'])) {
$this->error($result['errmsg']);
} else {
$this->error("授权换取失败,请稍候再试!");
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error("授权换取失败,{$exception->getMessage()}");
}
}
/**
* 获取小程序码
*/
public function qrcode(): Response
{
try {
$data = $this->_vali([
'size.default' => 430,
'type.default' => 'base64',
'path.require' => '跳转路径不能为空!',
]);
$result = Qrcode::instance($this->cfg)->createMiniPath($data['path'], $data['size']);
if ($data['type'] === 'base64') {
$this->success('生成小程序码成功!', [
'base64' => 'data:image/png;base64,' . base64_encode($result),
]);
} else {
return response($result)->contentType('image/png');
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
/**
* 获取直播列表
*/
public function getLiveList()
{
try {
$data = $this->_vali(['start.default' => 0, 'limit.default' => 10]);
$list = Live::instance($this->cfg)->getLiveList($data['start'], $data['limit']);
$this->success('获取直播列表成功!', $list);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
/**
* 获取回放源视频
*/
public function getLiveInfo()
{
try {
$data = $this->_vali([
'start.default' => 0,
'limit.default' => 10,
'action.default' => 'get_replay',
'room_id.require' => '直播间不能为空',
]);
$result = Live::instance($this->cfg)->getLiveInfo($data);
$this->success('获取回放视频成功!', $result);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
}

View File

@ -1,132 +0,0 @@
<?php
namespace app\data\controller\api\auth;
use app\data\controller\api\Auth;
use app\data\model\DataUserAddress;
use think\admin\extend\CodeExtend;
/**
* 用户收货地址管理
* Class Address
* @package app\data\controller\api\auth
*/
class Address extends Auth
{
/**
* 添加收货地址
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function set()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.default' => 0,
'code.default' => '',
'idcode.default' => '', // 身份证号码
'idimg1.default' => '', // 身份证正面
'idimg2.default' => '', // 身份证反面
'type.in:0,1' => '地址状态不在范围!',
'name.require' => '收货姓名不能为空!',
'phone.mobile' => '收货手机格式错误!',
'phone.require' => '收货手机不能为空!',
'province.require' => '地址省份不能为空!',
'city.require' => '地址城市不能为空!',
'area.require' => '地址区域不能为空!',
'address.require' => '详情地址不能为空!',
'deleted.value' => 0,
]);
if (empty($data['code'])) {
unset($data['code']);
$count = DataUserAddress::mk()->where($data)->count();
if ($count > 0) $this->error('抱歉,该地址已经存在!');
$data['code'] = CodeExtend::uniqidDate(20, 'A');
if (DataUserAddress::mk()->insert($data) === false) {
$this->error('添加地址失败!');
}
} else {
$map = ['uuid' => $this->uuid, 'code' => $data['code']];
$addr = DataUserAddress::mk()->where($map)->find();
if (empty($addr)) $this->error('修改地址不存在!');
DataUserAddress::mk()->where($map)->update($data);
}
// 去除其它默认选项
if (isset($data['type']) && $data['type'] > 0) {
$map = [['uuid', '=', $this->uuid], ['code', '<>', $data['code']]];
DataUserAddress::mk()->where($map)->update(['type' => 0]);
}
$this->success('地址保存成功!', $this->getAddress($data['code']));
}
/**
* 获取收货地址
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function get()
{
$query = DataUserAddress::mQuery()->withoutField('deleted');
$query->equal('code')->where(['uuid' => $this->uuid, 'deleted' => 0]);
$result = $query->order('type desc,id desc')->page(false, false, false, 15);
$this->success('获取地址数据!', $result);
}
/**
* 修改地址状态
*/
public function state()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.in:0,1' => '地址状态不在范围!',
'type.require' => '地址状态不能为空!',
'code.require' => '地址编号不能为空!',
]);
// 检查地址是否存在
$map = ['uuid' => $data['uuid'], 'code' => $data['code']];
if (DataUserAddress::mk()->where($map)->count() < 1) {
$this->error('修改的地址不存在!');
}
// 更新默认地址状态
$data['type'] = intval($data['type']);
DataUserAddress::mk()->where($map)->update(['type' => $data['type']]);
// 去除其它默认选项
if ($data['type'] > 0) {
$map = [['uuid', '=', $this->uuid], ['code', '<>', $data['code']]];
DataUserAddress::mk()->where($map)->update(['type' => 0]);
}
$this->success('默认设置成功!', $this->getAddress($data['code']));
}
/**
* 删除收货地址
*/
public function remove()
{
$map = $this->_vali([
'uuid.value' => $this->uuid,
'code.require' => '地址不能为空!',
]);
$item = DataUserAddress::mk()->where($map)->findOrEmpty();
if ($item->isEmpty()) $this->error('需要删除的地址不存在!');
if ($item->save(['deleted' => 1]) !== false) {
$this->success('删除地址成功!');
} else {
$this->error('删除地址失败!');
}
}
/**
* 获取指定的地址
* @param string $code
* @return null|array
*/
private function getAddress(string $code): array
{
$map = ['code' => $code, 'uuid' => $this->uuid, 'deleted' => 0];
return DataUserAddress::mk()->withoutField('deleted')->where($map)->findOrEmpty()->toArray();
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace app\data\controller\api\auth;
use app\data\controller\api\Auth;
use app\data\model\DataUserBalance;
use think\admin\helper\QueryHelper;
/**
* 用户余额转账
* Class Balance
* @package app\data\controller\api\auth
*/
class Balance extends Auth
{
/**
* 获取用户余额记录
*/
public function get()
{
DataUserBalance::mQuery(null, function (QueryHelper $query) {
$query->withoutField('deleted,create_by');
$query->where(['uuid' => $this->uuid, 'deleted' => 0])->like('create_at#date');
$this->success('获取数据成功', $query->order('id desc')->page(true, false, false, 10));
});
}
}

View File

@ -1,150 +0,0 @@
<?php
namespace app\data\controller\api\auth;
use app\data\controller\api\Auth;
use app\data\model\BaseUserUpgrade;
use app\data\model\DataUser;
use app\data\service\UserAdminService;
use app\data\service\UserUpgradeService;
use think\admin\Storage;
use think\exception\HttpResponseException;
/**
* 用户资料管理
* Class Center
* @package app\data\controller\api\auth
*/
class Center extends Auth
{
/**
* 更新用户资料
*/
public function set()
{
$data = $this->_vali([
'headimg.default' => '',
'username.default' => '',
'base_age.default' => '',
'base_sex.default' => '',
'base_height.default' => '',
'base_weight.default' => '',
'base_birthday.default' => '',
]);
foreach ($data as $key => $vo) if ($vo === '') unset($data[$key]);
if (empty($data)) $this->error('没有修改的数据!');
if (DataUser::mk()->where(['id' => $this->uuid])->update($data) !== false) {
$this->success('更新资料成功!', $this->getUser());
} else {
$this->error('更新资料失败!');
}
}
/**
* 获取用户资料
*/
public function get()
{
$this->success('获取用户资料', $this->getUser());
}
/**
* Base64 图片上传
*/
public function image()
{
try {
$data = $this->_vali(['base64.require' => '图片内容不为空!']);
if (preg_match($preg = '|^data:image/(.*?);base64,|i', $data['base64'])) {
[$ext, $img] = explode('|||', preg_replace($preg, '$1|||', $data['base64']));
if (empty($ext) || !in_array(strtolower($ext), ['png', 'jpg', 'jpeg'])) {
$this->error('图片格式异常!');
}
$name = Storage::name($img, $ext, 'image/');
$info = Storage::instance()->set($name, base64_decode($img));
$this->success('图片上传成功!', ['url' => $info['url']]);
} else {
$this->error('解析内容失败!');
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
/**
* 二进制文件上传
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function upload()
{
$file = $this->request->file('file');
if (empty($file)) $this->error('文件上传异常!');
$extension = strtolower($file->getOriginalExtension());
if (in_array($extension, ['php', 'sh'])) $this->error('禁止上传此类文件!');
$bina = file_get_contents($file->getRealPath());
$name = Storage::name($file->getPathname(), $extension, '', 'md5_file');
$info = Storage::instance()->set($name, $bina, false, $file->getOriginalName());
if (is_array($info) && isset($info['url'])) {
$this->success('文件上传成功!', $info);
} else {
$this->error('文件上传失败!');
}
}
/**
* 获取用户等级
*/
public function levels()
{
$levels = BaseUserUpgrade::items();
$this->success('获取用户等级', array_values($levels));
}
/**
* 获取我邀请的朋友
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getFrom()
{
$map = [];
$map[] = ['deleted', '=', 0];
$map[] = ['path', 'like', "%-{$this->uuid}-%"];
// 查询邀请的朋友
$query = DataUser::mQuery()->like('nickname|username#nickname')->equal('vip_code,pids,pid1,id#uuid');
$query->field('id,pid0,pid1,pid2,pids,username,nickname,headimg,order_amount_total,teams_amount_total,teams_amount_direct,teams_amount_indirect,teams_users_total,teams_users_direct,teams_users_indirect,rebate_total,rebate_used,rebate_lock,create_at');
$result = $query->where($map)->order('id desc')->page(true, false, false, 15);
// 统计当前用户所有下属数
$total = DataUser::mk()->where($map)->count();
// 统计当前用户本月下属数
$map[] = ['create_at', 'like', date('Y-m-%')];
$month = DataUser::mk()->where($map)->count();
// 返回结果列表数据及统计
$result['total'] = ['user_total' => $total, 'user_month' => $month];
$this->success('获取我邀请的朋友', $result);
}
/**
* 绑定用户邀请人
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function bindFrom()
{
$data = $this->_vali(['from.require' => '邀请人不能为空']);
[$state, $message] = UserUpgradeService::bindAgent($this->uuid, $data['from'], 0);
if ($state) {
$this->success($message, UserAdminService::total($this->uuid));
} else {
$this->error($message, UserAdminService::total($this->uuid));
}
}
}

View File

@ -1,209 +0,0 @@
<?php
namespace app\data\controller\api\auth;
use app\data\controller\api\Auth;
use app\data\model\DataNewsXCollect;
use app\data\service\NewsService;
/**
* 文章评论内容
* Class News
* @package app\data\controller\api\auth
*/
class News extends Auth
{
/**
* 用户评论内容
*/
public function addComment()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.value' => 4,
'status.value' => 1,
'code.require' => '文章不能为空!',
'reply.require' => '评论不能为空!',
]);
if (DataNewsXCollect::mk()->insert($data) !== false) {
NewsService::syncNewsTotal($data['code']);
$this->success('添加评论成功!');
} else {
$this->error('添加评论失败!');
}
}
/**
* 获取我的评论
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getComment()
{
$query = DataNewsXCollect::mQuery()->where(['uuid' => $this->uuid, 'type' => 4]);
$result = $query->whereIn('status', [1, 2])->order('id desc')->page(true, false);
NewsService::buildListByUidAndCode($result);
$this->success('获取评论列表成功', $result);
}
/**
* 删除内容评论
*/
public function delComment()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.value' => 4,
'id.require' => '评论编号不能为空!',
'code.require' => '文章编号不能为空!',
]);
if (DataNewsXCollect::mk()->where($data)->delete() !== false) {
$this->success('评论删除成功!');
} else {
$this->error('认证删除失败!');
}
}
/**
* 添加内容收藏
*/
public function addCollect()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.value' => 1,
'status.value' => 2,
'code.require' => '文章编号不能为空!',
]);
if (DataNewsXCollect::mk()->where($data)->count() > 0) {
$this->success('您已收藏!');
}
if (DataNewsXCollect::mk()->insert($data) !== false) {
NewsService::syncNewsTotal($data['code']);
$this->success('收藏成功!');
} else {
$this->error('收藏失败!');
}
}
/**
* 取消收藏文章
*/
public function delCollect()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.value' => 1,
'code.require' => '文章编号不能为空!',
]);
if (DataNewsXCollect::mk()->where($data)->delete() !== false) {
NewsService::syncNewsTotal($data['code']);
$this->success('取消收藏成功!');
} else {
$this->error('取消收藏失败!');
}
}
/**
* 获取用户收藏的资讯
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getCollect()
{
$map = ['uuid' => $this->uuid, 'type' => 1];
$query = DataNewsXCollect::mQuery()->where($map);
$result = $query->order('id desc')->page(true, false, false, 15);
NewsService::buildListByUidAndCode($result['list']);
$this->success('获取收藏记录成功!', $result);
}
/**
* 添加内容点赞
*/
public function addLike()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.value' => 2,
'status.value' => 2,
'code.require' => '文章编号不能为空!',
]);
if (DataNewsXCollect::mk()->where($data)->count() > 0) {
$this->success('您已点赞!');
}
if (DataNewsXCollect::mk()->insert($data) !== false) {
NewsService::syncNewsTotal($data['code']);
$this->success('点赞成功!');
} else {
$this->error('点赞失败!');
}
}
/**
* 取消内容点赞
*/
public function delLike()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.value' => 2,
'code.require' => '文章编号不能为空!',
]);
if (DataNewsXCollect::mk()->where($data)->delete() !== false) {
NewsService::syncNewsTotal($data['code']);
$this->success('取消点赞成功!');
} else {
$this->error('取消点赞失败!');
}
}
/**
* 获取用户收藏的资讯
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLike()
{
$query = DataNewsXCollect::mQuery();
$query->where(['uuid' => $this->uuid, 'type' => 2, 'status' => 2]);
$result = $query->order('id desc')->page(true, false, false, 15);
NewsService::buildListByUidAndCode($result['list']);
$this->success('获取点赞记录成功!', $result);
}
/**
* 添加用户的浏览历史
*/
public function addHistory()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'type.value' => 2,
'status.value' => 2,
'code.require' => '文章编号不能为空!',
]);
DataNewsXCollect::mk()->where($data)->delete();
DataNewsXCollect::mk()->insert($data);
$this->success('添加浏览历史成功!');
}
/**
* 获取用户的浏览历史
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getHistory()
{
$query = DataNewsXCollect::mQuery();
$query->where(['uuid' => $this->uuid, 'type' => 3, 'status' => 2]);
$result = $query->order('id desc')->page(true, false, false, 15);
NewsService::buildListByUidAndCode($result['list']);
$this->success('获取浏览历史成功!', $result);
}
}

View File

@ -1,496 +0,0 @@
<?php
namespace app\data\controller\api\auth;
use app\data\controller\api\Auth;
use app\data\model\BaseUserPayment;
use app\data\model\DataUser;
use app\data\model\DataUserAddress;
use app\data\model\ShopGoods;
use app\data\model\ShopGoodsItem;
use app\data\model\ShopOrder;
use app\data\model\ShopOrderItem;
use app\data\model\ShopOrderSend;
use app\data\service\ExpressService;
use app\data\service\GoodsService;
use app\data\service\OrderService;
use app\data\service\PaymentService;
use app\data\service\UserAdminService;
use think\admin\extend\CodeExtend;
use think\exception\HttpResponseException;
/**
* 用户订单数据接口
* Class Order
* @package app\data\controller\api\auth
*/
class Order extends Auth
{
/**
* 控制器初始化
*/
protected function initialize()
{
parent::initialize();
if (empty($this->user['status'])) {
$this->error('账户已被冻结,不能操作订单数据哦!');
}
}
/**
* 获取订单列表
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function get()
{
$map = ['uuid' => $this->uuid, 'deleted_status' => 0];
$query = ShopOrder::mQuery()->in('status')->equal('order_no');
$result = $query->where($map)->order('id desc')->page(true, false, false, 20);
if (count($result['list']) > 0) OrderService::buildData($result['list']);
$this->success('获取订单数据成功!', $result);
}
/**
* 用户创建订单
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function add()
{
// 检查用户状态
$this->checkUserStatus();
// 商品规则
$rules = $this->request->post('items', '');
if (empty($rules)) $this->error('商品不能为空');
// 订单数据
[$items, $order, $truckType, $allowPayments] = [[], [], -1, null];
$order['uuid'] = $this->uuid;
$order['order_no'] = CodeExtend::uniqidDate(18, 'N');
// 代理处理
$order['puid1'] = input('from', $this->user['pid1']);
if ($order['puid1'] == $this->uuid) $order['puid1'] = 0;
if ($order['puid1'] > 0) {
$map = ['id' => $order['puid1'], 'status' => 1];
$order['puid2'] = DataUser::mk()->where($map)->value('pid2');
if (is_null($order['puid2'])) $this->error('代理异常');
}
// 订单商品处理
foreach (explode('||', $rules) as $rule) {
[$code, $spec, $count] = explode('@', $rule);
// 商品信息检查
$goodsInfo = ShopGoods::mk()->where(['code' => $code, 'status' => 1, 'deleted' => 0])->find();
$goodsItem = ShopGoodsItem::mk()->where(['status' => 1, 'goods_code' => $code, 'goods_spec' => $spec])->find();
if (empty($goodsInfo) || empty($goodsItem)) $this->error('商品查询异常');
// 商品类型检查
if ($truckType < 0) $truckType = $goodsInfo['truck_type'];
if ($truckType !== $goodsInfo['truck_type']) $this->error('不能混合下单');
// 限制购买数量
if (isset($goodsInfo['limit_max_num']) && $goodsInfo['limit_max_num'] > 0) {
$map = [['a.uuid', '=', $this->uuid], ['a.status', 'in', [2, 3, 4, 5]], ['b.goods_code', '=', $goodsInfo['code']]];
$buys = ShopOrder::mk()->alias('a')->join('store_order_item b', 'a.order_no=b.order_no')->where($map)->sum('b.stock_sales');
if ($buys + $count > $goodsInfo['limit_max_num']) $this->error('超过限购数量');
}
// 限制购买身份
if ($goodsInfo['limit_low_vip'] > $this->user['vip_code']) $this->error('用户等级不够');
// 商品库存检查
if ($goodsItem['stock_sales'] + $count > $goodsItem['stock_total']) $this->error('商品库存不足');
// 支付支付处理
$_allowPayments = [];
foreach (str2arr($goodsInfo['payment']) as $code) {
if (is_null($allowPayments) || in_array($code, $allowPayments)) $_allowPayments[] = $code;
}
if (empty($_allowPayments)) {
$this->error('订单无法统一支付');
} else {
$allowPayments = $_allowPayments;
}
// 商品折扣处理
[$discountId, $discountRate] = OrderService::discount($goodsInfo['discount_id'], $this->user['vip_code']);
// 订单详情处理
$items[] = [
'uuid' => $order['uuid'],
'order_no' => $order['order_no'],
// 商品信息字段
'goods_name' => $goodsInfo['name'],
'goods_cover' => $goodsInfo['cover'],
'goods_payment' => $goodsInfo['payment'],
'goods_sku' => $goodsItem['goods_sku'],
'goods_code' => $goodsItem['goods_code'],
'goods_spec' => $goodsItem['goods_spec'],
// 库存数量处理
'stock_sales' => $count,
// 快递发货数据
'truck_type' => $goodsInfo['truck_type'],
'truck_code' => $goodsInfo['truck_code'],
'truck_number' => $goodsInfo['rebate_type'] > 0 ? $goodsItem['number_express'] * $count : 0,
// 商品费用字段
'price_market' => $goodsItem['price_market'],
'price_selling' => $goodsItem['price_selling'],
'total_market' => $goodsItem['price_market'] * $count,
'total_selling' => $goodsItem['price_selling'] * $count,
// 奖励金额积分
'reward_balance' => $goodsItem['reward_balance'] * $count,
'reward_integral' => $goodsItem['reward_integral'] * $count,
// 绑定用户等级
'vip_name' => $this->user['vip_name'],
'vip_code' => $this->user['vip_code'],
// 是否入会礼包
'vip_entry' => $goodsInfo['vip_entry'],
'vip_upgrade' => $goodsInfo['vip_upgrade'],
// 是否参与返利
'rebate_type' => $goodsInfo['rebate_type'],
'rebate_amount' => $goodsInfo['rebate_type'] > 0 ? $goodsItem['price_selling'] * $count : 0,
// 等级优惠方案
'discount_id' => $discountId,
'discount_rate' => $discountRate,
'discount_amount' => $discountRate * $goodsItem['price_selling'] * $count / 100,
];
}
try {
$order['payment_allow'] = arr2str($allowPayments);
$order['rebate_amount'] = array_sum(array_column($items, 'rebate_amount'));
$order['reward_balance'] = array_sum(array_column($items, 'reward_balance'));
// 订单发货类型
$order['status'] = $truckType ? 1 : 2;
$order['truck_type'] = $truckType;
// 统计商品数量
$order['number_goods'] = array_sum(array_column($items, 'stock_sales'));
$order['number_express'] = array_sum(array_column($items, 'truck_number'));
// 统计商品金额
$order['amount_goods'] = array_sum(array_column($items, 'total_selling'));
// 优惠后的金额
$order['amount_discount'] = array_sum(array_column($items, 'discount_amount'));
// 订单随机免减
$order['amount_reduct'] = OrderService::getReduct();
if ($order['amount_reduct'] > $order['amount_goods']) {
$order['amount_reduct'] = $order['amount_goods'];
}
// 统计订单金额
$order['amount_real'] = $order['amount_discount'] - $order['amount_reduct'];
$order['amount_total'] = $order['amount_goods'];
// 写入商品数据
$this->app->db->transaction(function () use ($order, $items) {
ShopOrder::mk()->insert($order);
ShopOrderItem::mk()->insertAll($items);
});
// 同步商品库存销量
foreach (array_unique(array_column($items, 'goods_code')) as $code) {
GoodsService::stock($code);
}
// 触发订单创建事件
$this->app->event->trigger('ShopOrderCreate', $order['order_no']);
// 组装订单商品数据
$order['items'] = $items;
// 返回处理成功数据
$this->success('商品下单成功', $order);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
$this->error("商品下单失败,{$exception->getMessage()}");
}
}
/**
* 获取用户折扣
*/
public function discount()
{
$data = $this->_vali(['discount.require' => '折扣编号不能为空!']);
[, $rate] = OrderService::discount(intval($data['discount']), $this->user['vip_code']);
$this->success('获取用户折扣', ['rate' => $rate]);
}
/**
* 模拟计算订单运费
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function express()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'code.require' => '地址不能为空',
'order_no.require' => '单号不能为空',
]);
// 用户收货地址
$map = ['uuid' => $this->uuid, 'code' => $data['code']];
$addr = DataUserAddress::mk()->where($map)->find();
if (empty($addr)) $this->error('收货地址异常');
// 订单状态检查
$map = ['uuid' => $this->uuid, 'order_no' => $data['order_no']];
$tCount = ShopOrderItem::mk()->where($map)->sum('truck_number');
// 根据地址计算运费
$map = ['status' => 1, 'deleted' => 0, 'order_no' => $data['order_no']];
$tCode = ShopOrderItem::mk()->where($map)->column('truck_code');
[$amount, , , $remark] = ExpressService::amount($tCode, $addr['province'], $addr['city'], $tCount);
$this->success('计算运费成功', ['amount' => $amount, 'remark' => $remark]);
}
/**
* 订单信息完成
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function perfect()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'code.require' => '地址不能为空',
'order_no.require' => '单号不能为空',
]);
// 用户收货地址
$map = ['uuid' => $this->uuid, 'code' => $data['code'], 'deleted' => 0];
$addr = DataUserAddress::mk()->where($map)->find();
if (empty($addr)) $this->error('收货地址异常');
// 订单状态检查
$map1 = ['uuid' => $this->uuid, 'order_no' => $data['order_no']];
$order = ShopOrder::mk()->where($map1)->whereIn('status', [1, 2])->find();
if (empty($order)) $this->error('不能修改地址');
if (empty($order['truck_type'])) $this->success('无需快递配送', ['order_no' => $order['order_no']]);
// 根据地址计算运费
$map2 = ['status' => 1, 'deleted' => 0, 'order_no' => $data['order_no']];
$tCount = ShopOrderItem::mk()->where($map1)->sum('truck_number');
$tCodes = ShopOrderItem::mk()->where($map2)->column('truck_code');
[$amount, $tCount, $tCode, $remark] = ExpressService::amount($tCodes, $addr['province'], $addr['city'], $tCount);
// 创建订单发货信息
$express = [
'template_code' => $tCode, 'template_count' => $tCount, 'uuid' => $this->uuid,
'template_remark' => $remark, 'template_amount' => $amount, 'status' => 1,
];
$express['order_no'] = $data['order_no'];
$express['address_code'] = $data['code'];
$express['address_datetime'] = date('Y-m-d H:i:s');
// 收货人信息
$express['address_name'] = $addr['name'];
$express['address_phone'] = $addr['phone'];
$express['address_idcode'] = $addr['idcode'];
$express['address_idimg1'] = $addr['idimg1'];
$express['address_idimg2'] = $addr['idimg2'];
// 收货地址信息
$express['address_province'] = $addr['province'];
$express['address_city'] = $addr['city'];
$express['address_area'] = $addr['area'];
$express['address_content'] = $addr['address'];
ShopOrderSend::mUpdate($express, 'order_no');
data_save(ShopOrderSend::class, $express, 'order_no');
// 组装更新订单数据
$update = ['status' => 2, 'amount_express' => $express['template_amount']];
// 重新计算订单金额
$update['amount_real'] = $order['amount_discount'] + $amount - $order['amount_reduct'];
$update['amount_total'] = $order['amount_goods'] + $amount;
// 支付金额不能为零
if ($update['amount_real'] <= 0) $update['amount_real'] = 0.00;
if ($update['amount_total'] <= 0) $update['amount_total'] = 0.00;
// 更新用户订单数据
$map = ['uuid' => $this->uuid, 'order_no' => $data['order_no']];
if (ShopOrder::mk()->where($map)->update($update) !== false) {
// 触发订单确认事件
$this->app->event->trigger('ShopOrderPerfect', $order['order_no']);
// 返回处理成功数据
$this->success('订单确认成功', ['order_no' => $order['order_no']]);
} else {
$this->error('订单确认失败');
}
}
/**
* 获取支付支付数据
*/
public function channel()
{
$data = $this->_vali(['uuid.value' => $this->uuid, 'order_no.require' => '单号不能为空']);
$payments = ShopOrder::mk()->where($data)->value('payment_allow');
if (empty($payments)) $this->error('获取订单支付参数失败');
// 读取支付通道配置
$query = BaseUserPayment::mk()->where(['status' => 1, 'deleted' => 0]);
$query->whereIn('code', str2arr($payments))->whereIn('type', PaymentService::getTypeApi($this->type));
$result = $query->order('sort desc,id desc')->column('type,code,name,cover,content,remark', 'code');
foreach ($result as &$vo) $vo['content'] = ['voucher_qrcode' => json_decode($vo['content'])->voucher_qrcode ?? ''];
$this->success('获取支付参数数据', array_values($result));
}
/**
* 获取订单支付状态
* @throws \think\db\exception\DbException
*/
public function payment()
{
$data = $this->_vali([
'uuid.value' => $this->uuid,
'order_no.require' => '单号不能为空',
'order_remark.default' => '',
'payment_code.require' => '支付不能为空',
'payment_back.default' => '', # 支付回跳地址
'payment_image.default' => '', # 支付凭证图片
]);
[$map, $order] = $this->getOrderData();
if ($order['status'] !== 2) $this->error('不能发起支付');
if ($order['payment_status'] > 0) $this->error('已经完成支付');
// 更新订单备注
if (!empty($data['order_remark'])) {
ShopOrder::mk()->where($map)->update([
'order_remark' => $data['order_remark'],
]);
}
// 自动处理用户字段
$openid = '';
if (in_array($this->type, [UserAdminService::API_TYPE_WXAPP, UserAdminService::API_TYPE_WECHAT])) {
$openid = $this->user[UserAdminService::TYPES[$this->type]['auth']] ?? '';
if (empty($openid)) $this->error("发起支付失败");
}
try {
// 返回订单数据及支付发起参数
$type = $order['amount_real'] <= 0 ? 'empty' : $data['payment_code'];
$param = PaymentService::instance($type)->create($openid, $order['order_no'], $order['amount_real'], '商城订单支付', '', $data['payment_back'], $data['payment_image']);
$this->success('获取支付参数', ['order' => ShopOrder::mk()->where($map)->find() ?: new \stdClass(), 'param' => $param]);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
$this->error($exception->getMessage());
}
}
/**
* 主动取消未支付的订单
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function cancel()
{
[$map, $order] = $this->getOrderData();
if (in_array($order['status'], [1, 2, 3])) {
$result = ShopOrder::mk()->where($map)->update([
'status' => 0,
'cancel_status' => 1,
'cancel_remark' => '用户主动取消订单',
'cancel_datetime' => date('Y-m-d H:i:s'),
]);
if ($result !== false && OrderService::stock($order['order_no'])) {
// 触发订单取消事件
$this->app->event->trigger('ShopOrderCancel', $order['order_no']);
// 返回处理成功数据
$this->success('订单取消成功');
} else {
$this->error('订单取消失败');
}
} else {
$this->error('订单不可取消');
}
}
/**
* 用户主动删除已取消的订单
* @throws \think\db\exception\DbException
*/
public function remove()
{
[$map, $order] = $this->getOrderData();
if (empty($order)) $this->error('读取订单失败');
if ($order['status'] == 0) {
$result = ShopOrder::mk()->where($map)->update([
'status' => 0,
'deleted_status' => 1,
'deleted_remark' => '用户主动删除订单',
'deleted_datetime' => date('Y-m-d H:i:s'),
]);
if ($result !== false) {
// 触发订单删除事件
$this->app->event->trigger('ShopOrderRemove', $order['order_no']);
// 返回处理成功数据
$this->success('订单删除成功');
} else {
$this->error('订单删除失败');
}
} else {
$this->error('订单不可删除');
}
}
/**
* 订单确认收货
* @throws \think\db\exception\DbException
*/
public function confirm()
{
[$map, $order] = $this->getOrderData();
if ($order['status'] == 5) {
if (ShopOrder::mk()->where($map)->update(['status' => 6]) !== false) {
// 触发订单确认事件
$this->app->event->trigger('ShopOrderConfirm', $order['order_no']);
// 返回处理成功数据
$this->success('订单确认成功');
} else {
$this->error('订单确认失败');
}
} else {
$this->error('订单确认失败');
}
}
/**
* 获取输入订单
* @return array [map, order]
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function getOrderData(): array
{
$map = $this->_vali([
'uuid.value' => $this->uuid,
'order_no.require' => '单号不能为空',
]);
$order = ShopOrder::mk()->where($map)->find();
if (empty($order)) $this->error('读取订单失败');
return [$map, $order];
}
/**
* 订单状态统计
*/
public function total()
{
$data = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0];
$query = ShopOrder::mk()->where(['uuid' => $this->uuid, 'deleted_status' => 0]);
foreach ($query->field('status,count(1) count')->group('status')->cursor() as $item) {
$data["t{$item['status']}"] = $item['count'];
}
$this->success('获取订单统计', $data);
}
/**
* 物流追踪查询
*/
public function track()
{
try {
$data = $this->_vali([
'code.require' => '快递不能为空',
'number.require' => '单号不能为空'
]);
$result = ExpressService::query($data['code'], $data['number']);
empty($result['code']) ? $this->error($result['info']) : $this->success('快递追踪信息', $result);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
$this->error($exception->getMessage());
}
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace app\data\controller\api\auth;
use app\data\controller\api\Auth;
use app\data\model\BaseUserUpgrade;
use app\data\model\DataUserRebate;
use app\data\service\RebateService;
/**
* 用户返利管理
* Class Rebate
* @package app\data\controller\api\auth
*/
class Rebate extends Auth
{
/**
* 获取用户返利记录
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function get()
{
$date = trim(input('date', date('Y-m')), '-');
[$map, $year] = [['uuid' => $this->uuid], substr($date, 0, 4)];
$query = DataUserRebate::mQuery()->where($map)->equal('type,status')->whereLike('date', "{$date}%");
$this->success('获取返利统计', array_merge($query->order('id desc')->page(true, false, false, 10), [
'total' => [
'年度' => DataUserRebate::mQuery()->where($map)->equal('type,status')->whereLike('date', "{$year}%")->db()->sum('amount'),
'月度' => DataUserRebate::mQuery()->where($map)->equal('type,status')->whereLike('date', "{$date}%")->db()->sum('amount'),
],
]));
}
/**
* 获取我的奖励
*/
public function prize()
{
[$map, $data] = [['number' => $this->user['vip_code']], []];
$prizes = DataUserRebate::mk()->group('name')->column('name');
$rebate = BaseUserUpgrade::mk()->where($map)->value('rebate_rule', '');
$codemap = array_merge($prizes, str2arr($rebate));
foreach (RebateService::PRIZES as $prize) {
if (in_array($prize['code'], $codemap)) $data[] = $prize;
}
$this->success('获取我的奖励', $data);
}
/**
* 获取奖励配置
*/
public function prizes()
{
$this->success('获取系统奖励', array_values(RebateService::PRIZES));
}
}

View File

@ -1,162 +0,0 @@
<?php
namespace app\data\controller\api\auth;
use app\data\controller\api\Auth;
use app\data\model\DataUserTransfer;
use app\data\service\UserRebateService;
use app\data\service\UserTransferService;
use think\admin\extend\CodeExtend;
/**
* 用户提现接口
* Class Transfer
* @package app\data\controller\api\auth
*/
class Transfer extends Auth
{
/**
* 提交提现处理
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function add()
{
// 检查用户状态
$this->checkUserStatus();
// 接收输入数据
$data = $this->_vali([
'type.require' => '提现方式不能为空!',
'amount.require' => '提现金额不能为空!',
'remark.default' => '用户提交提现申请!',
]);
$state = UserTransferService::config('status');
if (empty($state)) $this->error('提现还没有开启!');
$transfers = UserTransferService::config('transfer');
if (empty($transfers[$data['type']]['state'])) $this->error('提现方式已停用!');
// 提现数据补充
$data['uuid'] = $this->uuid;
$data['date'] = date('Y-m-d');
$data['code'] = CodeExtend::uniqidDate(20, 'T');
// 提现状态处理
if (empty($transfers[$data['type']]['state']['audit'])) {
$data['status'] = 1;
$data['audit_status'] = 0;
} else {
$data['status'] = 3;
$data['audit_status'] = 1;
$data['audit_remark'] = '提现免审核';
$data['audit_datetime'] = date('Y-m-d H:i:s');
}
// 扣除手续费
$chargeRate = floatval(UserTransferService::config('charge'));
$data['charge_rate'] = $chargeRate;
$data['charge_amount'] = $chargeRate * $data['amount'] / 100;
// 检查可提现余额
[$total, $count] = UserRebateService::amount($this->uuid);
if ($total - $count < $data['amount']) $this->error('可提现余额不足!');
// 提现方式处理
if ($data['type'] == 'alipay_account') {
$data = array_merge($data, $this->_vali([
'alipay_user.require' => '开户姓名不能为空!',
'alipay_code.require' => '支付账号不能为空!',
]));
} elseif (in_array($data['type'], ['wechat_qrcode', 'alipay_qrcode'])) {
$data = array_merge($data, $this->_vali([
'qrcode.require' => '收款码不能为空!',
]));
} elseif (in_array($data['type'], ['wechat_banks', 'transfer_banks'])) {
$data = array_merge($data, $this->_vali([
'bank_wseq.require' => '银行编号不能为空!',
'bank_name.require' => '银行名称不能为空!',
'bank_user.require' => '开户账号不能为空!',
'bank_bran.require' => '银行分行不能为空!',
'bank_code.require' => '银行卡号不能为空!',
]));
} elseif ($data['type'] != 'wechat_wallet') {
$this->error('转账方式不存在!');
}
// 当日提现次数限制
$map = ['uuid' => $this->uuid, 'type' => $data['type'], 'date' => $data['date']];
$count = DataUserTransfer::mk()->where($map)->count();
if ($count >= $transfers[$data['type']]['dayNumber']) $this->error("当日提现次数受限");
// 提现金额范围控制
if ($transfers[$data['type']]['minAmount'] > $data['amount']) {
$this->error("不能少于{$transfers[$data['type']]['minAmount']}");
}
if ($transfers[$data['type']]['maxAmount'] < $data['amount']) {
$this->error("不能大于{$transfers[$data['type']]['maxAmount']}");
}
// 写入用户提现数据
if (DataUserTransfer::mk()->insert($data) !== false) {
UserRebateService::amount($this->uuid);
$this->success('提现申请成功');
} else {
$this->error('提现申请失败');
}
}
/**
* 用户提现记录
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function get()
{
$query = DataUserTransfer::mQuery()->where(['uuid' => $this->uuid]);
$result = $query->like('date,code')->in('status')->order('id desc')->page(true, false, false, 10);
// 统计历史数据
$map = [['uuid', '=', $this->uuid], ['status', '>', 0]];
[$total, $count, $locks] = UserRebateService::amount($this->uuid);
$this->success('获取提现成功', array_merge($result, [
'total' => [
'锁定' => $locks,
'可提' => $total - $count,
'上月' => DataUserTransfer::mk()->where($map)->whereLike('date', date("Y-m-%", strtotime('-1 month')))->sum('amount'),
'本月' => DataUserTransfer::mk()->where($map)->whereLike('date', date("Y-m-%"))->sum('amount'),
'全年' => DataUserTransfer::mk()->where($map)->whereLike('date', date("Y-%"))->sum('amount'),
],
]));
}
/**
* 用户取消提现
*/
public function cancel()
{
$data = $this->_vali(['uuid.value' => $this->uuid, 'code.require' => '单号不能为空!']);
DataUserTransfer::mk()->where($data)->whereIn('status', [1, 2, 3])->update([
'status' => 0, 'change_time' => date("Y-m-d H:i:s"), 'change_desc' => '用户主动取消提现',
]);
UserRebateService::amount($this->uuid);
$this->success('取消提现成功');
}
/**
* 用户确认提现
*/
public function confirm()
{
$data = $this->_vali(['uuid.value' => $this->uuid, 'code.require' => '单号不能为空!']);
DataUserTransfer::mk()->where($data)->whereIn('status', [4])->update([
'status' => 5, 'change_time' => date("Y-m-d H:i:s"), 'change_desc' => '用户主动确认收款',
]);
UserRebateService::amount($this->uuid);
$this->success('确认收款成功');
}
/**
* 获取用户提现配置
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function config()
{
$data = UserTransferService::config();
$data['banks'] = UserTransferService::instance()->banks();
$this->success('获取用户提现配置', $data);
}
}

View File

@ -1,72 +0,0 @@
<?php
namespace app\data\controller\base;
use think\admin\Controller;
/**
* 应用参数配置
* Class Config
* @package app\data\controller\base
*/
class Config extends Controller
{
/**
* 微信小程序配置
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function wxapp()
{
$this->skey = 'wxapp';
$this->title = '微信小程序配置';
$this->__sysdata('wxapp');
}
/**
* 邀请二维码设置
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function cropper()
{
$this->skey = 'cropper';
$this->title = '邀请二维码设置';
$this->__sysdata('cropper');
}
/**
* 显示并保存数据
* @param string $template 模板文件
* @param string $history 跳转处理
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function __sysdata(string $template, string $history = '')
{
if ($this->request->isGet()) {
$this->data = sysdata($this->skey);
$this->fetch($template);
}
if ($this->request->isPost()) {
if (is_string(input('data'))) {
$data = json_decode(input('data'), true) ?: [];
} else {
$data = $this->request->post();
}
if (sysdata($this->skey, $data) !== false) {
$this->success('内容保存成功!', $history);
} else {
$this->error('内容保存失败,请稍候再试!');
}
}
}
}

View File

@ -1,93 +0,0 @@
<?php
namespace app\data\controller\base;
use app\data\model\BaseUserDiscount;
use app\data\model\BaseUserUpgrade;
use app\data\service\UserUpgradeService;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
/**
* 折扣方案管理
* Class Discount
* @package app\data\controller\base
*/
class Discount extends Controller
{
/**
* 折扣方案管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->type = input('get.type', 'index');
BaseUserDiscount::mQuery()->layTable(function () {
$this->title = '折扣方案管理';
}, function (QueryHelper $query) {
$query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]);
});
}
/**
* 添加折扣方案
* @auth true
*/
public function add()
{
BaseUserDiscount::mForm('form');
}
/**
* 编辑折扣方案
* @auth true
*/
public function edit()
{
BaseUserDiscount::mForm('form');
}
/**
* 表单数据处理
* @param array $vo
*/
protected function _form_filter(array &$vo)
{
if ($this->request->isPost()) {
$rule = [];
foreach ($vo as $k => $v) if (stripos($k, '_level_') !== false) {
[, $level] = explode('_level_', $k);
$rule[] = ['level' => $level, 'discount' => $v];
}
$vo['items'] = json_encode($rule, JSON_UNESCAPED_UNICODE);
} else {
$this->levels = BaseUserUpgrade::items();
if (empty($this->levels)) $this->error('未配置用户等级!');
foreach ($vo['items'] ?? [] as $item) {
$vo["_level_{$item['level']}"] = $item['discount'];
}
}
}
/**
* 修改折扣方案状态
* @auth true
*/
public function state()
{
BaseUserDiscount::mSave();
}
/**
* 删除折扣方案配置
* @auth true
*/
public function remove()
{
BaseUserDiscount::mDelete();
}
}

View File

@ -1,85 +0,0 @@
<?php
namespace app\data\controller\base;
use app\data\model\BaseUserMessage;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
/**
* 系统通知管理
* Class Notify
* @package app\data\controller\base
*/
class Message extends Controller
{
/**
* 系统通知管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
BaseUserMessage::mQuery()->layTable(function () {
$this->title = '系统通知管理';
}, function (QueryHelper $query) {
$query->where(['deleted' => 0]);
$query->like('name')->equal('status')->dateBetween('create_at');
});
}
/**
* 添加系统通知
* @auth true
*/
public function add()
{
$this->title = '添加系统通知';
BaseUserMessage::mForm('form');
}
/**
* 编辑系统通知
* @auth true
*/
public function edit()
{
$this->title = '编辑系统通知';
BaseUserMessage::mForm('form');
}
/**
* 表单结果处理
* @param boolean $state
*/
protected function _form_result(bool $state)
{
if ($state) {
$this->success('内容保存成功!', 'javascript:history.back()');
}
}
/**
* 修改通知状态
* @auth true
*/
public function state()
{
BaseUserMessage::mSave($this->_vali([
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]));
}
/**
* 删除系统通知
* @auth true
*/
public function remove()
{
BaseUserMessage::mDelete();
}
}

View File

@ -1,87 +0,0 @@
<?php
namespace app\data\controller\base;
use think\admin\Controller;
use think\admin\model\SystemBase;
/**
* 页面内容管理
* Class Pager
* @package app\data\controller\base
*/
class Pager extends Controller
{
/**
* 字典类型
* @var string
*/
protected $type = '页面内容';
/**
* 页面类型
* @var array
*/
protected $types = [];
/**
* 控制器初始化
* @return void
*/
protected function initialize()
{
$this->types = SystemBase::mk()->items($this->type);
}
/**
* 内容页面管理
* @auth true
* @menu true
*/
public function index()
{
$this->title = '内容页面管理';
$this->fetch();
}
/**
* 内容页面编辑
* @auth true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function edit()
{
$this->skey = input('get.type', '');
$this->base = $this->types[$this->skey] ?? [];
if (empty($this->base)) $this->error('未配置基础数据!');
$this->title = "编辑{$this->base['name']}";
$this->sysdata();
}
/**
* 显示并保存数据
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function sysdata()
{
if ($this->request->isGet()) {
$this->data = sysdata($this->skey);
$this->fetch('form');
} elseif ($this->request->isPost()) {
if (is_string(input('data'))) {
$data = json_decode(input('data'), true) ?: [];
} else {
$data = $this->request->post();
}
if (sysdata($this->skey, $data) !== false) {
$this->success('内容保存成功!', 'javascript:history.back()');
} else {
$this->error('内容保存失败,请稍候再试!');
}
}
}
}

View File

@ -1,134 +0,0 @@
<?php
namespace app\data\controller\base;
use app\data\model\BaseUserPayment;
use app\data\service\PaymentService;
use app\data\service\UserAdminService;
use think\admin\Controller;
use think\admin\extend\CodeExtend;
use think\admin\helper\QueryHelper;
/**
* 支付通道管理
* Class Payment
* @package app\data\controller\base
*/
class Payment extends Controller
{
/**
* 支付通道类型
* @var array
*/
protected $types = PaymentService::TYPES;
/**
* 支付通道管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->type = input('get.type', 'index');
BaseUserPayment::mQuery()->layTable(function () {
$this->title = '支付通道管理';
}, function (QueryHelper $query) {
$query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]);
$query->like('name,code')->equal('status,type#ptype')->dateBetween('create_at');
});
}
/**
* 获取支付名称
* @param array $data
* @return void
*/
protected function _page_filter(array &$data)
{
foreach ($data as &$vo) {
$vo['ntype'] = $this->types[$vo['type']]['name'] ?? $vo['type'];
}
}
/**
* 添加支付通道
* @auth true
*/
public function add()
{
$this->title = '添加支付通道';
BaseUserPayment::mForm('form');
}
/**
* 编辑支付通道
* @auth true
*/
public function edit()
{
$this->title = '编辑支付通道';
BaseUserPayment::mForm('form');
}
/**
* 数据表单处理
* @param array $data
*/
protected function _form_filter(array &$data)
{
if (empty($data['code'])) {
$data['code'] = CodeExtend::uniqidNumber(20, 'M');
}
if ($this->request->isGet()) {
$this->payments = [];
foreach ($this->types as $k => $vo) {
$allow = [];
foreach ($vo['bind'] as $api) if (isset(UserAdminService::TYPES[$api])) {
$allow[$api] = UserAdminService::TYPES[$api]['name'];
}
if (empty($allow)) continue;
$this->payments[$k] = array_merge($vo, ['allow' => join('、', $allow)]);
}
$data['content'] = json_decode($data['content'] ?? '[]', true) ?: [];
} else {
if (empty($data['type'])) $this->error('请选择支付通道并配置参数!');
if (empty($data['cover'])) $this->error('请上传支付方式图标!');
$data['content'] = json_encode($this->request->post() ?: [], JSON_UNESCAPED_UNICODE);
}
}
/**
* 表单结果处理
* @param boolean $state
*/
protected function _form_result(bool $state)
{
if ($state) {
$this->success('支付通道保存成功!', 'javascript:history.back()');
}
}
/**
* 修改通道状态
* @auth true
*/
public function state()
{
BaseUserPayment::mSave($this->_vali([
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]));
}
/**
* 删除支付通道
* @auth true
*/
public function remove()
{
BaseUserPayment::mDelete();
}
}

View File

@ -1,102 +0,0 @@
<?php
namespace app\data\controller\base;
use think\admin\Controller;
use think\admin\model\SystemBase;
/**
* 图片内容管理
* Class Slider
* @package app\data\controller\base
*/
class Slider extends Controller
{
/**
* 跳转规则定义
* @var string[]
*/
protected $rules = [
'#' => ['name' => '不跳转'],
'LK' => ['name' => '自定义链接'],
'NL' => ['name' => '新闻资讯列表'],
'NS' => ['name' => '新闻资讯详情', 'node' => 'data/news.item/select'],
];
/**
* 数据类型
* @var string
*/
protected $type = '图片内容';
/**
* 页面类型
* @var array
*/
protected $types = [];
/**
* 控制器初始化
* @return void
*/
protected function initialize()
{
$this->types = SystemBase::mk()->items($this->type);
foreach ($this->types as &$type) {
if (preg_match('/^(.*?)#(\d+)$/', $type['name'], $matches)) {
$type['name'] = $matches[1];
$type['number'] = $matches[2];
} else {
$type['number'] = 0;
}
}
}
/**
* 图片内容管理
* @auth true
* @menu true
*/
public function index()
{
$this->title = '图片内容管理';
$this->fetch();
}
/**
* 编辑图片内容
* @auth true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function edit()
{
$this->skey = input('get.type', '');
$this->base = $this->types[$this->skey] ?? [];
if (empty($this->base)) $this->error('未配置基础数据!');
$this->number = $this->base['number'];
$this->sysdata();
}
/**
* 保存图片内容
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function sysdata()
{
if ($this->request->isGet()) {
$this->data = sysdata($this->skey);
$this->title = "{$this->base['name']}管理";
$this->fetch('form');
} else {
if (sysdata($this->skey, json_decode(input('data'), true))) {
$this->success("{$this->base['name']}保存成功!");
} else {
$this->error("{$this->base['name']}保存失败,请稍候再试!");
}
}
}
}

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