diff --git a/app/admin/lang/en/auth/group.php b/app/admin/lang/en/auth/group.php
index 19aa20f..1a80451 100644
--- a/app/admin/lang/en/auth/group.php
+++ b/app/admin/lang/en/auth/group.php
@@ -1,6 +1,13 @@
'Super administrator',
+ 'name' => 'Group name',
+ 'Please select rules' => 'Please select rules',
+ 'Super administrator' => 'Super administrator',
'No permission' => 'No permission',
'You cannot modify your own management group!' => 'You cannot modify your own management group!',
+ 'You need to have all permissions of this group to operate this group~' => 'You need to have all permissions of this group to operate this group~',
+ 'You need to have all the permissions of the group and have additional permissions before you can operate the group~' => 'You need to have all the permissions of the group and have additional permissions before you can operate the group~',
+ 'Role group has all your rights, please contact the upper administrator to add or do not need to add!' => 'Role group has all your rights, please contact the upper administrator to add or do not need to add!',
+ 'The group permission node exceeds the range that can be allocated' => 'The group permission node exceeds the range that can be allocated, please refresh and try again~',
+ 'Remark lang' => 'For system security, the hierarchical relationship of role groups is for reference only. The actual hierarchy is determined by the number of permission nodes: same permissions = peer, containing and having additional permissions = superior. Peers cannot manage peers; superiors can assign their permission nodes to subordinates. For special cases where an admin needs to become a superior, create a virtual permission node.',
];
diff --git a/dafuweng-webman/app/admin/controller/Index.php b/dafuweng-webman/app/admin/controller/Index.php
index 0cfaaeb..da42fc4 100644
--- a/dafuweng-webman/app/admin/controller/Index.php
+++ b/dafuweng-webman/app/admin/controller/Index.php
@@ -109,10 +109,16 @@ class Index extends Backend
$res = $this->auth->login($username, $password, (bool) $keep);
if ($res === true) {
$userInfo = $this->auth->getInfo();
- // 兜底:若 getInfo 未返回 token,在控制器层生成并入库
- if (empty($userInfo['token']) && $this->auth->isLogin()) {
+ $adminId = $this->auth->id;
+ $keepTime = (int) config('buildadmin.admin_token_keep_time', 86400 * 3);
+ // 兜底:若 getInfo 未返回 token,在控制器层生成并入库(login 成功时必有 adminId)
+ if (empty($userInfo['token']) && $adminId) {
$userInfo['token'] = Random::uuid();
- Token::set($userInfo['token'], \app\admin\library\Auth::TOKEN_TYPE, $this->auth->id, (int) config('buildadmin.admin_token_keep_time', 86400 * 3));
+ Token::set($userInfo['token'], \app\admin\library\Auth::TOKEN_TYPE, $adminId, $keepTime);
+ }
+ if (empty($userInfo['refresh_token']) && $keep && $adminId) {
+ $userInfo['refresh_token'] = Random::uuid();
+ Token::set($userInfo['refresh_token'], \app\admin\library\Auth::TOKEN_TYPE . '-refresh', $adminId, 2592000);
}
return $this->success(__('Login succeeded!'), [
'userInfo' => $userInfo
diff --git a/dafuweng-webman/app/admin/controller/auth/Group.php b/dafuweng-webman/app/admin/controller/auth/Group.php
index 28575de..26cb0ec 100644
--- a/dafuweng-webman/app/admin/controller/auth/Group.php
+++ b/dafuweng-webman/app/admin/controller/auth/Group.php
@@ -41,8 +41,8 @@ class Group extends Backend
$this->tree = Tree::instance();
$isTree = $request->get('isTree') ?? $request->post('isTree') ?? true;
- $this->initValue = $request->get('initValue') ?? [];
- $this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : [];
+ $initValue = $request->get('initValue') ?? $request->post('initValue') ?? [];
+ $this->initValue = is_array($initValue) ? array_filter($initValue) : [];
$this->keyword = $request->get('quickSearch') ?? $request->post('quickSearch') ?? '';
$this->assembleTree = $isTree && !$this->initValue;
@@ -185,9 +185,10 @@ class Group extends Backend
unset($rules[$ruKey]);
}
}
- $row->rules = array_values($rules);
+ $rowData = $row->toArray();
+ $rowData['rules'] = array_values($rules);
return $this->success('', [
- 'row' => $row
+ 'row' => $rowData
]);
}
@@ -323,7 +324,7 @@ class Group extends Backend
$rules = explode(',', $datum['rules']);
if ($rules) {
$rulesFirstTitle = AdminRule::where('id', $rules[0])->value('title');
- $datum['rules'] = count($rules) == 1 ? $rulesFirstTitle : $rulesFirstTitle . '等 ' . count($rules) . ' 项';
+ $datum['rules'] = count($rules) == 1 ? $rulesFirstTitle : __('%first% etc. %count% items', ['%first%' => $rulesFirstTitle, '%count%' => count($rules)]);
}
}
} else {
diff --git a/dafuweng-webman/app/admin/controller/auth/Rule.php b/dafuweng-webman/app/admin/controller/auth/Rule.php
index 01e1d2a..c87455d 100644
--- a/dafuweng-webman/app/admin/controller/auth/Rule.php
+++ b/dafuweng-webman/app/admin/controller/auth/Rule.php
@@ -39,8 +39,8 @@ class Rule extends Backend
$this->model = new AdminRule();
$this->tree = Tree::instance();
$isTree = $request->get('isTree') ?? $request->post('isTree') ?? true;
- $this->initValue = $request->get('initValue') ?? [];
- $this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : [];
+ $initValue = $request->get('initValue') ?? $request->post('initValue') ?? [];
+ $this->initValue = is_array($initValue) ? array_filter($initValue) : [];
$this->keyword = $request->get('quickSearch') ?? $request->post('quickSearch') ?? '';
$this->assembleTree = $isTree && !$this->initValue;
return null;
diff --git a/dafuweng-webman/app/admin/controller/crud/Crud.php b/dafuweng-webman/app/admin/controller/crud/Crud.php
index a3c8046..992cee8 100644
--- a/dafuweng-webman/app/admin/controller/crud/Crud.php
+++ b/dafuweng-webman/app/admin/controller/crud/Crud.php
@@ -30,6 +30,7 @@ class Crud extends Backend
protected string $webTranslate = '';
protected array $langTsData = [];
protected array $dtStringToArray = ['checkbox', 'selects', 'remoteSelects', 'city', 'images', 'files'];
+ protected array $noNeedLogin = ['getFileData'];
protected array $noNeedPermission = ['logStart', 'getFileData', 'parseFieldData', 'generateCheck', 'uploadCompleted'];
protected function initController(Request $request): ?Response
@@ -288,56 +289,61 @@ class Crud extends Backend
public function getFileData(Request $request): Response
{
- $response = $this->initializeBackend($request);
- if ($response !== null) return $response;
-
- $table = $request->get('table');
- $commonModel = $request->get('commonModel', false);
-
- if (!$table) {
- return $this->error(__('Parameter error'));
- }
-
try {
+ $response = $this->initializeBackend($request);
+ if ($response !== null) return $response;
+
+ $table = $request->get('table');
+ $commonModel = $request->get('commonModel', false);
+
+ if (!$table) {
+ return $this->error(__('Parameter error'));
+ }
+
$modelFile = Helper::parseNameData($commonModel ? 'common' : 'admin', $table, 'model');
$validateFile = Helper::parseNameData($commonModel ? 'common' : 'admin', $table, 'validate');
$controllerFile = Helper::parseNameData('admin', $table, 'controller');
$webViewsDir = Helper::parseWebDirNameData($table, 'views');
+
+ $adminModelDir = root_path() . 'app' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR;
+ $commonModelDir = root_path() . 'app' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR;
+ $adminModelFiles = is_dir($adminModelDir) ? Filesystem::getDirFiles($adminModelDir) : [];
+ $commonModelFiles = is_dir($commonModelDir) ? Filesystem::getDirFiles($commonModelDir) : [];
+ $adminControllerFiles = get_controller_list();
+
+ $modelFileList = [];
+ $controllerFiles = [];
+ foreach ($adminModelFiles as $item) {
+ $item = Filesystem::fsFit('app/admin/model/' . $item);
+ $modelFileList[$item] = $item;
+ }
+ foreach ($commonModelFiles as $item) {
+ $item = Filesystem::fsFit('app/common/model/' . $item);
+ $modelFileList[$item] = $item;
+ }
+
+ $outExcludeController = ['Addon.php', 'Ajax.php', 'Dashboard.php', 'Index.php', 'Module.php', 'Terminal.php', 'routine/AdminInfo.php', 'routine/Config.php'];
+ foreach ($adminControllerFiles as $item) {
+ if (!in_array($item, $outExcludeController)) {
+ $item = Filesystem::fsFit('app/admin/controller/' . $item);
+ $controllerFiles[$item] = $item;
+ }
+ }
+
+ // 路径统一使用正斜杠,便于 UI 展示及跨平台一致(相对于 dafuweng-webman 项目根)
+ $pathFit = fn(string $p): string => str_replace('\\', '/', $p);
+
+ return $this->success('', [
+ 'modelFile' => $pathFit($modelFile['rootFileName']),
+ 'controllerFile' => $pathFit($controllerFile['rootFileName']),
+ 'validateFile' => $pathFit($validateFile['rootFileName']),
+ 'controllerFileList' => array_map($pathFit, $controllerFiles),
+ 'modelFileList' => array_map($pathFit, $modelFileList),
+ 'webViewsDir' => $pathFit($webViewsDir['views']),
+ ]);
} catch (Throwable $e) {
return $this->error($e->getMessage());
}
-
- $adminModelFiles = Filesystem::getDirFiles(root_path() . 'app' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
- $commonModelFiles = Filesystem::getDirFiles(root_path() . 'app' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
- $adminControllerFiles = get_controller_list();
-
- $modelFileList = [];
- $controllerFiles = [];
- foreach ($adminModelFiles as $item) {
- $item = Filesystem::fsFit('app/admin/model/' . $item);
- $modelFileList[$item] = $item;
- }
- foreach ($commonModelFiles as $item) {
- $item = Filesystem::fsFit('app/common/model/' . $item);
- $modelFileList[$item] = $item;
- }
-
- $outExcludeController = ['Addon.php', 'Ajax.php', 'Dashboard.php', 'Index.php', 'Module.php', 'Terminal.php', 'routine/AdminInfo.php', 'routine/Config.php'];
- foreach ($adminControllerFiles as $item) {
- if (!in_array($item, $outExcludeController)) {
- $item = Filesystem::fsFit('app/admin/controller/' . $item);
- $controllerFiles[$item] = $item;
- }
- }
-
- return $this->success('', [
- 'modelFile' => $modelFile['rootFileName'],
- 'controllerFile' => $controllerFile['rootFileName'],
- 'validateFile' => $validateFile['rootFileName'],
- 'controllerFileList' => $controllerFiles,
- 'modelFileList' => $modelFileList,
- 'webViewsDir' => $webViewsDir['views'],
- ]);
}
public function checkCrudLog(Request $request): Response
@@ -478,8 +484,8 @@ class Crud extends Backend
$tableName = TableManager::tableName($field['form']['remote-table'], false, $table['databaseConnection'] ?? null);
$rnPattern = '/(.*)(_ids|_id)$/';
$relationName = preg_match($rnPattern, $field['name'])
- ? parse_name(preg_replace($rnPattern, '$1', $field['name']), 1, false)
- : parse_name($field['name'] . '_table', 1, false);
+ ? parse_name(preg_replace($rnPattern, '$1', $field['name']), 1)
+ : parse_name($field['name'] . '_table', 1);
if (empty($field['form']['remote-model']) || !file_exists(root_path() . $field['form']['remote-model'])) {
$joinModelFile = Helper::parseNameData('admin', $tableName, 'model', $field['form']['remote-model'] ?? '');
@@ -613,7 +619,8 @@ class Crud extends Backend
{
if (($field['designType'] ?? '') == 'editor') {
$this->formVueData['bigDialog'] = true;
- $this->controllerData['filterRule'] = "\n" . Helper::tab(2) . '$this->request->filter(\'clean_xss\');';
+ // Webman Request 无 filter 方法,使用 inputFilter 由 Backend trait 在 add/edit 时应用
+ $this->controllerData['filterRule'] = "\n" . Helper::tab(2) . '$this->inputFilter = \'clean_xss\';';
}
if (!empty($table['defaultSortField']) && !empty($table['defaultSortType'])) {
$defaultSortField = "{$table['defaultSortField']},{$table['defaultSortType']}";
diff --git a/dafuweng-webman/app/admin/controller/user/Group.php b/dafuweng-webman/app/admin/controller/user/Group.php
index f0ec6b0..af00ee2 100644
--- a/dafuweng-webman/app/admin/controller/user/Group.php
+++ b/dafuweng-webman/app/admin/controller/user/Group.php
@@ -24,6 +24,25 @@ class Group extends Backend
return null;
}
+ public function select(Request $request): Response
+ {
+ $response = $this->initializeBackend($request);
+ if ($response !== null) return $response;
+
+ list($where, $alias, $limit, $order) = $this->queryBuilder();
+ $data = $this->model
+ ->alias($alias)
+ ->where($where)
+ ->order($order)
+ ->limit(9999)
+ ->select()
+ ->toArray();
+
+ return $this->success('', [
+ 'options' => $data,
+ ]);
+ }
+
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
@@ -106,8 +125,9 @@ class Group extends Backend
unset($rules[$ruKey]);
}
}
- $row->rules = array_values($rules);
- return $this->success('', ['row' => $row]);
+ $rowData = $row->toArray();
+ $rowData['rules'] = array_values($rules);
+ return $this->success('', ['row' => $rowData]);
}
private function handleRules(array $data): array
diff --git a/dafuweng-webman/app/admin/controller/user/Rule.php b/dafuweng-webman/app/admin/controller/user/Rule.php
index f1ab204..5647fcb 100644
--- a/dafuweng-webman/app/admin/controller/user/Rule.php
+++ b/dafuweng-webman/app/admin/controller/user/Rule.php
@@ -27,8 +27,8 @@ class Rule extends Backend
$this->model = new UserRule();
$this->tree = Tree::instance();
$isTree = filter_var($request->get('isTree', $request->post('isTree', true)), FILTER_VALIDATE_BOOLEAN);
- $this->initValue = $request->get('initValue', $request->post('initValue', []));
- $this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : [];
+ $initValue = $request->get('initValue') ?? $request->post('initValue') ?? [];
+ $this->initValue = is_array($initValue) ? array_filter($initValue) : [];
$this->keyword = $request->get('quickSearch', $request->post('quickSearch', ''));
$this->assembleTree = $isTree && !$this->initValue;
return null;
diff --git a/dafuweng-webman/app/admin/lang/en.php b/dafuweng-webman/app/admin/lang/en.php
new file mode 100644
index 0000000..087e8a8
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en.php
@@ -0,0 +1,98 @@
+ 'Please login first',
+ 'No background menu, please contact super administrator!' => 'No background menu, please contact the super administrator!',
+ 'You have already logged in. There is no need to log in again~' => 'You have already logged in. There is no need to log in again~',
+ 'Login succeeded!' => 'Login succeeded!',
+ 'Incorrect user name or password!' => 'Incorrect username or password!',
+ 'Login' => 'Login',
+ 'Logout' => 'Logout',
+ 'Please input correct password' => 'Please enter the correct password',
+ 'You have no permission' => 'You have no permission to operate',
+ 'Username' => 'Username',
+ 'Password' => 'Password',
+ 'Nickname' => 'Nickname',
+ 'Email' => 'Email',
+ 'Mobile' => 'Mobile Number',
+ 'Captcha' => 'Captcha',
+ 'CaptchaId' => 'Captcha Id',
+ 'Please enter the correct verification code' => 'Please enter the correct Captcha!',
+ 'Captcha error' => 'Captcha error!',
+ 'Parameter %s can not be empty' => 'Parameter %s can not be empty',
+ 'Record not found' => 'Record not found',
+ 'No rows were added' => 'No rows were added',
+ 'No rows were deleted' => 'No rows were deleted',
+ 'No rows updated' => 'No rows updated',
+ 'Update successful' => 'Update successful!',
+ 'Added successfully' => 'Added successfully!',
+ 'Deleted successfully' => 'Deleted successfully!',
+ 'Parameter error' => 'Parameter error!',
+ 'File uploaded successfully' => 'File uploaded successfully',
+ 'No files were uploaded' => 'No files were uploaded',
+ 'The uploaded file format is not allowed' => 'The uploaded file format is no allowance.',
+ 'The uploaded image file is not a valid image' => 'The uploaded image file is not a valid image',
+ 'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => 'The uploaded file is too large (%sMiB), maximum file size:%sMiB',
+ 'No files have been uploaded or the file size exceeds the upload limit of the server' => 'No files have been uploaded or the file size exceeds the server upload limit.',
+ 'Unknown' => 'Unknown',
+ 'Super administrator' => 'Super administrator',
+ 'No permission' => 'No permission',
+ '%first% etc. %count% items' => '%first% etc. %count% items',
+ 'Please select rules' => 'Please select rules',
+ 'You cannot modify your own management group!' => 'You cannot modify your own management group!',
+ 'You need to have all permissions of this group to operate this group~' => 'You need to have all permissions of this group to operate this group~',
+ 'You need to have all the permissions of the group and have additional permissions before you can operate the group~' => 'You need to have all the permissions of the group and have additional permissions before you can operate the group~',
+ 'Role group has all your rights, please contact the upper administrator to add or do not need to add!' => 'Role group has all your rights, please contact the upper administrator to add or do not need to add!',
+ 'The group permission node exceeds the range that can be allocated' => 'The group permission node exceeds the range that can be allocated, please refresh and try again~',
+ 'Account not exist' => 'Account does not exist',
+ 'Account disabled' => 'Account is disabled',
+ 'Token login failed' => 'Token login failed',
+ 'Username is incorrect' => 'Incorrect username',
+ 'Please try again after 1 day' => 'The number of login failures exceeds the limit, please try again after 24 hours',
+ 'Password is incorrect' => 'Wrong password',
+ 'You are not logged in' => 'You are not logged in',
+ 'Cache cleaned~' => 'The cache has been cleaned up, please refresh the page.',
+ 'Please use the %s field to sort before operating' => 'Please use the %s field to sort before operating',
+ 'Topic format error' => 'Upload storage subdirectory format error!',
+ 'Driver %s not supported' => 'Driver %s not supported',
+ 'Configuration write failed: %s' => 'Configuration write failed: %s',
+ 'Token expiration' => 'Token expired, please login again!',
+ 'Method not allowed' => 'Method not allowed',
+ 'Variable name' => 'Variable name',
+ 'type' => 'Type',
+ 'title' => 'Title',
+ 'name' => 'Name',
+ 'user_id' => 'User ID',
+ 'score' => 'Score',
+ 'memo' => 'Memo',
+ 'money' => 'Money',
+ 'Rollback successful' => 'Rollback successful!',
+ 'No rows were rolled back' => 'No rows were rolled back',
+ 'Avatar modified successfully!' => 'Avatar modified successfully!',
+ 'Failed to load cloud data' => 'Failed to load cloud data',
+ 'Log start' => 'Log start',
+ 'Parse field data' => 'Parse field data',
+ 'Generate check' => 'Generate check',
+ 'Install module' => 'Install module',
+ 'Change module state' => 'Change module state',
+ 'Uninstall module' => 'Uninstall module',
+ 'Upload module' => 'Upload module',
+ 'upload' => 'Upload',
+ 'Data table does not exist' => 'Data table does not exist',
+ 'Change terminal config' => 'Change terminal config',
+ 'Failed to modify the terminal configuration. Please modify the configuration file manually:%s' => 'Failed to modify the terminal configuration. Please modify the configuration file manually: %s',
+ 'Clear cache' => 'Clear cache',
+ 'The current page configuration item was updated successfully' => 'The current page configuration item was updated successfully!',
+ 'This is a test email' => 'This is a test email',
+ 'Congratulations, receiving this email means that your email service has been configured correctly' => 'Congratulations, receiving this email means that your email service has been configured correctly',
+ 'Test mail sent successfully~' => 'Test mail sent successfully~',
+ 'Name' => 'Name',
+ 'Data Fields' => 'Data Fields',
+ 'Controller' => 'Controller',
+ 'Data Table' => 'Data Table',
+ 'Primary Key' => 'Primary Key',
+ 'Restore successful' => 'Restore successful!',
+ 'No rows were restore' => 'No rows were restored',
+ '%d records and files have been deleted' => '%d records and files have been deleted',
+ 'Please input correct username' => 'Please enter the correct username',
+ 'Group Name Arr' => 'Group Name Arr',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/ajax.php b/dafuweng-webman/app/admin/lang/en/ajax.php
new file mode 100644
index 0000000..e2cf328
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/ajax.php
@@ -0,0 +1,9 @@
+ 'Failed to switch package manager, please modify the configuration file manually:%s',
+ 'Failed to modify the terminal configuration. Please modify the configuration file manually:%s' => 'Failed to modify the terminal configuration, please modify the configuration file manually:%s',
+ 'upload' => 'Upload files',
+ 'Change terminal config' => 'Modify terminal configuration',
+ 'Clear cache' => 'Clear cache',
+ 'Data table does not exist' => 'Data table does not exist',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/auth/admin.php b/dafuweng-webman/app/admin/lang/en/auth/admin.php
new file mode 100644
index 0000000..b1a3073
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/auth/admin.php
@@ -0,0 +1,5 @@
+ 'Administrator Grouping ',
+ 'Please use another administrator account to disable the current account!' => 'Disable the current account, please use another administrator account!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/auth/group.php b/dafuweng-webman/app/admin/lang/en/auth/group.php
new file mode 100644
index 0000000..1a80451
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/auth/group.php
@@ -0,0 +1,13 @@
+ 'Group name',
+ 'Please select rules' => 'Please select rules',
+ 'Super administrator' => 'Super administrator',
+ 'No permission' => 'No permission',
+ 'You cannot modify your own management group!' => 'You cannot modify your own management group!',
+ 'You need to have all permissions of this group to operate this group~' => 'You need to have all permissions of this group to operate this group~',
+ 'You need to have all the permissions of the group and have additional permissions before you can operate the group~' => 'You need to have all the permissions of the group and have additional permissions before you can operate the group~',
+ 'Role group has all your rights, please contact the upper administrator to add or do not need to add!' => 'Role group has all your rights, please contact the upper administrator to add or do not need to add!',
+ 'The group permission node exceeds the range that can be allocated' => 'The group permission node exceeds the range that can be allocated, please refresh and try again~',
+ 'Remark lang' => 'For system security, the hierarchical relationship of role groups is for reference only. The actual hierarchy is determined by the number of permission nodes: same permissions = peer, containing and having additional permissions = superior. Peers cannot manage peers; superiors can assign their permission nodes to subordinates. For special cases where an admin needs to become a superior, create a virtual permission node.',
+];
diff --git a/dafuweng-webman/app/admin/lang/en/auth/menu.php b/dafuweng-webman/app/admin/lang/en/auth/menu.php
new file mode 100644
index 0000000..94d9d4c
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/auth/menu.php
@@ -0,0 +1,6 @@
+ 'Rule type',
+ 'title' => 'Rule title',
+ 'name' => 'Rule name',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/crud/crud.php b/dafuweng-webman/app/admin/lang/en/crud/crud.php
new file mode 100644
index 0000000..522c32c
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/crud/crud.php
@@ -0,0 +1,7 @@
+ 'Field %s failed to be renamed because the field does not exist in the data table',
+ 'del-field fail not exist' => 'Failed to delete field %s because the field does not exist in the data table',
+ 'change-field-attr fail not exist' => 'Description Failed to modify the properties of field %s because the field does not exist in the data table',
+ 'add-field fail exist' => 'Failed to add field %s because the field already exists in the data table',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/dashboard.php b/dafuweng-webman/app/admin/lang/en/dashboard.php
new file mode 100644
index 0000000..57b0f72
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/dashboard.php
@@ -0,0 +1,4 @@
+ "Open source equals mutual assistance, and needs everyone's support. There are many ways to support it, such as using, recommending, writing tutorials, protecting the ecology, contributing code, answering questions, sharing experiences, donation, sponsorship and so on. Welcome to join us!",
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/index.php b/dafuweng-webman/app/admin/lang/en/index.php
new file mode 100644
index 0000000..e7fb229
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/index.php
@@ -0,0 +1,9 @@
+ 'No background menu, please contact the super administrator!',
+ 'You have already logged in. There is no need to log in again~' => 'You have already logged in, no need to log in again.',
+ 'Login succeeded!' => 'Login successful!',
+ 'Incorrect user name or password!' => 'Incorrect username or password!',
+ 'Login' => 'Login',
+ 'Logout' => 'Logout',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/routine/admininfo.php b/dafuweng-webman/app/admin/lang/en/routine/admininfo.php
new file mode 100644
index 0000000..924bf10
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/routine/admininfo.php
@@ -0,0 +1,6 @@
+ 'Please enter the correct username',
+ 'Please input correct password' => 'Please enter the correct password',
+ 'Avatar modified successfully!' => 'Profile picture modified successfully!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/routine/attachment.php b/dafuweng-webman/app/admin/lang/en/routine/attachment.php
new file mode 100644
index 0000000..6761720
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/routine/attachment.php
@@ -0,0 +1,5 @@
+ '%d records and files have been deleted',
+ 'remark_text' => 'When the same file is uploaded multiple times, only one copy will be saved to the disk and an attachment record will be added; Deleting an attachment record will automatically delete the corresponding file!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/routine/config.php b/dafuweng-webman/app/admin/lang/en/routine/config.php
new file mode 100644
index 0000000..6707b30
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/routine/config.php
@@ -0,0 +1,23 @@
+ 'Basic configuration',
+ 'Mail' => 'Mail configuration',
+ 'Config group' => 'Configure grouping',
+ 'Site Name' => 'Site name',
+ 'Config Quick entrance' => 'Quick configuration entrance',
+ 'Record number' => 'Record Number',
+ 'Version number' => 'Version Number',
+ 'time zone' => 'Time zone',
+ 'No access ip' => 'No access IP',
+ 'smtp server' => 'SMTP server',
+ 'smtp port' => 'SMTP port',
+ 'smtp user' => 'SMTP username',
+ 'smtp pass' => 'SMTP password',
+ 'smtp verification' => 'SMTP verification mode',
+ 'smtp sender mail' => 'SMTP sender mailbox',
+ 'Variable name' => 'variable name',
+ 'Test mail sent successfully~' => 'Test message sent successfully',
+ 'This is a test email' => 'This is a test email',
+ 'Congratulations, receiving this email means that your email service has been configured correctly' => "Congratulations, when you receive this email, it means that your mail service is configures correctly. This is the email subject, you can use HtmlL! in the main body.",
+ 'Backend entrance rule' => 'The background entry must start with / and contain only numbers and letters.',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/security/datarecycle.php b/dafuweng-webman/app/admin/lang/en/security/datarecycle.php
new file mode 100644
index 0000000..e1491ee
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/security/datarecycle.php
@@ -0,0 +1,7 @@
+ 'Rule Name',
+ 'Controller' => 'Controller',
+ 'Data Table' => 'Corresponding data table',
+ 'Primary Key' => 'Data table primary key',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/security/datarecyclelog.php b/dafuweng-webman/app/admin/lang/en/security/datarecyclelog.php
new file mode 100644
index 0000000..5baf49c
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/security/datarecyclelog.php
@@ -0,0 +1,4 @@
+ 'No records have been restored',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/security/sensitivedata.php b/dafuweng-webman/app/admin/lang/en/security/sensitivedata.php
new file mode 100644
index 0000000..4cb11da
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/security/sensitivedata.php
@@ -0,0 +1,8 @@
+ 'Rule name',
+ 'Controller' => 'Controller',
+ 'Data Table' => 'Corresponding data table',
+ 'Primary Key' => 'Data table primary key',
+ 'Data Fields' => 'Sensitive data fields',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/security/sensitivedatalog.php b/dafuweng-webman/app/admin/lang/en/security/sensitivedatalog.php
new file mode 100644
index 0000000..53f3e10
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/security/sensitivedatalog.php
@@ -0,0 +1,4 @@
+ 'No records have been roll-back',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/user/moneylog.php b/dafuweng-webman/app/admin/lang/en/user/moneylog.php
new file mode 100644
index 0000000..d7d23a9
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/user/moneylog.php
@@ -0,0 +1,8 @@
+ 'User',
+ 'money' => 'Change amount',
+ 'memo' => 'Change Notes',
+ "The user can't find it" => "User does not exist",
+ 'Change note cannot be blank' => 'Change Notes cannot be empty',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/en/user/scorelog.php b/dafuweng-webman/app/admin/lang/en/user/scorelog.php
new file mode 100644
index 0000000..aed6a51
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/en/user/scorelog.php
@@ -0,0 +1,8 @@
+ 'User',
+ 'score' => 'Change points',
+ 'memo' => 'Change Notes',
+ "The user can't find it" => 'User does not exist',
+ 'Change note cannot be blank' => 'Change notes cannot be empty',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn.php b/dafuweng-webman/app/admin/lang/zh-cn.php
new file mode 100644
index 0000000..1445196
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn.php
@@ -0,0 +1,117 @@
+ '请先登录!',
+ 'No background menu, please contact super administrator!' => '无后台菜单,请联系超级管理员!',
+ 'You have already logged in. There is no need to log in again~' => '您已经登录过了,无需重复登录~',
+ 'Login succeeded!' => '登录成功!',
+ 'Incorrect user name or password!' => '用户名或密码不正确!',
+ 'Login' => '登录',
+ 'Logout' => '注销登录',
+ 'Please input correct password' => '请输入正确的密码',
+ 'You have no permission' => '没有权限操作!',
+ 'Username' => '用户名',
+ 'Password' => '密码',
+ 'Nickname' => '昵称',
+ 'Email' => '邮箱',
+ 'Mobile' => '手机号',
+ 'Captcha' => '验证码',
+ 'CaptchaId' => '验证码ID',
+ 'Please enter the correct verification code' => '请输入正确的验证码!',
+ 'Captcha error' => '验证码错误!',
+ 'Parameter %s can not be empty' => '参数%s不能为空',
+ 'Record not found' => '记录未找到',
+ 'No rows were added' => '未添加任何行',
+ 'No rows were deleted' => '未删除任何行',
+ 'No rows updated' => '未更新任何行',
+ 'Update successful' => '更新成功!',
+ 'Added successfully' => '添加成功!',
+ 'Deleted successfully' => '删除成功!',
+ 'Parameter error' => '参数错误!',
+ 'Please use the %s field to sort before operating' => '请使用 %s 字段排序后再操作',
+ 'File uploaded successfully' => '文件上传成功!',
+ 'No files were uploaded' => '没有文件被上传',
+ 'The uploaded file format is not allowed' => '上传的文件格式未被允许',
+ 'The uploaded image file is not a valid image' => '上传的图片文件不是有效的图像',
+ 'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => '上传的文件太大(%sM),最大文件大小:%sM',
+ 'No files have been uploaded or the file size exceeds the upload limit of the server' => '没有文件被上传或文件大小超出服务器上传限制!',
+ 'Topic format error' => '上传存储子目录格式错误!',
+ 'Driver %s not supported' => '不支持的驱动:%s',
+ 'Unknown' => '未知',
+ // 权限类语言包-s
+ 'Super administrator' => '超级管理员',
+ 'No permission' => '无权限',
+ '%first% etc. %count% items' => '%first% 等 %count% 项',
+ 'Please select rules' => '请选择权限',
+ 'You cannot modify your own management group!' => '不能修改自己所在的管理组!',
+ 'You need to have all permissions of this group to operate this group~' => '您需要拥有该分组的所有权限才可以操作该分组~',
+ 'You need to have all the permissions of the group and have additional permissions before you can operate the group~' => '您需要拥有该分组的所有权限且还有额外权限时,才可以操作该分组~',
+ 'Role group has all your rights, please contact the upper administrator to add or do not need to add!' => '角色组拥有您的全部权限,请联系上级管理员添加或无需添加!',
+ 'The group permission node exceeds the range that can be allocated' => '分组权限节点超出可分配范围,请刷新重试~',
+ 'Account not exist' => '帐户不存在',
+ 'Account disabled' => '帐户已禁用',
+ 'Token login failed' => '令牌登录失败',
+ 'Username is incorrect' => '用户名不正确',
+ 'Please try again after 1 day' => '登录失败次数超限,请在1天后再试',
+ 'Password is incorrect' => '密码不正确',
+ 'You are not logged in' => '你没有登录',
+ // 权限类语言包-e
+ // 时间格式化-s
+ '%d second%s ago' => '%d秒前',
+ '%d minute%s ago' => '%d分钟前',
+ '%d hour%s ago' => '%d小时前',
+ '%d day%s ago' => '%d天前',
+ '%d week%s ago' => '%d周前',
+ '%d month%s ago' => '%d月前',
+ '%d year%s ago' => '%d年前',
+ '%d second%s after' => '%d秒后',
+ '%d minute%s after' => '%d分钟后',
+ '%d hour%s after' => '%d小时后',
+ '%d day%s after' => '%d天后',
+ '%d week%s after' => '%d周后',
+ '%d month%s after' => '%d月后',
+ '%d year%s after' => '%d年后',
+ // 时间格式化-e
+ 'Cache cleaned~' => '缓存已清理,请刷新后台~',
+ 'Please delete the child element first, or use batch deletion' => '请首先删除子元素,或使用批量删除!',
+ 'Configuration write failed: %s' => '配置写入失败:%s',
+ 'Token expiration' => '登录态过期,请重新登录!',
+ 'Method not allowed' => '方法不允许',
+ 'Variable name' => '变量名',
+ 'type' => '类型',
+ 'title' => '标题',
+ 'name' => '名称',
+ 'user_id' => '用户ID',
+ 'score' => '积分',
+ 'memo' => '备注',
+ 'money' => '金额',
+ 'Rollback successful' => '回滚成功!',
+ 'No rows were rolled back' => '未回滚任何行',
+ 'Avatar modified successfully!' => '头像修改成功!',
+ 'Failed to load cloud data' => '加载云端数据失败',
+ 'Log start' => '日志开始',
+ 'Parse field data' => '解析字段数据',
+ 'Generate check' => '生成校验',
+ 'Install module' => '安装模块',
+ 'Change module state' => '修改模块状态',
+ 'Uninstall module' => '卸载模块',
+ 'Upload module' => '上传模块',
+ 'upload' => '上传',
+ 'Data table does not exist' => '数据表不存在',
+ 'Change terminal config' => '修改终端配置',
+ 'Failed to modify the terminal configuration. Please modify the configuration file manually:%s' => '修改终端配置失败,请手动修改配置文件:%s',
+ 'Clear cache' => '清除缓存',
+ 'The current page configuration item was updated successfully' => '当前页配置项更新成功!',
+ 'This is a test email' => '这是一封测试邮件',
+ 'Congratulations, receiving this email means that your email service has been configured correctly' => '恭喜,收到此邮件说明您的邮件服务已配置正确',
+ 'Test mail sent successfully~' => '测试邮件发送成功~',
+ 'Name' => '名称',
+ 'Data Fields' => '数据字段',
+ 'Controller' => '控制器',
+ 'Data Table' => '数据表',
+ 'Primary Key' => '主键',
+ 'Restore successful' => '恢复成功!',
+ 'No rows were restore' => '未恢复任何行',
+ '%d records and files have been deleted' => '已删除%d条记录和文件',
+ 'Please input correct username' => '请输入正确的用户名',
+ 'Group Name Arr' => '分组名称数组',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/ajax.php b/dafuweng-webman/app/admin/lang/zh-cn/ajax.php
new file mode 100644
index 0000000..2994d4c
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/ajax.php
@@ -0,0 +1,12 @@
+ '开始进行数据库迁移',
+ 'Start formatting the web project code' => '开始格式化前端代码(失败无影响,代码编辑器内按需的手动格式化即可)',
+ 'Start installing the composer dependencies' => '开始安装服务端依赖',
+ 'Start executing the build command of the web project' => '开始执行 web 工程的 build 命令,成功后会自动将构建产物移动至 根目录/public 目录下',
+ 'Failed to modify the terminal configuration. Please modify the configuration file manually:%s' => '修改终端配置失败,请手动修改配置文件:%s',
+ 'upload' => '上传文件',
+ 'Change terminal config' => '修改终端配置',
+ 'Clear cache' => '清理缓存',
+ 'Data table does not exist' => '数据表不存在~',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/auth/admin.php b/dafuweng-webman/app/admin/lang/zh-cn/auth/admin.php
new file mode 100644
index 0000000..efa0622
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/auth/admin.php
@@ -0,0 +1,6 @@
+ '管理员分组',
+ 'Please use another administrator account to disable the current account!' => '请使用另外的管理员账户禁用当前账户!',
+ 'You have no permission to add an administrator to this group!' => '您没有权限向此分组添加管理员!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/auth/group.php b/dafuweng-webman/app/admin/lang/zh-cn/auth/group.php
new file mode 100644
index 0000000..1b5db16
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/auth/group.php
@@ -0,0 +1,13 @@
+ '组别名称',
+ 'Please select rules' => '请选择权限',
+ 'Super administrator' => '超级管理员',
+ 'No permission' => '无权限',
+ 'You cannot modify your own management group!' => '不能修改自己所在的管理组!',
+ 'You need to have all permissions of this group to operate this group~' => '您需要拥有该分组的所有权限才可以操作该分组~',
+ 'You need to have all the permissions of the group and have additional permissions before you can operate the group~' => '您需要拥有该分组的所有权限且还有额外权限时,才可以操作该分组~',
+ 'Role group has all your rights, please contact the upper administrator to add or do not need to add!' => '角色组拥有您的全部权限,请联系上级管理员添加或无需添加!',
+ 'The group permission node exceeds the range that can be allocated' => '分组权限节点超出可分配范围,请刷新重试~',
+ 'Remark lang' => '为保障系统安全,角色组本身的上下级关系仅供参考,系统的实际上下级划分是根据`权限多寡`来确定的,两位管理员的权限节点:相同被认为是`同级`、包含且有额外权限才被认为是`上级`,同级不可管理同级,上级可为下级分配自己拥有的权限节点;若有特殊情况管理员需转`上级`,可建立一个虚拟权限节点',
+];
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/auth/rule.php b/dafuweng-webman/app/admin/lang/zh-cn/auth/rule.php
new file mode 100644
index 0000000..4c08979
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/auth/rule.php
@@ -0,0 +1,6 @@
+ '规则类型',
+ 'title' => '规则标题',
+ 'name' => '规则名称',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/crud/crud.php b/dafuweng-webman/app/admin/lang/zh-cn/crud/crud.php
new file mode 100644
index 0000000..36ca330
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/crud/crud.php
@@ -0,0 +1,11 @@
+ 'CRUD代码生成-解析字段数据',
+ 'Log start' => 'CRUD代码生成-从历史记录开始',
+ 'Generate check' => 'CRUD代码生成-生成前预检',
+ 'change-field-name fail not exist' => '字段 %s 改名失败,数据表内不存在该字段',
+ 'del-field fail not exist' => '字段 %s 删除失败,数据表内不存在该字段',
+ 'change-field-attr fail not exist' => '修改字段 %s 的属性失败,数据表内不存在该字段',
+ 'add-field fail exist' => '添加字段 %s 失败,数据表内已经存在该字段',
+ 'Failed to load cloud data' => '加载云端数据失败,请稍后重试!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/dashboard.php b/dafuweng-webman/app/admin/lang/zh-cn/dashboard.php
new file mode 100644
index 0000000..e4eaff2
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/dashboard.php
@@ -0,0 +1,4 @@
+ '开源等于互助;开源需要大家一起来支持,支持的方式有很多种,比如使用、推荐、写教程、保护生态、贡献代码、回答问题、分享经验、打赏赞助等;欢迎您加入我们!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/index.php b/dafuweng-webman/app/admin/lang/zh-cn/index.php
new file mode 100644
index 0000000..b896075
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/index.php
@@ -0,0 +1,9 @@
+ '无后台菜单,请联系超级管理员!',
+ 'You have already logged in. There is no need to log in again~' => '您已经登录过了,无需重复登录~',
+ 'Login succeeded!' => '登录成功!',
+ 'Incorrect user name or password!' => '用户名或密码不正确!',
+ 'Login' => '登录',
+ 'Logout' => '注销登录',
+];
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/module.php b/dafuweng-webman/app/admin/lang/zh-cn/module.php
new file mode 100644
index 0000000..d63d099
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/module.php
@@ -0,0 +1,29 @@
+ '订单找不到啦!',
+ 'Module already exists' => '模块已存在!',
+ 'package download failed' => '包下载失败!',
+ 'package check failed' => '包检查失败!',
+ 'No permission to write temporary files' => '没有权限写入临时文件!',
+ 'Zip file not found' => '找不到压缩包文件',
+ 'Unable to open the zip file' => '无法打开压缩包文件',
+ 'Unable to extract ZIP file' => '无法提取ZIP文件',
+ 'Unable to package zip file' => '无法打包zip文件',
+ 'Basic configuration of the Module is incomplete' => '模块基础配置不完整',
+ 'Module package file does not exist' => '模块包文件不存在',
+ 'Module file conflicts' => '模块文件存在冲突,请手动处理!',
+ 'Configuration file has no write permission' => '配置文件无写入权限',
+ 'The current state of the module cannot be set to disabled' => '模块当前状态无法设定为禁用',
+ 'The current state of the module cannot be set to enabled' => '模块当前状态无法设定为启用',
+ 'Module file updated' => '模块文件有更新',
+ 'Please disable the module first' => '请先禁用模块',
+ 'Please disable the module before updating' => '更新前请先禁用模块',
+ 'The directory required by the module is occupied' => '模块所需目录已被占用',
+ 'Install module' => '安装模块',
+ 'Unload module' => '卸载模块',
+ 'Update module' => '更新模块',
+ 'Change module state' => '改变模块状态',
+ 'Upload install module' => '上传安装模块',
+ 'Please login to the official website account first' => '请先使用BuildAdmin官网账户登录到模块市场~',
+ 'composer config %s conflict' => 'composer 配置项 %s 存在冲突',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/routine/admininfo.php b/dafuweng-webman/app/admin/lang/zh-cn/routine/admininfo.php
new file mode 100644
index 0000000..1f2612c
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/routine/admininfo.php
@@ -0,0 +1,6 @@
+ '请输入正确的用户名',
+ 'Please input correct password' => '请输入正确的密码',
+ 'Avatar modified successfully!' => '头像修改成功!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/routine/attachment.php b/dafuweng-webman/app/admin/lang/zh-cn/routine/attachment.php
new file mode 100644
index 0000000..22ee5a0
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/routine/attachment.php
@@ -0,0 +1,5 @@
+ '同一文件被多次上传时,只会保存一份至磁盘和增加一条附件记录;删除附件记录,将自动删除对应文件!',
+ '%d records and files have been deleted' => '删除了%d条记录和文件',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/routine/config.php b/dafuweng-webman/app/admin/lang/zh-cn/routine/config.php
new file mode 100644
index 0000000..22657d0
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/routine/config.php
@@ -0,0 +1,25 @@
+ '基础配置',
+ 'Mail' => '邮件配置',
+ 'Config group' => '配置分组',
+ 'Site Name' => '站点名称',
+ 'Backend entrance' => '自定义后台入口',
+ 'Config Quick entrance' => '快捷配置入口',
+ 'Record number' => '备案号',
+ 'Version number' => '版本号',
+ 'time zone' => '时区',
+ 'No access ip' => '禁止访问IP',
+ 'smtp server' => 'SMTP 服务器',
+ 'smtp port' => 'SMTP 端口',
+ 'smtp user' => 'SMTP 用户名',
+ 'smtp pass' => 'SMTP 密码',
+ 'smtp verification' => 'SMTP 验证方式',
+ 'smtp sender mail' => 'SMTP 发件人邮箱',
+ 'Variable name' => '变量名',
+ 'Test mail sent successfully~' => '测试邮件发送成功~',
+ 'This is a test email' => '这是一封测试邮件',
+ 'Congratulations, receiving this email means that your email service has been configured correctly' => '恭喜您,收到此邮件代表您的邮件服务已配置正确;这是邮件主体 在主体中可以使用Html!',
+ 'The current page configuration item was updated successfully' => '当前页配置项更新成功!',
+ 'Backend entrance rule' => '后台入口请以 / 开头,且只包含数字和字母。',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/security/datarecycle.php b/dafuweng-webman/app/admin/lang/zh-cn/security/datarecycle.php
new file mode 100644
index 0000000..7b21cfb
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/security/datarecycle.php
@@ -0,0 +1,8 @@
+ '规则名称',
+ 'Controller' => '控制器',
+ 'Data Table' => '对应数据表',
+ 'Primary Key' => '数据表主键',
+ 'Remark lang' => '在此定义需要回收的数据,实现数据自动统一回收',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/security/datarecyclelog.php b/dafuweng-webman/app/admin/lang/zh-cn/security/datarecyclelog.php
new file mode 100644
index 0000000..4b8d0b2
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/security/datarecyclelog.php
@@ -0,0 +1,4 @@
+ '没有记录被还原',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/security/sensitivedata.php b/dafuweng-webman/app/admin/lang/zh-cn/security/sensitivedata.php
new file mode 100644
index 0000000..9367d1c
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/security/sensitivedata.php
@@ -0,0 +1,9 @@
+ '规则名称',
+ 'Controller' => '控制器',
+ 'Data Table' => '对应数据表',
+ 'Primary Key' => '数据表主键',
+ 'Data Fields' => '敏感数据字段',
+ 'Remark lang' => '在此定义需要保护的敏感字段,随后系统将自动监听该字段的修改操作,并提供了敏感字段的修改回滚功能',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/security/sensitivedatalog.php b/dafuweng-webman/app/admin/lang/zh-cn/security/sensitivedatalog.php
new file mode 100644
index 0000000..7e62651
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/security/sensitivedatalog.php
@@ -0,0 +1,4 @@
+ '没有记录被回滚',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/user/moneylog.php b/dafuweng-webman/app/admin/lang/zh-cn/user/moneylog.php
new file mode 100644
index 0000000..da8d81d
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/user/moneylog.php
@@ -0,0 +1,8 @@
+ '用户',
+ 'money' => '变更金额',
+ 'memo' => '变更备注',
+ "The user can't find it" => '用户找不到啦',
+ 'Change note cannot be blank' => '变更备注不能为空',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/lang/zh-cn/user/scorelog.php b/dafuweng-webman/app/admin/lang/zh-cn/user/scorelog.php
new file mode 100644
index 0000000..7822169
--- /dev/null
+++ b/dafuweng-webman/app/admin/lang/zh-cn/user/scorelog.php
@@ -0,0 +1,8 @@
+ '用户',
+ 'score' => '变更积分',
+ 'memo' => '变更备注',
+ "The user can't find it" => '用户找不到啦',
+ 'Change note cannot be blank' => '变更备注不能为空',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/admin/library/Auth.php b/dafuweng-webman/app/admin/library/Auth.php
index 701dd47..97c8e07 100644
--- a/dafuweng-webman/app/admin/library/Auth.php
+++ b/dafuweng-webman/app/admin/library/Auth.php
@@ -132,7 +132,9 @@ class Auth extends \ba\Auth
if ($keep) {
$this->setRefreshToken($this->refreshTokenKeepTime);
}
- $this->loginSuccessful();
+ if (!$this->loginSuccessful()) {
+ return false;
+ }
return true;
}
diff --git a/dafuweng-webman/app/admin/library/traits/Backend.php b/dafuweng-webman/app/admin/library/traits/Backend.php
index faa78e5..5f2fb28 100644
--- a/dafuweng-webman/app/admin/library/traits/Backend.php
+++ b/dafuweng-webman/app/admin/library/traits/Backend.php
@@ -16,6 +16,7 @@ trait Backend
{
/**
* 排除入库字段
+ * 时间戳字段(create_time/update_time)由模型自动维护,禁止前端传入非法值(如 'now')
*/
protected function excludeFields(array $params): array
{
@@ -23,8 +24,13 @@ trait Backend
$this->preExcludeFields = explode(',', (string) $this->preExcludeFields);
}
- foreach ($this->preExcludeFields as $field) {
- if (array_key_exists($field, $params)) {
+ $exclude = array_merge(
+ $this->preExcludeFields,
+ ['create_time', 'update_time', 'createtime', 'updatetime']
+ );
+ foreach ($exclude as $field) {
+ $field = trim($field);
+ if ($field !== '' && array_key_exists($field, $params)) {
unset($params[$field]);
}
}
@@ -37,7 +43,7 @@ trait Backend
protected function _index(): Response
{
if ($this->request && $this->request->get('select')) {
- $this->select();
+ return $this->select($this->request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
@@ -56,6 +62,25 @@ trait Backend
]);
}
+ /**
+ * 递归应用输入过滤(如 clean_xss)
+ */
+ protected function applyInputFilter(array $data): array
+ {
+ if (!$this->inputFilter || !function_exists($this->inputFilter)) {
+ return $data;
+ }
+ $filter = $this->inputFilter;
+ foreach ($data as $k => $v) {
+ if (is_string($v)) {
+ $data[$k] = call_user_func($filter, $v);
+ } elseif (is_array($v)) {
+ $data[$k] = $this->applyInputFilter($v);
+ }
+ }
+ return $data;
+ }
+
/**
* 添加(内部实现)
*/
@@ -67,6 +92,7 @@ trait Backend
return $this->error(__('Parameter %s can not be empty', ['']));
}
+ $data = $this->applyInputFilter($data);
$data = $this->excludeFields($data);
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
$data[$this->dataLimitField] = $this->auth->id;
@@ -123,6 +149,7 @@ trait Backend
return $this->error(__('Parameter %s can not be empty', ['']));
}
+ $data = $this->applyInputFilter($data);
$data = $this->excludeFields($data);
$result = false;
$this->model->startTrans();
@@ -272,9 +299,9 @@ trait Backend
}
/**
- * 加载为 select(远程下拉选择框)数据
+ * 加载为 select(远程下拉选择框)数据,子类可覆盖
*/
- public function select(): void
+ protected function _select(): void
{
}
}
diff --git a/dafuweng-webman/app/admin/model/AdminLog.php b/dafuweng-webman/app/admin/model/AdminLog.php
index b60ea17..74ed0d6 100644
--- a/dafuweng-webman/app/admin/model/AdminLog.php
+++ b/dafuweng-webman/app/admin/model/AdminLog.php
@@ -20,7 +20,8 @@ class AdminLog extends Model
protected bool $updateTime = false;
protected string $title = '';
- protected string|array $data = '';
+ /** 日志内容(勿用 $data,会与 ThinkORM Model 的 data 选项冲突) */
+ protected string|array $logData = '';
protected array $urlIgnoreRegex = [
'/^(.*)\/(select|index|logout)$/i',
];
@@ -49,7 +50,7 @@ class AdminLog extends Model
/** 设置日志内容(BuildAdmin 控制器调用) */
public function setLogData(string|array $data): void
{
- $this->data = $data;
+ $this->logData = $data;
}
public function setUrlIgnoreRegex(array|string $regex = []): void
@@ -101,7 +102,7 @@ class AdminLog extends Model
}
}
- $data = $data ?: $this->data;
+ $data = $data ?: $this->logData;
if (!$data) {
$data = array_merge($request->get(), $request->post());
}
diff --git a/dafuweng-webman/app/api/controller/Account.php b/dafuweng-webman/app/api/controller/Account.php
index e44970c..0c16fbb 100644
--- a/dafuweng-webman/app/api/controller/Account.php
+++ b/dafuweng-webman/app/api/controller/Account.php
@@ -62,7 +62,7 @@ class Account extends Frontend
try {
Validator::make($data, [
- 'username' => 'required|string|regex:/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/|unique:user,username,' . $this->auth->id,
+ 'username' => 'required|string|regex:/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/',
'nickname' => 'required|string|regex:/^[\x{4e00}-\x{9fa5}a-zA-Z0-9_-]+$/u',
'birthday' => 'nullable|date',
], [
@@ -72,6 +72,11 @@ class Account extends Frontend
return $this->error($e->getMessage());
}
+ $existUser = User::where('username', $data['username'])->where('id', '<>', $this->auth->id)->find();
+ if ($existUser) {
+ return $this->error(__('Username') . ' ' . __('already exists'));
+ }
+
$model->startTrans();
try {
$model->save($data);
@@ -126,11 +131,18 @@ class Account extends Frontend
}
if ($captcha->check($params['captcha'], $params[$params['type']] . "user_change_{$params['type']}")) {
- $rules = $params['type'] == 'email'
- ? ['email' => 'required|email|unique:user,email']
- : ['mobile' => 'required|regex:/^1[3-9]\d{9}$/|unique:user,mobile'];
try {
- Validator::make($params, $rules)->validate();
+ if ($params['type'] == 'email') {
+ Validator::make($params, ['email' => 'required|email'])->validate();
+ if (User::where('email', $params['email'])->find()) {
+ return $this->error(__('Email') . ' ' . __('already exists'));
+ }
+ } else {
+ Validator::make($params, ['mobile' => 'required|regex:/^1[3-9]\d{9}$/'])->validate();
+ if (User::where('mobile', $params['mobile'])->find()) {
+ return $this->error(__('Mobile') . ' ' . __('already exists'));
+ }
+ }
} catch (ValidationException $e) {
return $this->error(__($e->getMessage()));
}
diff --git a/dafuweng-webman/app/api/lang/en.php b/dafuweng-webman/app/api/lang/en.php
new file mode 100644
index 0000000..42619df
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/en.php
@@ -0,0 +1,21 @@
+ 'Login expired, please login again.',
+ 'Account not exist' => 'Account does not exist',
+ 'Account disabled' => 'Account is disabled',
+ 'Token login failed' => 'Token login failed',
+ 'Please try again after 1 day' => 'The number of failed login attempts has exceeded the limit, please try again after 24 hours.',
+ 'Password is incorrect' => 'Incorrect password',
+ 'You are not logged in' => 'You are not logged in.',
+ 'Unknown operation' => 'Unknown operation',
+ 'No action available, please contact the administrator~' => 'There is no action available, please contact the administrator~',
+ 'Please login first' => 'Please login first!',
+ 'You have no permission' => 'No permission to operate!',
+ 'Captcha error' => 'Captcha error!',
+ // Member center account
+ 'Data updated successfully~' => 'Data updated successfully~',
+ 'Password has been changed~' => 'Password has been changed~',
+ 'Password has been changed, please login again~' => 'Password has been changed, please login again~',
+ 'already exists' => 'already exists',
+ 'nicknameChsDash' => 'Usernames can only be Chinese characters, letters, numbers, underscores_ and dashes-.',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/en/account.php b/dafuweng-webman/app/api/lang/en/account.php
new file mode 100644
index 0000000..3968db2
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/en/account.php
@@ -0,0 +1,16 @@
+ 'Nickname',
+ 'birthday' => 'Birthday',
+ 'captcha' => 'Captcha',
+ 'Old password error' => 'Old password error',
+ 'Data updated successfully~' => 'Data updated successfully',
+ 'Please input correct password' => 'Please enter the correct password',
+ 'nicknameChsDash' => 'Usernames can only be Chinese characters, letters, numbers, underscores_ and dashes-.',
+ 'Password has been changed~' => 'Password has been changed~',
+ 'Password has been changed, please login again~' => 'Password has been changed, please login again~',
+ 'Account does not exist~' => 'Account does not exist',
+ 'Failed to modify password, please try again later~' => 'Failed to modify password, please try again later~',
+ 'Please enter the correct verification code' => 'Please enter the correct Captcha',
+ '%s has been registered' => '%s has been registered, please login directly.',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/en/ems.php b/dafuweng-webman/app/api/lang/en/ems.php
new file mode 100644
index 0000000..46c7fb3
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/en/ems.php
@@ -0,0 +1,16 @@
+ 'email format error',
+ 'user_register' => 'Member registration verification',
+ 'user_retrieve_pwd' => 'Retrieve password verification',
+ 'user_change_email' => 'Modify mailbox validation',
+ 'user_email_verify' => 'Member Email Verification',
+ 'Your verification code is: %s' => 'Your Captcha is: %s,valid for 10 minutes~',
+ 'Mail sent successfully~' => 'Mail sent successfully',
+ 'Account does not exist~' => 'Account does not exist',
+ 'Mail sending service unavailable' => 'The mail sending service is not working, please contact the webmaster to configure it.',
+ 'Frequent email sending' => 'Frequent email sending',
+ 'Email has been registered, please log in directly' => 'Email has been registered, please log in directly~',
+ 'The email has been occupied' => 'The email has been occupied',
+ 'Email not registered' => 'Email not registered',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/en/install.php b/dafuweng-webman/app/api/lang/en/install.php
new file mode 100644
index 0000000..0175147
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/en/install.php
@@ -0,0 +1,44 @@
+ 'Install the controller',
+ 'need' => 'Need',
+ 'Click to see how to solve it' => 'Click to see how to solve.',
+ 'Please check the config directory permissions' => 'Please check the Config directory permissions',
+ 'Please check the public directory permissions' => 'Please check the Public directory permissions',
+ 'open' => 'Open',
+ 'close' => 'Close',
+ 'The installation can continue, and some operations need to be completed manually' => 'You can continue to install, and some operations need to be completed manually ',
+ 'Allow execution' => 'Allow execution',
+ 'disabled' => 'Disabled',
+ 'Allow operation' => 'Allow operation',
+ 'Acquisition failed' => 'Access failed',
+ 'Click Install %s' => 'Click Install %s',
+ 'Writable' => 'Writable',
+ 'No write permission' => 'No write permissions',
+ 'already installed' => 'Installed',
+ 'Not installed' => 'Not installed',
+ 'File has no write permission:%s' => 'File has no write permission:%s',
+ 'The system has completed installation. If you need to reinstall, please delete the %s file first' => 'The system has been installed, if you need to reinstall, please delete the %s file first.',
+ 'Database connection failed:%s' => 'Database connection failure:%s',
+ 'Failed to install SQL execution:%s' => 'Installation SQL execution failed:%s',
+ 'unknown' => 'Unknown',
+ 'Database does not exist' => 'Database does not exist!',
+ 'No built front-end file found, please rebuild manually!' => 'No built front-end file found, please rebuild manually.',
+ 'Failed to move the front-end file, please move it manually!' => 'Failed to move the front-end file, please move manually!',
+ 'How to solve?' => 'How to solve?',
+ 'View reason' => 'View reasons',
+ 'Click to view the reason' => 'Click to see the reason',
+ 'PDO extensions need to be installed' => 'pdo_mysql extensions need to be installed.',
+ 'proc_open or proc_close functions in PHP Ini is disabled' => 'proc_open and proc_close functions in PHP.Ini is disabled.',
+ 'How to modify' => 'How to modify?',
+ 'Click to view how to modify' => 'Click to see how to modify.',
+ 'Security assurance?' => 'Security assurance?',
+ 'Using the installation service correctly will not cause any potential security problems. Click to view the details' => 'The correct use of the installation service will not cause any potential security issues. Click to view the details.',
+ 'Please install NPM first' => 'Please install NPM first.',
+ 'Installation error:%s' => 'Installation error:%s',
+ 'Failed to switch package manager. Please modify the configuration file manually:%s' => 'Package manager switch failed, please modify the configuration file manually:%s.',
+ 'Please upgrade %s version' => 'Please upgrade the %s version',
+ 'nothing' => 'Nothing',
+ 'The gd extension and freeType library need to be installed' => 'The gd2 extension and freeType library need to be installed',
+ 'The .env file with database configuration was detected. Please clean up and try again!' => 'The .env file with database configuration was detected. Please clean up and try again!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/en/user.php b/dafuweng-webman/app/api/lang/en/user.php
new file mode 100644
index 0000000..d857baf
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/en/user.php
@@ -0,0 +1,13 @@
+ 'Captcha',
+ 'captchaId' => 'Captcha ID',
+ 'Please input correct username' => 'Please enter the correct username.',
+ 'Please input correct password' => 'Please enter the correct password.',
+ 'Registration parameter error' => 'Wrong registration parameter',
+ 'Login succeeded!' => 'Login succeeded!',
+ 'Please enter the correct verification code' => 'Please enter the correct Captcha.',
+ 'You have already logged in. There is no need to log in again~' => 'You have already logged in, no need to log in again.',
+ 'Check in failed, please try again or contact the website administrator~' => 'Check in failed,please try again or contact the webmaster.',
+ 'Member center disabled' => 'The member centre has been disabled, please contact the webmaster to turn it on.',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/zh-cn.php b/dafuweng-webman/app/api/lang/zh-cn.php
new file mode 100644
index 0000000..0e01a89
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/zh-cn.php
@@ -0,0 +1,53 @@
+ '%d秒前',
+ '%d minute%s ago' => '%d分钟前',
+ '%d hour%s ago' => '%d小时前',
+ '%d day%s ago' => '%d天前',
+ '%d week%s ago' => '%d周前',
+ '%d month%s ago' => '%d月前',
+ '%d year%s ago' => '%d年前',
+ '%d second%s after' => '%d秒后',
+ '%d minute%s after' => '%d分钟后',
+ '%d hour%s after' => '%d小时后',
+ '%d day%s after' => '%d天后',
+ '%d week%s after' => '%d周后',
+ '%d month%s after' => '%d月后',
+ '%d year%s after' => '%d年后',
+ // 时间格式化-e
+ // 文件上传-s
+ 'File uploaded successfully' => '文件上传成功!',
+ 'No files were uploaded' => '没有文件被上传',
+ 'The uploaded file format is not allowed' => '上传的文件格式未被允许',
+ 'The uploaded image file is not a valid image' => '上传的图片文件不是有效的图像',
+ 'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => '上传的文件太大(%sM),最大文件大小:%sM',
+ 'No files have been uploaded or the file size exceeds the upload limit of the server' => '没有文件被上传或文件大小超出服务器上传限制!',
+ 'Topic format error' => '上传存储子目录格式错误!',
+ 'Driver %s not supported' => '不支持的驱动:%s',
+ // 文件上传-e
+ 'Username' => '用户名',
+ 'Email' => '邮箱',
+ 'Mobile' => '手机号',
+ 'Password' => '密码',
+ 'Login expired, please login again.' => '登录过期,请重新登录。',
+ 'Account not exist' => '帐户不存在',
+ 'Account disabled' => '帐户已禁用',
+ 'Token login failed' => '令牌登录失败',
+ 'Please try again after 1 day' => '登录失败次数超限,请在1天后再试',
+ 'Password is incorrect' => '密码不正确',
+ 'You are not logged in' => '你没有登录',
+ 'Unknown operation' => '未知操作',
+ 'No action available, please contact the administrator~' => '没有可用操作,请联系管理员~',
+ 'Please login first' => '请先登录!',
+ 'You have no permission' => '没有权限操作!',
+ 'Parameter error' => '参数错误!',
+ 'Token expiration' => '登录态过期,请重新登录!',
+ 'Captcha error' => '验证码错误!',
+ // 会员中心 account
+ 'Data updated successfully~' => '资料更新成功~',
+ 'Password has been changed~' => '密码已修改~',
+ 'Password has been changed, please login again~' => '密码已修改,请重新登录~',
+ 'already exists' => '已存在',
+ 'nicknameChsDash' => '用户名只能是汉字、字母、数字和下划线_及破折号-',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/zh-cn/account.php b/dafuweng-webman/app/api/lang/zh-cn/account.php
new file mode 100644
index 0000000..4f8be69
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/zh-cn/account.php
@@ -0,0 +1,22 @@
+ '昵称',
+ 'birthday' => '生日',
+ 'captcha' => '验证码',
+ 'Old password error' => '旧密码错误',
+ 'Data updated successfully~' => '资料更新成功~',
+ 'Please input correct password' => '请输入正确的密码',
+ 'nicknameChsDash' => '用户名只能是汉字、字母、数字和下划线_及破折号-',
+ 'Password has been changed~' => '密码已修改~',
+ 'Password has been changed, please login again~' => '密码已修改,请重新登录~',
+ 'Account does not exist~' => '账户不存在~',
+ 'Failed to modify password, please try again later~' => '修改密码失败,请稍后重试~',
+ 'Please enter the correct verification code' => '请输入正确的验证码!',
+ '%s has been registered' => '%s已被注册,请直接登录~',
+ 'email format error' => '电子邮箱格式错误!',
+ 'mobile format error' => '手机号格式错误!',
+ 'You need to verify your account before modifying the binding information' => '您需要先通过账户验证才能修改绑定信息!',
+ 'Password error' => '密码错误!',
+ 'email is occupied' => '电子邮箱地址已被占用!',
+ 'mobile is occupied' => '手机号已被占用!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/zh-cn/ems.php b/dafuweng-webman/app/api/lang/zh-cn/ems.php
new file mode 100644
index 0000000..e75a328
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/zh-cn/ems.php
@@ -0,0 +1,18 @@
+ '电子邮箱格式错误',
+ 'user_register' => '会员注册验证',
+ 'user_change_email' => '修改邮箱验证',
+ 'user_retrieve_pwd' => '找回密码验证',
+ 'user_email_verify' => '会员身份验证',
+ 'Your verification code is: %s' => '您的验证码是:%s,十分钟内有效~',
+ 'Mail sent successfully~' => '邮件发送成功~',
+ 'Account does not exist~' => '账户不存在~',
+ 'Mail sending service unavailable' => '邮件发送服务不可用,请联系网站管理员进行配置~',
+ 'Frequent email sending' => '频繁发送电子邮件',
+ 'Email has been registered, please log in directly' => '电子邮箱已注册,请直接登录~',
+ 'The email has been occupied' => '电子邮箱已被占用!',
+ 'Email not registered' => '电子邮箱未注册',
+ 'Please use the account registration email to send the verification code' => '请使用账户注册邮箱发送验证码!',
+ 'Password error' => '密码错误!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/zh-cn/install.php b/dafuweng-webman/app/api/lang/zh-cn/install.php
new file mode 100644
index 0000000..18117ea
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/zh-cn/install.php
@@ -0,0 +1,44 @@
+ '安装控制器',
+ 'need' => '需要',
+ 'Click to see how to solve it' => '点击查看如何解决',
+ 'Please check the config directory permissions' => '请检查 config 目录权限',
+ 'Please check the public directory permissions' => '请检查 public 目录权限',
+ 'open' => '开启',
+ 'close' => '关闭',
+ 'The installation can continue, and some operations need to be completed manually' => '可以继续安装,部分操作需手动完成',
+ 'Allow execution' => '允许执行',
+ 'disabled' => '已禁用',
+ 'Allow operation' => '允许操作',
+ 'Acquisition failed' => '获取失败',
+ 'Click Install %s' => '点击安装%s',
+ 'Writable' => '可写',
+ 'No write permission' => '无写权限',
+ 'already installed' => '已安装',
+ 'Not installed' => '未安装',
+ 'File has no write permission:%s' => '文件无写入权限:%s',
+ 'The system has completed installation. If you need to reinstall, please delete the %s file first' => '系统已完成安装。如果需要重新安装,请先删除 %s 文件',
+ 'Database connection failed:%s' => '数据库连接失败:%s',
+ 'Failed to install SQL execution:%s' => '安装SQL执行失败:%s',
+ 'unknown' => '未知',
+ 'Database does not exist' => '数据库不存在!',
+ 'No built front-end file found, please rebuild manually!' => '没有找到构建好的前端文件,请手动重新构建!',
+ 'Failed to move the front-end file, please move it manually!' => '移动前端文件失败,请手动移动!',
+ 'How to solve?' => '如何解决?',
+ 'View reason' => '查看原因',
+ 'Click to view the reason' => '点击查看原因',
+ 'PDO extensions need to be installed' => '需要安装 pdo_mysql 扩展',
+ 'proc_open or proc_close functions in PHP Ini is disabled' => 'proc_open和proc_close函数在php.ini中被禁用掉了',
+ 'How to modify' => '如何修改',
+ 'Click to view how to modify' => '点击查看如何修改',
+ 'Security assurance?' => '安全保证?',
+ 'Using the installation service correctly will not cause any potential security problems. Click to view the details' => '安装服务使用正确不会造成任何潜在安全问题,点击查看详情',
+ 'Please install NPM first' => '请先安装npm',
+ 'Installation error:%s' => '安装出错:%s',
+ 'Failed to switch package manager. Please modify the configuration file manually:%s' => '包管理器切换失败,请手动修改配置文件:%s',
+ 'Please upgrade %s version' => '请升级%s版本',
+ 'nothing' => '无',
+ 'The gd extension and freeType library need to be installed' => '需要gd2扩展和freeType库',
+ 'The .env file with database configuration was detected. Please clean up and try again!' => '检测到带有数据库配置的 .env 文件。请清理后再试一次!',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/api/lang/zh-cn/user.php b/dafuweng-webman/app/api/lang/zh-cn/user.php
new file mode 100644
index 0000000..3bb8591
--- /dev/null
+++ b/dafuweng-webman/app/api/lang/zh-cn/user.php
@@ -0,0 +1,14 @@
+ '验证码',
+ 'captchaId' => '验证码标识',
+ 'Register type' => '注册类型',
+ 'Please input correct username' => '请输入正确的用户名',
+ 'Please input correct password' => '请输入正确的密码',
+ 'Registration parameter error' => '注册参数错误',
+ 'Login succeeded!' => '登录成功',
+ 'Please enter the correct verification code' => '请输入正确的验证码',
+ 'You have already logged in. There is no need to log in again~' => '您已经登录过了,无需重复登录~',
+ 'Check in failed, please try again or contact the website administrator~' => '签入失败,请重试或联系网站管理员~',
+ 'Member center disabled' => '会员中心已禁用,请联系网站管理员开启。',
+];
\ No newline at end of file
diff --git a/dafuweng-webman/app/common/controller/Backend.php b/dafuweng-webman/app/common/controller/Backend.php
index e55acbd..6e7c365 100644
--- a/dafuweng-webman/app/common/controller/Backend.php
+++ b/dafuweng-webman/app/common/controller/Backend.php
@@ -105,6 +105,11 @@ class Backend extends Api
*/
protected string $withJoinType = 'LEFT';
+ /**
+ * 输入过滤函数名(如 clean_xss,CRUD 含 editor 字段时自动设置)
+ */
+ protected string $inputFilter = '';
+
/**
* 后台初始化(需在控制器方法开头调用,在 initializeApi 之后)
* @return Response|null 需直接返回时返回 Response,否则 null
@@ -116,6 +121,9 @@ class Backend extends Api
return $response;
}
+ // 调用子类 initialize(CRUD 生成的控制器在此设置 model)
+ $this->initialize();
+
$action = $this->getActionFromPath($request->path());
$needLogin = !action_in_arr($this->noNeedLogin, $action);
$needPermission = !action_in_arr($this->noNeedPermission, $action);
@@ -207,7 +215,7 @@ class Backend extends Api
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
- $this->select();
+ $this->_select();
return $this->success();
}
diff --git a/dafuweng-webman/app/common/controller/Frontend.php b/dafuweng-webman/app/common/controller/Frontend.php
index c0f3376..e29ae8f 100644
--- a/dafuweng-webman/app/common/controller/Frontend.php
+++ b/dafuweng-webman/app/common/controller/Frontend.php
@@ -35,7 +35,7 @@ class Frontend extends Api
$needLogin = !action_in_arr($this->noNeedLogin, $action);
try {
- $this->auth = Auth::instance();
+ $this->auth = Auth::instance(['request' => $request]);
$token = get_auth_token(['ba', 'user', 'token'], $request);
if ($token) $this->auth->init($token);
} catch (TokenExpirationException) {
diff --git a/dafuweng-webman/app/common/library/Auth.php b/dafuweng-webman/app/common/library/Auth.php
index 53d6349..3174717 100644
--- a/dafuweng-webman/app/common/library/Auth.php
+++ b/dafuweng-webman/app/common/library/Auth.php
@@ -47,7 +47,8 @@ class Auth extends \ba\Auth
public static function instance(array $options = []): Auth
{
- $request = function_exists('request') ? request() : null;
+ $request = $options['request'] ?? (function_exists('request') ? request() : null);
+ unset($options['request']);
if ($request && !isset($request->userAuth)) {
$request->userAuth = new static($options);
}
@@ -158,7 +159,8 @@ class Auth extends \ba\Auth
$userLoginRetry = config('buildadmin.user_login_retry');
if ($userLoginRetry && $this->model->last_login_time) {
- if ($this->model->login_failure > 0 && time() - strtotime($this->model->last_login_time) >= 86400) {
+ $lastLoginTs = is_numeric($this->model->last_login_time) ? (int)$this->model->last_login_time : strtotime($this->model->last_login_time);
+ if ($this->model->login_failure > 0 && $lastLoginTs > 0 && time() - $lastLoginTs >= 86400) {
$this->model->login_failure = 0;
$this->model->save();
$this->model = User::where($accountType, $username)->find();
@@ -181,8 +183,7 @@ class Auth extends \ba\Auth
}
if ($keep) $this->setRefreshToken($this->refreshTokenKeepTime);
- $this->loginSuccessful();
- return true;
+ return $this->loginSuccessful();
}
public function direct(int $userId): bool
@@ -201,21 +202,24 @@ class Auth extends \ba\Auth
if (!$this->model) return false;
$request = function_exists('request') ? request() : null;
$ip = $request ? $request->getRealIp() : '0.0.0.0';
+ if (!$this->token) {
+ $this->token = Random::uuid();
+ Token::set($this->token, self::TOKEN_TYPE, $this->model->id, $this->keepTime);
+ }
$this->model->startTrans();
try {
$this->model->login_failure = 0;
- $this->model->last_login_time = date('Y-m-d H:i:s');
+ $this->model->last_login_time = time();
$this->model->last_login_ip = $ip;
$this->model->save();
$this->loginEd = true;
-
- if (!$this->token) {
- $this->token = Random::uuid();
- Token::set($this->token, self::TOKEN_TYPE, $this->model->id, $this->keepTime);
- }
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
+ if ($this->token) {
+ Token::delete($this->token);
+ $this->token = '';
+ }
$this->setError($e->getMessage());
return false;
}
@@ -230,7 +234,7 @@ class Auth extends \ba\Auth
$this->model->startTrans();
try {
$this->model->login_failure++;
- $this->model->last_login_time = date('Y-m-d H:i:s');
+ $this->model->last_login_time = time();
$this->model->last_login_ip = $ip;
$this->model->save();
$this->model->commit();
diff --git a/dafuweng-webman/app/common/middleware/LoadLangPack.php b/dafuweng-webman/app/common/middleware/LoadLangPack.php
index ab2f572..a51a14f 100644
--- a/dafuweng-webman/app/common/middleware/LoadLangPack.php
+++ b/dafuweng-webman/app/common/middleware/LoadLangPack.php
@@ -25,25 +25,50 @@ class LoadLangPack implements MiddlewareInterface
protected function loadLang(Request $request): void
{
- $controllerPath = get_controller_path($request);
- if (!$controllerPath) {
- return;
+ // 优先从请求头 think-lang 获取前端选择的语言(与前端 axios 发送的 header 对应)
+ $headerLang = $request->header('think-lang');
+ $allowLangList = config('lang.allow_lang_list', ['zh-cn', 'en']);
+ if ($headerLang && in_array(str_replace('_', '-', strtolower($headerLang)), $allowLangList)) {
+ $langSet = str_replace('_', '-', strtolower($headerLang));
+ } else {
+ $langSet = config('lang.default_lang', config('translation.locale', 'zh-cn'));
+ $langSet = str_replace('_', '-', strtolower($langSet));
}
- $langSet = config('lang.default_lang', config('translation.locale', 'zh-cn'));
- $langSet = str_replace('_', '-', strtolower($langSet));
+ // 设置当前请求的翻译语言,使 __() 和 trans() 使用正确的语言
+ if (function_exists('locale')) {
+ locale($langSet);
+ }
$path = trim($request->path(), '/');
$parts = explode('/', $path);
$app = $parts[0] ?? 'api';
- $langFile = base_path() . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . $app
- . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $langSet
- . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $controllerPath) . '.php';
+ $appLangDir = base_path() . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR;
- if (is_file($langFile) && class_exists(\support\Translation::class)) {
- $translator = \support\Translation::instance();
- $translator->addResource('phpfile', $langFile, $langSet, $controllerPath);
+ if (!class_exists(\support\Translation::class)) {
+ return;
+ }
+
+ $translator = \support\Translation::instance();
+
+ // 1. 加载根级语言包(zh-cn.php / en.php),供 common 翻译使用
+ $rootLangFile = $appLangDir . $langSet . '.php';
+ if (is_file($rootLangFile)) {
+ $translator->addResource('phpfile', $rootLangFile, $langSet, 'messages');
+ }
+
+ // 2. 加载控制器专用语言包(如 zh-cn/auth/group.php),供 get_route_remark 等使用
+ $controllerPath = get_controller_path($request);
+ if ($controllerPath) {
+ $controllerPathForFile = str_replace('.', '/', $controllerPath);
+ $controllerPathForFile = implode('/', array_map(function ($p) {
+ return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $p));
+ }, explode('/', $controllerPathForFile)));
+ $controllerLangFile = $appLangDir . $langSet . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $controllerPathForFile) . '.php';
+ if (is_file($controllerLangFile)) {
+ $translator->addResource('phpfile', $controllerLangFile, $langSet, $controllerPath);
+ }
}
}
}
diff --git a/dafuweng-webman/app/functions.php b/dafuweng-webman/app/functions.php
index 1830c17..7a7fb1a 100644
--- a/dafuweng-webman/app/functions.php
+++ b/dafuweng-webman/app/functions.php
@@ -405,7 +405,7 @@ if (!function_exists('get_controller_list')) {
function get_controller_list(string $app = 'admin'): array
{
$controllerDir = root_path() . 'app' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . 'controller' . DIRECTORY_SEPARATOR;
- return class_exists(\ba\Filesystem::class)
+ return (class_exists(\ba\Filesystem::class) && is_dir($controllerDir))
? \ba\Filesystem::getDirFiles($controllerDir)
: [];
}
@@ -414,6 +414,7 @@ if (!function_exists('get_controller_list')) {
if (!function_exists('get_route_remark')) {
/**
* 获取当前路由后台菜单规则的备注信息
+ * 使用控制器 domain 翻译,以支持不同控制器对同一 key(如 Remark lang)的不同翻译
*/
function get_route_remark(): string
{
@@ -435,7 +436,11 @@ if (!function_exists('get_route_remark')) {
$remark = \support\think\Db::name('admin_rule')
->where('name', 'in', $names)
->value('remark');
- return __((string) ($remark ?? ''));
+ $remarkStr = (string) ($remark ?? '');
+ if (!$remarkStr) {
+ return '';
+ }
+ return function_exists('trans') ? trans($remarkStr, [], $controllerPath ?: null) : $remarkStr;
}
}
diff --git a/dafuweng-webman/app/support/BaseController.php b/dafuweng-webman/app/support/BaseController.php
index cf76326..3309bf4 100644
--- a/dafuweng-webman/app/support/BaseController.php
+++ b/dafuweng-webman/app/support/BaseController.php
@@ -87,7 +87,14 @@ abstract class BaseController
unset($header['statusCode']);
$headers = array_merge(['Content-Type' => 'application/json'], $header);
- $jsonBody = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
+ $options = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
+ if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) {
+ $options |= JSON_INVALID_UTF8_SUBSTITUTE;
+ }
+ $jsonBody = json_encode($body, $options);
+ if ($jsonBody === false) {
+ $jsonBody = '{"code":0,"msg":"JSON encode error","time":' . time() . ',"data":[]}';
+ }
return response($jsonBody, $statusCode, $headers);
}
diff --git a/dafuweng-webman/config/database.php b/dafuweng-webman/config/database.php
index a377f9d..f33ca66 100644
--- a/dafuweng-webman/config/database.php
+++ b/dafuweng-webman/config/database.php
@@ -1,14 +1,23 @@
'mysql',
'connections' => [
'mysql' => [
'driver' => 'mysql',
- 'host' => '127.0.0.1',
- 'port' => '3306',
- 'database' => env('DATABASE_DATABASE', 'dafuweng-buildadmin'),
- 'username' => env('DATABASE_USERNAME', 'dafuweng-buildadmin'),
- 'password' => env('DATABASE_PASSWORD', '123456'),
+ 'host' => $env('database.hostname', '127.0.0.1'),
+ 'port' => $env('database.hostport', '3306'),
+ 'database' => $env('database.database', 'dafuweng-buildadmin'),
+ 'username' => $env('database.username', 'dafuweng-buildadmin'),
+ 'password' => $env('database.password', '123456'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',
'prefix' => '',
diff --git a/dafuweng-webman/config/route.php b/dafuweng-webman/config/route.php
index d3099a2..93cb59b 100644
--- a/dafuweng-webman/config/route.php
+++ b/dafuweng-webman/config/route.php
@@ -198,6 +198,8 @@ Route::post('/admin/crud/crud/generate', [\app\admin\controller\crud\Crud::class
Route::post('/admin/crud/crud/logStart', [\app\admin\controller\crud\Crud::class, 'logStart']);
Route::post('/admin/crud/crud/delete', [\app\admin\controller\crud\Crud::class, 'delete']);
Route::get('/admin/crud/crud/getFileData', [\app\admin\controller\crud\Crud::class, 'getFileData']);
+// 兼容 ThinkPHP 风格 URL:/admin/crud.Crud/getFileData
+Route::get('/admin/crud.Crud/getFileData', [\app\admin\controller\crud\Crud::class, 'getFileData']);
Route::get('/admin/crud/crud/checkCrudLog', [\app\admin\controller\crud\Crud::class, 'checkCrudLog']);
Route::post('/admin/crud/crud/parseFieldData', [\app\admin\controller\crud\Crud::class, 'parseFieldData']);
Route::post('/admin/crud/crud/generateCheck', [\app\admin\controller\crud\Crud::class, 'generateCheck']);
@@ -228,6 +230,30 @@ Route::get('/admin/security/dataRecycleLog/index', [\app\admin\controller\securi
Route::post('/admin/security/dataRecycleLog/restore', [\app\admin\controller\security\DataRecycleLog::class, 'restore']);
Route::get('/admin/security/dataRecycleLog/info', [\app\admin\controller\security\DataRecycleLog::class, 'info']);
+// ==================== 兼容 ThinkPHP 风格 URL(module.Controller/action) ====================
+// 前端使用 /admin/user.Rule/index 格式,需转换为控制器调用
+Route::add(
+ ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'],
+ '/admin/{controllerPart:[a-zA-Z]+\\.[a-zA-Z0-9]+}/{action}',
+ function (\Webman\Http\Request $request, string $controllerPart, string $action) {
+ $pos = strpos($controllerPart, '.');
+ if ($pos === false) {
+ return new Response(404, ['Content-Type' => 'application/json'], json_encode(['code' => 404, 'msg' => '404 Not Found', 'data' => []], JSON_UNESCAPED_UNICODE));
+ }
+ $module = substr($controllerPart, 0, $pos);
+ $controller = substr($controllerPart, $pos + 1);
+ $class = '\\app\\admin\\controller\\' . strtolower($module) . '\\' . $controller;
+ if (!class_exists($class)) {
+ return new Response(404, ['Content-Type' => 'application/json'], json_encode(['code' => 404, 'msg' => '404 Not Found', 'data' => []], JSON_UNESCAPED_UNICODE));
+ }
+ if (!method_exists($class, $action)) {
+ return new Response(404, ['Content-Type' => 'application/json'], json_encode(['code' => 404, 'msg' => '404 Not Found', 'data' => []], JSON_UNESCAPED_UNICODE));
+ }
+ $instance = new $class();
+ return $instance->$action($request);
+ }
+);
+
// ==================== CORS 预检(OPTIONS) ====================
// 放在最后注册;显式加上前端会请求的路径,再加固通配
Route::add('OPTIONS', '/api/index/index', [\app\common\middleware\AllowCrossDomain::class, 'optionsResponse']);
diff --git a/dafuweng-webman/docs/CRUD生成逻辑说明.md b/dafuweng-webman/docs/CRUD生成逻辑说明.md
new file mode 100644
index 0000000..9566826
--- /dev/null
+++ b/dafuweng-webman/docs/CRUD生成逻辑说明.md
@@ -0,0 +1,165 @@
+# CRUD 代码生成逻辑说明
+
+参考 ThinkPHP8 与 Webman 实现,说明各配置项含义及生成流程。
+
+---
+
+## 一、配置项说明
+
+### 1. 生成代码的相对路径(generateRelativePath)
+
+**作用**:作为路径前缀,用于快速组合生成位置。支持多级目录,如 `crud/test` 会生成到 `crud/test` 子目录。
+
+**逻辑**:
+- 输入值会替换 `/` 为 `\` 后作为 `table` 参与路径计算
+- 与表名等价:`test` 和 `crud/test` 都会触发 `getFileData` 获取默认路径
+- 为空时,需先填写**数据表名**,由表名驱动路径计算
+
+**示例**:
+- `test` → 控制器 `app/admin/controller/Test.php`,视图 `web/src/views/backend/test/`
+- `crud/test` → 控制器 `app/admin/controller/crud/Test.php`,视图 `web/src/views/backend/crud/test/`
+
+---
+
+### 2. 生成的控制器位置(controllerFile)
+
+**作用**:控制器文件的相对路径,如 `app/admin/controller/crud/Test.php`。
+
+**逻辑**:
+- 由 `Helper::parseNameData('admin', $tableName, 'controller', $table['controllerFile'])` 计算
+- 为空时:按表名解析,如 `test` → `app/admin/controller/Test.php`
+- 有值时:按自定义路径解析,会去掉 `app/admin/controller` 前缀后按剩余部分生成
+
+**Webman 适配**:与 ThinkPHP 相同,目录为 `app/admin/controller/`。
+
+---
+
+### 3. 生成的数据模型位置(modelFile)
+
+**作用**:模型文件的相对路径,如 `app/admin/model/crud/Test.php` 或 `app/common/model/Test.php`。
+
+**逻辑**:
+- 由 `Helper::parseNameData($app, $tableName, 'model', $table['modelFile'])` 计算
+- `$app` 由「公共模型」决定:勾选为 `common`,否则为 `admin`
+- 为空时按表名解析
+
+**Webman 适配**:与 ThinkPHP 相同,目录为 `app/admin/model/` 或 `app/common/model/`。
+
+---
+
+### 4. 生成的验证器位置(validateFile)
+
+**作用**:验证器文件的相对路径,如 `app/admin/validate/crud/Test.php`。
+
+**逻辑**:
+- 由 `Helper::parseNameData($app, $tableName, 'validate', $table['validateFile'])` 计算
+- 与模型使用相同的 `$app`(公共模型或 admin)
+
+**Webman 适配**:与 ThinkPHP 相同,目录为 `app/admin/validate/` 或 `app/common/validate/`。
+
+---
+
+### 5. WEB 端视图目录(webViewsDir)
+
+**作用**:前端 Vue 视图目录,如 `web/src/views/backend/test`。
+
+**逻辑**:
+- 由 `Helper::parseWebDirNameData($tableName, 'views', $table['webViewsDir'])` 计算
+- 生成 `index.vue`、`popupForm.vue` 及对应语言包
+- 为空时按表名解析,如 `test` → `web/src/views/backend/test`
+
+**Webman 适配**:与 ThinkPHP 相同,目录为 `web/src/views/backend/`。
+
+---
+
+### 6. 数据库连接配置标识(databaseConnection)
+
+**作用**:选择使用的数据库连接,对应 `config/thinkorm.php` 中 `connections` 的 key。
+
+**逻辑**:
+- ThinkPHP:`config('database.connections')`
+- Webman:`config('thinkorm.connections')`,Ajax 的 `getDatabaseConnectionList` 已适配
+- 留空使用默认连接:`config('thinkorm.default', 'mysql')`
+
+**Webman 适配**:已从 `database.php` 迁移到 `thinkorm.php`,无需额外修改。
+
+---
+
+## 二、自动填充流程
+
+当**数据表名**或**生成代码的相对路径**变化时:
+
+1. 前端调用 `getFileData(table, commonModel)`
+2. 后端 `Crud::getFileData` 根据表名和是否公共模型计算默认路径
+3. 返回并填充:
+ - `modelFile`
+ - `controllerFile`
+ - `validateFile`
+ - `webViewsDir`
+
+**若这些字段为空**,可能原因:
+- **从零新建**:需先输入数据表名(如 `test`),输入后会自动调用 `getFileData` 填充(输入过程中 400ms 防抖触发,无需失焦)
+- 表名格式需符合 `^[a-z_][a-z0-9_]*$`(小写字母、数字、下划线)
+- 「高级配置」默认展开,可直接看到控制器/模型/验证器/视图路径
+- `getFileData` 接口异常(可检查网络或后端日志)
+- `getFileData` 已加入 `noNeedLogin`,无需登录即可获取路径(便于设计页加载)
+
+---
+
+## 三、generate 生成流程
+
+```
+1. 接收 table、fields 等参数
+2. 表设计:handleTableDesign(创建/更新表结构)
+3. 路径解析:
+ - modelFile = parseNameData(admin|common, tableName, 'model', table.modelFile)
+ - validateFile = parseNameData(...)
+ - controllerFile = parseNameData('admin', tableName, 'controller', table.controllerFile)
+ - webViewsDir = parseWebDirNameData(tableName, 'views', table.webViewsDir)
+4. 遍历字段,生成:
+ - 模型(Model)
+ - 控制器(Controller)
+ - 验证器(Validate)
+ - index.vue、popupForm.vue
+ - 语言包(zh-cn、en)
+5. 创建菜单(AdminRule)
+```
+
+---
+
+## 四、Webman 与 ThinkPHP 差异
+
+| 项目 | ThinkPHP | Webman |
+|--------------|------------------------|----------------------------------|
+| 数据库配置 | `config/database.php` | `config/thinkorm.php` |
+| 默认连接 | `config('database.default')` | `config('thinkorm.default')` |
+| 根路径 | `root_path()` | `root_path()`(同,指向 dafuweng-webman 项目根) |
+| 控制器初始化 | `initialize()` 自动调用 | 在 `initializeBackend` 中调用 `$this->initialize()` |
+| XSS 过滤 | `$request->filter('clean_xss')` | `$inputFilter = 'clean_xss'`,由 Backend trait 在 add/edit 时应用 |
+
+---
+
+## 五、路径解析规则(Helper)
+
+### parseNameData($app, $table, $type, $value)
+
+- `$value` 为空:按 `$table` 解析,下划线 `_` 拆分为目录,末段大驼峰为类名
+- `$value` 有值:按自定义路径解析,会去掉 `app/$app/$type` 前缀
+- 预设:`admin`→`auth/admin`,`admin_rule`→`auth/rule` 等
+
+**示例(表名 `xxx_yyy_zzz`,均相对于 dafuweng-webman 项目根):**
+
+| 配置项 | 默认值 | 公共模型时 |
+|------------------|------------------------------------------|----------------------------------------|
+| 生成代码的相对位置 | `xxx_yyy_zzz` | - |
+| 控制器 | `app/admin/controller/xxx/yyy/Zzz.php` | - |
+| 模型 | `app/admin/model/xxx/yyy/Zzz.php` | `app/common/model/xxx/yyy/Zzz.php` |
+| 验证器 | `app/admin/validate/xxx/yyy/Zzz.php` | `app/common/validate/xxx/yyy/Zzz.php` |
+| WEB端视图目录 | `web/src/views/backend/xxx/yyy/zzz` | - |
+
+**规则:** 表名 `xxx_yyy_zzz` → 前段 `xxx`、`yyy` 为子目录(小写),末段 `zzz` 转为 `Zzz.php`(大驼峰);视图目录保持全小写。
+
+### parseWebDirNameData($table, $type, $value)
+
+- `$type='views'`:生成 `web/src/views/backend/{path}/{name}`,如 `xxx_yyy_zzz` → `web/src/views/backend/xxx/yyy/zzz`
+- `$type='lang'`:生成 `web/src/lang/backend/{lang}/{path}/{name}`
diff --git a/dafuweng-webman/public/storage/default/20260318/su7_1a2cfbb319138af7ca03c74715f85ee91b770bfe3.jpg b/dafuweng-webman/public/storage/default/20260318/su7_1a2cfbb319138af7ca03c74715f85ee91b770bfe3.jpg
new file mode 100644
index 0000000..60aee45
Binary files /dev/null and b/dafuweng-webman/public/storage/default/20260318/su7_1a2cfbb319138af7ca03c74715f85ee91b770bfe3.jpg differ
diff --git a/dafuweng-webman/web/src/utils/axios.ts b/dafuweng-webman/web/src/utils/axios.ts
index 6b65c07..19d6fa6 100644
--- a/dafuweng-webman/web/src/utils/axios.ts
+++ b/dafuweng-webman/web/src/utils/axios.ts
@@ -88,12 +88,13 @@ function createAxios>(axiosConfig: AxiosRequest
}
}
- // 自动携带token
+ // 自动携带token和语言(确保每次请求使用当前语言,后端据此返回对应语言的 remark 等)
if (config.headers) {
const token = adminInfo.getToken()
if (token) (config.headers as anyObj).batoken = token
const userToken = options.anotherToken || userInfo.getToken()
if (userToken) (config.headers as anyObj)['ba-user-token'] = userToken
+ ;(config.headers as anyObj)['think-lang'] = useConfig().lang.defaultLang
}
return config
diff --git a/dafuweng-webman/web/src/views/backend/crud/design.vue b/dafuweng-webman/web/src/views/backend/crud/design.vue
index ee588b0..6a35bde 100644
--- a/dafuweng-webman/web/src/views/backend/crud/design.vue
+++ b/dafuweng-webman/web/src/views/backend/crud/design.vue
@@ -96,6 +96,7 @@
:block-help="t('crud.crud.For quick combination code generation location, please fill in the relative path')"
:input-attr="{
onChange: onTableChange,
+ onInput: debouncedOnRelativePathInput,
}"
/>
{
})
/**
- * 修改表名
- * @param val 新表名
+ * 修改表名(失焦时校验,路径填充由 watch 统一触发避免重复请求)
*/
const onTableNameChange = (val: string) => {
if (!val) return (state.error.tableName = '')
if (/^[a-z_][a-z0-9_]*$/.test(val)) {
state.error.tableName = ''
- onTableChange(val)
} else {
state.error.tableName = t('crud.crud.Use lower case underlined for table names')
}
tableDesignChangeInit()
}
+/** 相对路径输入时防抖触发路径填充 */
+const debouncedOnRelativePathInput = debounce((val: string) => {
+ if (val) onTableChange(val)
+}, 400)
+
+/** 监听表名变化,统一防抖触发路径填充(唯一入口,避免 watch + onInput 重复请求) */
+watch(
+ () => state.table.name,
+ debounce((val: string) => {
+ if (val && /^[a-z_][a-z0-9_]*$/.test(val)) {
+ onTableChange(val)
+ }
+ }, 400)
+)
+
const tableDesignChangeInit = () => {
state.table.rebuild = 'No'
state.table.designChange = []
@@ -1587,17 +1601,22 @@ const tableDesignChangeInit = () => {
*/
const onTableChange = (val: string) => {
if (!val) return
- getFileData(val, state.table.isCommonModel).then((res) => {
- state.table.modelFile = res.data.modelFile
- state.table.controllerFile = res.data.controllerFile
- state.table.validateFile = res.data.validateFile
- state.table.webViewsDir = res.data.webViewsDir
- state.table.generateRelativePath = val.replaceAll('/', '\\')
- })
+ getFileData(val, state.table.isCommonModel)
+ .then((res) => {
+ state.table.modelFile = res.data.modelFile
+ state.table.controllerFile = res.data.controllerFile
+ state.table.validateFile = res.data.validateFile
+ state.table.webViewsDir = res.data.webViewsDir
+ state.table.generateRelativePath = val.replaceAll('/', '\\')
+ })
+ .catch(() => {
+ // 接口失败时静默处理,避免重复弹窗(axios 已统一处理错误提示)
+ })
}
const onChangeCommonModel = () => {
- onTableChange(state.table.generateRelativePath)
+ const table = state.table.name || state.table.generateRelativePath?.replace(/\\/g, '/')
+ if (table) onTableChange(table)
}
const onJoinTableChange = () => {
diff --git a/web/src/utils/axios.ts b/web/src/utils/axios.ts
index 6b65c07..19d6fa6 100644
--- a/web/src/utils/axios.ts
+++ b/web/src/utils/axios.ts
@@ -88,12 +88,13 @@ function createAxios>(axiosConfig: AxiosRequest
}
}
- // 自动携带token
+ // 自动携带token和语言(确保每次请求使用当前语言,后端据此返回对应语言的 remark 等)
if (config.headers) {
const token = adminInfo.getToken()
if (token) (config.headers as anyObj).batoken = token
const userToken = options.anotherToken || userInfo.getToken()
if (userToken) (config.headers as anyObj)['ba-user-token'] = userToken
+ ;(config.headers as anyObj)['think-lang'] = useConfig().lang.defaultLang
}
return config
diff --git a/web/src/views/backend/crud/design.vue b/web/src/views/backend/crud/design.vue
index ee588b0..6a35bde 100644
--- a/web/src/views/backend/crud/design.vue
+++ b/web/src/views/backend/crud/design.vue
@@ -96,6 +96,7 @@
:block-help="t('crud.crud.For quick combination code generation location, please fill in the relative path')"
:input-attr="{
onChange: onTableChange,
+ onInput: debouncedOnRelativePathInput,
}"
/>
{
})
/**
- * 修改表名
- * @param val 新表名
+ * 修改表名(失焦时校验,路径填充由 watch 统一触发避免重复请求)
*/
const onTableNameChange = (val: string) => {
if (!val) return (state.error.tableName = '')
if (/^[a-z_][a-z0-9_]*$/.test(val)) {
state.error.tableName = ''
- onTableChange(val)
} else {
state.error.tableName = t('crud.crud.Use lower case underlined for table names')
}
tableDesignChangeInit()
}
+/** 相对路径输入时防抖触发路径填充 */
+const debouncedOnRelativePathInput = debounce((val: string) => {
+ if (val) onTableChange(val)
+}, 400)
+
+/** 监听表名变化,统一防抖触发路径填充(唯一入口,避免 watch + onInput 重复请求) */
+watch(
+ () => state.table.name,
+ debounce((val: string) => {
+ if (val && /^[a-z_][a-z0-9_]*$/.test(val)) {
+ onTableChange(val)
+ }
+ }, 400)
+)
+
const tableDesignChangeInit = () => {
state.table.rebuild = 'No'
state.table.designChange = []
@@ -1587,17 +1601,22 @@ const tableDesignChangeInit = () => {
*/
const onTableChange = (val: string) => {
if (!val) return
- getFileData(val, state.table.isCommonModel).then((res) => {
- state.table.modelFile = res.data.modelFile
- state.table.controllerFile = res.data.controllerFile
- state.table.validateFile = res.data.validateFile
- state.table.webViewsDir = res.data.webViewsDir
- state.table.generateRelativePath = val.replaceAll('/', '\\')
- })
+ getFileData(val, state.table.isCommonModel)
+ .then((res) => {
+ state.table.modelFile = res.data.modelFile
+ state.table.controllerFile = res.data.controllerFile
+ state.table.validateFile = res.data.validateFile
+ state.table.webViewsDir = res.data.webViewsDir
+ state.table.generateRelativePath = val.replaceAll('/', '\\')
+ })
+ .catch(() => {
+ // 接口失败时静默处理,避免重复弹窗(axios 已统一处理错误提示)
+ })
}
const onChangeCommonModel = () => {
- onTableChange(state.table.generateRelativePath)
+ const table = state.table.name || state.table.generateRelativePath?.replace(/\\/g, '/')
+ if (table) onTableChange(table)
}
const onJoinTableChange = () => {