mirror of
https://gitee.com/wonderful-code/buildadmin
synced 2024-11-21 14:41:29 +00:00
feat(CRUD):多数据库连接配置支持
This commit is contained in:
parent
c95fc126d0
commit
bbeb267649
@ -59,7 +59,7 @@ class Crud extends Backend
|
||||
*/
|
||||
protected array $dtStringToArray = ['checkbox', 'selects', 'remoteSelects', 'city', 'images', 'files'];
|
||||
|
||||
protected array $noNeedPermission = ['logStart', 'getFileData', 'parseFieldData', 'generateCheck', 'databaseList'];
|
||||
protected array $noNeedPermission = ['logStart', 'getFileData', 'parseFieldData', 'generateCheck'];
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
@ -89,11 +89,11 @@ class Crud extends Backend
|
||||
]);
|
||||
|
||||
// 表名称
|
||||
$tableName = TableManager::tableName($table['name'], false);
|
||||
$tableName = TableManager::tableName($table['name'], false, $table['databaseConnection']);
|
||||
|
||||
if ($type == 'create' || $table['rebuild'] == 'Yes') {
|
||||
// 数据表存在则删除
|
||||
TableManager::phinxTable($tableName)->drop()->save();
|
||||
TableManager::phinxTable($tableName, [], true, $table['databaseConnection'])->drop()->save();
|
||||
}
|
||||
|
||||
// 处理表设计
|
||||
@ -127,6 +127,7 @@ class Crud extends Backend
|
||||
$this->modelData['beforeInsertMixins'] = [];
|
||||
$this->modelData['beforeInsert'] = '';
|
||||
$this->modelData['afterInsert'] = '';
|
||||
$this->modelData['connection'] = $table['databaseConnection'];
|
||||
$this->modelData['name'] = $tableName;
|
||||
$this->modelData['className'] = $modelFile['lastName'];
|
||||
$this->modelData['namespace'] = $modelFile['namespace'];
|
||||
@ -205,7 +206,7 @@ class Crud extends Backend
|
||||
|
||||
// 关联表数据解析
|
||||
if (in_array($field['designType'], ['remoteSelect', 'remoteSelects'])) {
|
||||
$this->parseJoinData($field);
|
||||
$this->parseJoinData($field, $table);
|
||||
}
|
||||
|
||||
// 模型方法
|
||||
@ -308,9 +309,15 @@ class Crud extends Backend
|
||||
}
|
||||
|
||||
// 数据表是否有数据
|
||||
$adapter = TableManager::phinxAdapter();
|
||||
if ($adapter->hasTable($info['table']['name'])) {
|
||||
$info['table']['empty'] = Db::name($info['table']['name'])->limit(1)->select()->isEmpty();
|
||||
$connection = TableManager::getConnection($info['table']['databaseConnection']);
|
||||
$tableName = TableManager::tableName($info['table']['name'], false, $connection);
|
||||
$adapter = TableManager::phinxAdapter(true, $connection);
|
||||
if ($adapter->hasTable($tableName)) {
|
||||
$info['table']['empty'] = Db::connect($connection)
|
||||
->name($tableName)
|
||||
->limit(1)
|
||||
->select()
|
||||
->isEmpty();
|
||||
} else {
|
||||
$info['table']['empty'] = true;
|
||||
}
|
||||
@ -438,9 +445,13 @@ class Crud extends Backend
|
||||
*/
|
||||
public function checkCrudLog(): void
|
||||
{
|
||||
$table = $this->request->get('table');
|
||||
$table = $this->request->get('table');
|
||||
$connection = $this->request->get('connection');
|
||||
$connection = $connection ?: config('database.default');
|
||||
|
||||
$crudLog = Db::name('crud_log')
|
||||
->where('table_name', $table)
|
||||
->where('connection', $connection)
|
||||
->order('create_time desc')
|
||||
->find();
|
||||
$this->success('', [
|
||||
@ -455,27 +466,36 @@ class Crud extends Backend
|
||||
public function parseFieldData(): void
|
||||
{
|
||||
AdminLog::setTitle(__('Parse field data'));
|
||||
$type = $this->request->post('type');
|
||||
$table = $this->request->post('table');
|
||||
$table = TableManager::tableName($table);
|
||||
$type = $this->request->post('type');
|
||||
$table = $this->request->post('table');
|
||||
$connection = $this->request->post('connection');
|
||||
$connection = TableManager::getConnection($connection);
|
||||
|
||||
$table = TableManager::tableName($table, true, $connection);
|
||||
$connectionConfig = TableManager::getConnectionConfig($connection);
|
||||
|
||||
if ($type == 'db') {
|
||||
$sql = 'SELECT * FROM `information_schema`.`tables` '
|
||||
. 'WHERE TABLE_SCHEMA = ? AND table_name = ?';
|
||||
$tableInfo = Db::query($sql, [config('database.connections.mysql.database'), $table]);
|
||||
$tableInfo = Db::connect($connection)->query($sql, [$connectionConfig['database'], $table]);
|
||||
if (!$tableInfo) {
|
||||
$this->error(__('Record not found'));
|
||||
}
|
||||
|
||||
// 数据表是否有数据
|
||||
$adapter = TableManager::phinxAdapter(false);
|
||||
$adapter = TableManager::phinxAdapter(false, $connection);
|
||||
if ($adapter->hasTable($table)) {
|
||||
$empty = Db::table($table)->limit(1)->select()->isEmpty();
|
||||
$empty = Db::connect($connection)
|
||||
->table($table)
|
||||
->limit(1)
|
||||
->select()
|
||||
->isEmpty();
|
||||
} else {
|
||||
$empty = true;
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'columns' => Helper::parseTableColumns($table),
|
||||
'columns' => Helper::parseTableColumns($table, false, $connection),
|
||||
'comment' => $tableInfo[0]['TABLE_COMMENT'] ?? '',
|
||||
'empty' => $empty,
|
||||
]);
|
||||
@ -490,6 +510,7 @@ class Crud extends Backend
|
||||
{
|
||||
$table = $this->request->post('table');
|
||||
$controllerFile = $this->request->post('controllerFile', '');
|
||||
$connection = $this->request->post('connection');
|
||||
|
||||
if (!$table) {
|
||||
$this->error(__('Parameter error'));
|
||||
@ -505,8 +526,8 @@ class Crud extends Backend
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
$tableList = TableManager::getTableList();
|
||||
$tableExist = array_key_exists(TableManager::tableName($table), $tableList);
|
||||
$tableList = TableManager::getTableList($connection);
|
||||
$tableExist = array_key_exists(TableManager::tableName($table, true, $connection), $tableList);
|
||||
$controllerExist = file_exists(root_path() . $controllerFile);
|
||||
|
||||
if ($controllerExist || $tableExist) {
|
||||
@ -518,55 +539,21 @@ class Crud extends Backend
|
||||
$this->success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据表列表
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function databaseList(): void
|
||||
{
|
||||
$tablePrefix = config('database.connections.mysql.prefix');
|
||||
$outExcludeTable = [
|
||||
// 功能表
|
||||
'area',
|
||||
'token',
|
||||
'captcha',
|
||||
'admin_group_access',
|
||||
'config',
|
||||
'admin_log',
|
||||
// 不建议生成crud的表
|
||||
'user_money_log',
|
||||
'user_score_log',
|
||||
];
|
||||
|
||||
$outTables = [];
|
||||
$tables = TableManager::getTableList();
|
||||
$pattern = '/^' . $tablePrefix . '/i';
|
||||
foreach ($tables as $table => $tableComment) {
|
||||
if (!preg_match($pattern, $table)) continue;
|
||||
$table = preg_replace($pattern, '', $table);
|
||||
if (!in_array($table, $outExcludeTable)) {
|
||||
$outTables[$table] = $tableComment;
|
||||
}
|
||||
}
|
||||
$this->success('', [
|
||||
'dbs' => $outTables,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联表数据解析
|
||||
* @param $field
|
||||
* @param $table
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function parseJoinData($field): void
|
||||
private function parseJoinData($field, $table): void
|
||||
{
|
||||
$dictEn = [];
|
||||
$dictZhCn = [];
|
||||
|
||||
if ($field['form']['relation-fields'] && $field['form']['remote-table']) {
|
||||
$columns = Helper::parseTableColumns($field['form']['remote-table'], true);
|
||||
$columns = Helper::parseTableColumns($field['form']['remote-table'], true, $table['databaseConnection']);
|
||||
$relationFields = explode(',', $field['form']['relation-fields']);
|
||||
$tableName = TableManager::tableName($field['form']['remote-table'], false);
|
||||
$tableName = TableManager::tableName($field['form']['remote-table'], false, $table['databaseConnection']);
|
||||
$rnPattern = '/(.*)(_ids|_id)$/';
|
||||
if (preg_match($rnPattern, $field['name'])) {
|
||||
$relationName = parse_name(preg_replace($rnPattern, '$1', $field['name']), 1, false);
|
||||
@ -586,6 +573,7 @@ class Crud extends Backend
|
||||
$joinModelData['beforeInsertMixins'] = [];
|
||||
$joinModelData['beforeInsert'] = '';
|
||||
$joinModelData['afterInsert'] = '';
|
||||
$joinModelData['connection'] = $table['databaseConnection'];
|
||||
$joinModelData['name'] = $tableName;
|
||||
$joinModelData['className'] = $joinModelFile['lastName'];
|
||||
$joinModelData['namespace'] = $joinModelFile['namespace'];
|
||||
|
@ -281,10 +281,13 @@ class Helper
|
||||
]);
|
||||
return $data['id'];
|
||||
}
|
||||
$log = CrudLog::create([
|
||||
|
||||
$connection = $data['table']['databaseConnection'] ?: config('database.default');
|
||||
$log = CrudLog::create([
|
||||
'table_name' => $data['table']['name'],
|
||||
'table' => $data['table'],
|
||||
'fields' => $data['fields'],
|
||||
'connection' => $connection,
|
||||
'status' => $data['status'],
|
||||
]);
|
||||
return $log->id;
|
||||
@ -424,16 +427,17 @@ class Helper
|
||||
|
||||
/**
|
||||
* 表字段排序
|
||||
* @param string $tableName 表名
|
||||
* @param array $fields 字段数据
|
||||
* @param array $designChange 前端字段改变数据
|
||||
* @param string $tableName 表名
|
||||
* @param array $fields 字段数据
|
||||
* @param array $designChange 前端字段改变数据
|
||||
* @param ?string $connection 数据库连接标识
|
||||
* @return void
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function updateFieldOrder(string $tableName, array $fields, array $designChange): void
|
||||
public static function updateFieldOrder(string $tableName, array $fields, array $designChange, ?string $connection = null): void
|
||||
{
|
||||
if ($designChange) {
|
||||
$table = TableManager::phinxTable($tableName, [], false);
|
||||
$table = TableManager::phinxTable($tableName, [], false, $connection);
|
||||
foreach ($designChange as $item) {
|
||||
if (!$item['sync']) continue;
|
||||
|
||||
@ -469,10 +473,10 @@ class Helper
|
||||
*/
|
||||
public static function handleTableDesign(array $table, array $fields): array
|
||||
{
|
||||
$name = TableManager::tableName($table['name']);
|
||||
$name = TableManager::tableName($table['name'], true, $table['databaseConnection']);
|
||||
$comment = $table['comment'] ?? '';
|
||||
$designChange = $table['designChange'] ?? [];
|
||||
$adapter = TableManager::phinxAdapter(false);
|
||||
$adapter = TableManager::phinxAdapter(false, $table['databaseConnection']);
|
||||
|
||||
$pk = self::searchArray($fields, function ($item) {
|
||||
return $item['primaryKey'];
|
||||
@ -482,8 +486,8 @@ class Helper
|
||||
if ($adapter->hasTable($name)) {
|
||||
// 更新表
|
||||
if ($designChange) {
|
||||
$table = TableManager::phinxTable($name, [], false);
|
||||
$table->changeComment($comment)->update();
|
||||
$tableManager = TableManager::phinxTable($name, [], false, $table['databaseConnection']);
|
||||
$tableManager->changeComment($comment)->update();
|
||||
|
||||
// 改名和删除操作优先
|
||||
$priorityOpt = false;
|
||||
@ -491,23 +495,23 @@ class Helper
|
||||
|
||||
if (!$item['sync']) continue;
|
||||
|
||||
if (in_array($item['type'], ['change-field-name', 'del-field']) && !$table->hasColumn($item['oldName'])) {
|
||||
if (in_array($item['type'], ['change-field-name', 'del-field']) && !$tableManager->hasColumn($item['oldName'])) {
|
||||
// 字段不存在
|
||||
throw new BaException(__($item['type'] . ' fail not exist', [$item['oldName']]));
|
||||
}
|
||||
|
||||
if ($item['type'] == 'change-field-name') {
|
||||
$priorityOpt = true;
|
||||
$table->renameColumn($item['oldName'], $item['newName']);
|
||||
$tableManager->renameColumn($item['oldName'], $item['newName']);
|
||||
} elseif ($item['type'] == 'del-field') {
|
||||
$priorityOpt = true;
|
||||
$table->removeColumn($item['oldName']);
|
||||
$tableManager->removeColumn($item['oldName']);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存需要优先执行的操作,避免先改名再改属性时找不到字段
|
||||
if ($priorityOpt) {
|
||||
$table->update();
|
||||
$tableManager->update();
|
||||
}
|
||||
|
||||
// 修改字段属性和添加字段操作
|
||||
@ -517,7 +521,7 @@ class Helper
|
||||
|
||||
if ($item['type'] == 'change-field-attr') {
|
||||
|
||||
if (!$table->hasColumn($item['oldName'])) {
|
||||
if (!$tableManager->hasColumn($item['oldName'])) {
|
||||
// 字段不存在
|
||||
throw new BaException(__($item['type'] . ' fail not exist', [$item['oldName']]));
|
||||
}
|
||||
@ -525,10 +529,10 @@ class Helper
|
||||
$phinxFieldData = self::getPhinxFieldData(self::searchArray($fields, function ($field) use ($item) {
|
||||
return $field['name'] == $item['oldName'];
|
||||
}));
|
||||
$table->changeColumn($item['oldName'], $phinxFieldData['type'], $phinxFieldData['options']);
|
||||
$tableManager->changeColumn($item['oldName'], $phinxFieldData['type'], $phinxFieldData['options']);
|
||||
} elseif ($item['type'] == 'add-field') {
|
||||
|
||||
if ($table->hasColumn($item['newName'])) {
|
||||
if ($tableManager->hasColumn($item['newName'])) {
|
||||
// 字段已经存在
|
||||
throw new BaException(__($item['type'] . ' fail exist', [$item['newName']]));
|
||||
}
|
||||
@ -536,28 +540,28 @@ class Helper
|
||||
$phinxFieldData = self::getPhinxFieldData(self::searchArray($fields, function ($field) use ($item) {
|
||||
return $field['name'] == $item['newName'];
|
||||
}));
|
||||
$table->addColumn($item['newName'], $phinxFieldData['type'], $phinxFieldData['options']);
|
||||
$tableManager->addColumn($item['newName'], $phinxFieldData['type'], $phinxFieldData['options']);
|
||||
}
|
||||
}
|
||||
$table->update();
|
||||
$tableManager->update();
|
||||
|
||||
// 表更新结构完成再处理字段排序
|
||||
self::updateFieldOrder($name, $fields, $designChange);
|
||||
self::updateFieldOrder($name, $fields, $designChange, $table['databaseConnection']);
|
||||
}
|
||||
} else {
|
||||
// 创建表
|
||||
$table = TableManager::phinxTable($name, [
|
||||
$tableManager = TableManager::phinxTable($name, [
|
||||
'id' => false,
|
||||
'comment' => $comment,
|
||||
'row_format' => 'DYNAMIC',
|
||||
'primary_key' => $pk,
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
], false);
|
||||
], false, $table['databaseConnection']);
|
||||
foreach ($fields as $field) {
|
||||
$phinxFieldData = self::getPhinxFieldData($field);
|
||||
$table->addColumn($field['name'], $phinxFieldData['type'], $phinxFieldData['options']);
|
||||
$tableManager->addColumn($field['name'], $phinxFieldData['type'], $phinxFieldData['options']);
|
||||
}
|
||||
$table->create();
|
||||
$tableManager->create();
|
||||
}
|
||||
|
||||
return [$pk];
|
||||
@ -732,15 +736,19 @@ class Helper
|
||||
* 根据数据表解析字段数据
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function parseTableColumns(string $table, bool $analyseField = false): array
|
||||
public static function parseTableColumns(string $table, bool $analyseField = false, ?string $connection = null): array
|
||||
{
|
||||
$connection = TableManager::getConnection($connection);
|
||||
$connectionConfig = TableManager::getConnectionConfig($connection);
|
||||
|
||||
// 从数据库中获取表字段信息
|
||||
$sql = 'SELECT * FROM `information_schema`.`columns` '
|
||||
. 'WHERE TABLE_SCHEMA = ? AND table_name = ? '
|
||||
. 'ORDER BY ORDINAL_POSITION';
|
||||
|
||||
$columns = [];
|
||||
$tableColumn = Db::query($sql, [config('database.connections.mysql.database'), TableManager::tableName($table)]);
|
||||
$tableColumn = Db::connect($connection)->query($sql, [$connectionConfig['database'], TableManager::tableName($table, true, $connection)]);
|
||||
|
||||
foreach ($tableColumn as $item) {
|
||||
$isNullAble = $item['IS_NULLABLE'] == 'YES';
|
||||
if (str_contains($item['COLUMN_TYPE'], '(')) {
|
||||
@ -989,7 +997,13 @@ class Helper
|
||||
|
||||
public static function writeModelFile(string $tablePk, array $fieldsMap, array $modelData, array $modelFile): void
|
||||
{
|
||||
$modelData['pk'] = $tablePk == 'id' ? '' : "\n" . self::tab() . "// 表主键\n" . self::tab() . 'protected $pk = ' . "'$tablePk';\n" . self::tab();
|
||||
if ($modelData['connection'] && $modelData['connection'] != config('database.default')) {
|
||||
$modelData['connection'] = "\n" . self::tab() . "// 数据库连接配置标识\n" . self::tab() . 'protected $connection = ' . "'{$modelData['connection']}';\n";
|
||||
} else {
|
||||
$modelData['connection'] = '';
|
||||
}
|
||||
|
||||
$modelData['pk'] = $tablePk == 'id' ? '' : "\n" . self::tab() . "// 表主键\n" . self::tab() . 'protected $pk = ' . "'$tablePk';\n";
|
||||
$modelData['autoWriteTimestamp'] = array_key_exists(self::$createTimeField, $fieldsMap) || array_key_exists(self::$updateTimeField, $fieldsMap) ? 'true' : 'false';
|
||||
if ($modelData['autoWriteTimestamp'] == 'true') {
|
||||
$modelData['createTime'] = array_key_exists(self::$createTimeField, $fieldsMap) ? '' : "\n" . self::tab() . "protected \$createTime = false;";
|
||||
|
@ -8,7 +8,7 @@ use think\Model;
|
||||
* {%className%}
|
||||
*/
|
||||
class {%className%} extends Model
|
||||
{{%pk%}
|
||||
{{%connection%}{%pk%}
|
||||
// 表名
|
||||
protected $name = '{%name%}';
|
||||
|
||||
|
@ -26,5 +26,11 @@ class Version206 extends Migrator
|
||||
$table = $this->table('config');
|
||||
$table->insert($rows)->saveData();
|
||||
}
|
||||
|
||||
$crudLog = $this->table('crud_log');
|
||||
if (!$crudLog->hasColumn('connection')) {
|
||||
$crudLog->addColumn('connection', 'string', ['limit' => 100, 'default' => '', 'comment' => '数据库连接配置标识', 'null' => false, 'after' => 'status']);
|
||||
$crudLog->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,6 @@ export function generate(data: anyObj) {
|
||||
)
|
||||
}
|
||||
|
||||
export function getDatabaseList() {
|
||||
return createAxios({
|
||||
url: url + 'databaseList',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
export function getFileData(table: string, commonModel = 0) {
|
||||
return createAxios({
|
||||
url: url + 'getFileData',
|
||||
@ -46,15 +39,11 @@ export function generateCheck(data: anyObj) {
|
||||
)
|
||||
}
|
||||
|
||||
export function parseFieldData(type: string, table = '', sql = '') {
|
||||
export function parseFieldData(data: anyObj) {
|
||||
return createAxios({
|
||||
url: url + 'parseFieldData',
|
||||
method: 'post',
|
||||
data: {
|
||||
type: type,
|
||||
table: table,
|
||||
sql: sql,
|
||||
},
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
@ -78,12 +67,13 @@ export function postDel(id: number) {
|
||||
})
|
||||
}
|
||||
|
||||
export function checkCrudLog(table: string) {
|
||||
export function checkCrudLog(table: string, connection: string) {
|
||||
return createAxios({
|
||||
url: url + 'checkCrudLog',
|
||||
method: 'get',
|
||||
params: {
|
||||
table: table,
|
||||
connection: connection,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -71,6 +71,10 @@ export default {
|
||||
'Generated Validator Location': 'Generated Validator Location',
|
||||
'Common model': 'Common model',
|
||||
'WEB end view directory': 'WEB end view directory',
|
||||
'Database connection': 'Database connection',
|
||||
'Database connection help': 'You can configure multiple database connections in config/database.php and select it here',
|
||||
'Check model class': "Check whether protected $connection = '{connection}'; is configured in the above data model class",
|
||||
'There is no connection attribute in model class': 'If no configuration is available, you can configure it manually',
|
||||
'Advanced Configuration': 'Advanced Configuration',
|
||||
'Common Fields': 'Common Fields',
|
||||
'Base Fields': 'Base Fields',
|
||||
|
@ -69,6 +69,10 @@ export default {
|
||||
'Generated Data Model Location': '生成的数据模型位置',
|
||||
'Generated Validator Location': '生成的验证器位置',
|
||||
'WEB end view directory': 'WEB端视图目录',
|
||||
'Database connection': '数据库连接配置标识',
|
||||
'Database connection help': '您可以在 config/database.php 内配置多个数据库连接,然后在此处选择它',
|
||||
'Check model class': "请检查以上数据模型类中是否已经配置 protected $connection = '{connection}';",
|
||||
'There is no connection attribute in model class': '未配置请手动配置。',
|
||||
'Common model': '公共模型',
|
||||
'Advanced Configuration': '高级配置',
|
||||
'Common Fields': '常用字段',
|
||||
|
@ -138,6 +138,20 @@
|
||||
labelWidth: 140,
|
||||
}"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('crud.crud.Database connection')"
|
||||
v-model="state.table.databaseConnection"
|
||||
type="remoteSelect"
|
||||
:attr="{
|
||||
labelWidth: 140,
|
||||
blockHelp: t('crud.crud.Database connection help'),
|
||||
}"
|
||||
:input-attr="{
|
||||
pk: 'key',
|
||||
field: 'key',
|
||||
'remote-url': getDatabaseConnectionListUrl,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
@ -418,15 +432,31 @@
|
||||
v-if="state.remoteSelectPre.index != -1 && state.fields[state.remoteSelectPre.index]"
|
||||
>
|
||||
<FormItem
|
||||
prop="table"
|
||||
type="select"
|
||||
:label="t('crud.crud.Associated Data Table')"
|
||||
v-model="state.remoteSelectPre.form.table"
|
||||
:key="JSON.stringify(state.remoteSelectPre.dbList)"
|
||||
:data="{
|
||||
content: state.remoteSelectPre.dbList,
|
||||
type="remoteSelect"
|
||||
:key="state.table.databaseConnection"
|
||||
:input-attr="{
|
||||
pk: 'table',
|
||||
field: 'comment',
|
||||
params: {
|
||||
connection: state.table.databaseConnection,
|
||||
samePrefix: 1,
|
||||
excludeTable: [
|
||||
'area',
|
||||
'token',
|
||||
'captcha',
|
||||
'admin_group_access',
|
||||
'config',
|
||||
'admin_log',
|
||||
'user_money_log',
|
||||
'user_score_log',
|
||||
],
|
||||
},
|
||||
'remote-url': getTableListUrl,
|
||||
onChange: onJoinTableChange,
|
||||
}"
|
||||
:input-attr="{ onChange: onJoinTableChange }"
|
||||
prop="table"
|
||||
/>
|
||||
<div v-loading="state.loading.remoteSelect">
|
||||
<FormItem
|
||||
@ -494,6 +524,16 @@
|
||||
),
|
||||
}"
|
||||
/>
|
||||
<el-form-item
|
||||
v-if="state.table.databaseConnection && state.remoteSelectPre.form.modelFile"
|
||||
:label="t('crud.crud.Database connection')"
|
||||
>
|
||||
<el-text size="large" type="danger">{{ state.table.databaseConnection }}</el-text>
|
||||
<div class="block-help">
|
||||
<div>{{ t('crud.crud.Check model class', { connection: state.table.databaseConnection }) }}</div>
|
||||
<div>{{ t('crud.crud.There is no connection attribute in model class') }}</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('Reminder')">
|
||||
<div class="block-help">
|
||||
{{ t('crud.crud.Design remote select tips') }}
|
||||
@ -604,8 +644,8 @@ import { useTemplateRefsList } from '@vueuse/core'
|
||||
import { changeStep, state as crudState, getTableAttr, fieldItem, designTypes, tableFieldsKey } from '/@/views/backend/crud/index'
|
||||
import { ElNotification, ElMessageBox, ElMessage } from 'element-plus'
|
||||
import type { FormItemRule, FormInstance, TimelineItemProps, MessageHandler } from 'element-plus'
|
||||
import { getDatabaseList, getFileData, generateCheck, generate, parseFieldData, postLogStart } from '/@/api/backend/crud'
|
||||
import { getTableFieldList } from '/@/api/common'
|
||||
import { getFileData, generateCheck, generate, parseFieldData, postLogStart } from '/@/api/backend/crud'
|
||||
import { getTableFieldList, getTableListUrl, getDatabaseConnectionListUrl } from '/@/api/common'
|
||||
import { buildValidatorData, regularVarName } from '/@/utils/validate'
|
||||
import { getArrayKey } from '/@/utils/common'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@ -635,6 +675,7 @@ const state: {
|
||||
controllerFile: string
|
||||
validateFile: string
|
||||
webViewsDir: string
|
||||
databaseConnection: string
|
||||
designChange: TableDesignChange[]
|
||||
rebuild: string
|
||||
}
|
||||
@ -644,7 +685,6 @@ const state: {
|
||||
remoteSelectPre: {
|
||||
show: boolean
|
||||
index: number
|
||||
dbList: anyObj
|
||||
fieldList: anyObj
|
||||
modelFileList: anyObj
|
||||
controllerFileList: anyObj
|
||||
@ -692,6 +732,7 @@ const state: {
|
||||
controllerFile: '',
|
||||
validateFile: '',
|
||||
webViewsDir: '',
|
||||
databaseConnection: '',
|
||||
designChange: [],
|
||||
rebuild: 'No',
|
||||
},
|
||||
@ -701,7 +742,6 @@ const state: {
|
||||
remoteSelectPre: {
|
||||
show: false,
|
||||
index: -1,
|
||||
dbList: [],
|
||||
fieldList: [],
|
||||
modelFileList: [],
|
||||
controllerFileList: [],
|
||||
@ -884,34 +924,30 @@ const showRemoteSelectPre = (index: number, hideDelField = false) => {
|
||||
state.remoteSelectPre.loading = true
|
||||
state.remoteSelectPre.index = index
|
||||
state.remoteSelectPre.hideDelField = hideDelField
|
||||
getDatabaseList()
|
||||
.then((res) => {
|
||||
state.remoteSelectPre.dbList = res.data.dbs
|
||||
if (state.fields[index] && state.fields[index].form['remote-table'].value) {
|
||||
state.remoteSelectPre.form.table = state.fields[index].form['remote-table'].value
|
||||
state.remoteSelectPre.form.pk = state.fields[index].form['remote-pk'].value
|
||||
state.remoteSelectPre.form.label = state.fields[index].form['remote-field'].value
|
||||
state.remoteSelectPre.form.controllerFile = state.fields[index].form['remote-controller'].value
|
||||
state.remoteSelectPre.form.modelFile = state.fields[index].form['remote-model'].value
|
||||
state.remoteSelectPre.form.joinField = state.fields[index].form['relation-fields'].value.split(',')
|
||||
getTableFieldList(state.fields[index].form['remote-table'].value).then((res) => {
|
||||
const fieldSelect: anyObj = {}
|
||||
for (const key in res.data.fieldList) {
|
||||
fieldSelect[key] = (key ? key + ' - ' : '') + res.data.fieldList[key]
|
||||
}
|
||||
state.remoteSelectPre.fieldList = fieldSelect
|
||||
})
|
||||
if (isEmpty(state.remoteSelectPre.modelFileList) || isEmpty(state.remoteSelectPre.controllerFileList)) {
|
||||
getFileData(state.fields[index].form['remote-table'].value).then((res) => {
|
||||
state.remoteSelectPre.modelFileList = res.data.modelFileList
|
||||
state.remoteSelectPre.controllerFileList = res.data.controllerFileList
|
||||
})
|
||||
}
|
||||
|
||||
if (state.fields[index] && state.fields[index].form['remote-table'].value) {
|
||||
state.remoteSelectPre.form.table = state.fields[index].form['remote-table'].value
|
||||
state.remoteSelectPre.form.pk = state.fields[index].form['remote-pk'].value
|
||||
state.remoteSelectPre.form.label = state.fields[index].form['remote-field'].value
|
||||
state.remoteSelectPre.form.controllerFile = state.fields[index].form['remote-controller'].value
|
||||
state.remoteSelectPre.form.modelFile = state.fields[index].form['remote-model'].value
|
||||
state.remoteSelectPre.form.joinField = state.fields[index].form['relation-fields'].value.split(',')
|
||||
getTableFieldList(state.fields[index].form['remote-table'].value).then((res) => {
|
||||
const fieldSelect: anyObj = {}
|
||||
for (const key in res.data.fieldList) {
|
||||
fieldSelect[key] = (key ? key + ' - ' : '') + res.data.fieldList[key]
|
||||
}
|
||||
state.remoteSelectPre.fieldList = fieldSelect
|
||||
})
|
||||
.finally(() => {
|
||||
state.remoteSelectPre.loading = false
|
||||
})
|
||||
if (isEmpty(state.remoteSelectPre.modelFileList) || isEmpty(state.remoteSelectPre.controllerFileList)) {
|
||||
getFileData(state.fields[index].form['remote-table'].value).then((res) => {
|
||||
state.remoteSelectPre.modelFileList = res.data.modelFileList
|
||||
state.remoteSelectPre.controllerFileList = res.data.controllerFileList
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
state.remoteSelectPre.loading = false
|
||||
}
|
||||
|
||||
const onEditField = (index: number, field: FieldItem) => {
|
||||
@ -987,6 +1023,7 @@ const onGenerate = () => {
|
||||
state.loading.generate = true
|
||||
generateCheck({
|
||||
table: state.table.name,
|
||||
connection: state.table.databaseConnection,
|
||||
controllerFile: state.table.controllerFile,
|
||||
})
|
||||
.then(() => {
|
||||
@ -1112,7 +1149,12 @@ const loadData = () => {
|
||||
}
|
||||
|
||||
// 从数据表或sql开始
|
||||
parseFieldData(crudState.type, crudState.startData.db, crudState.startData.sql)
|
||||
parseFieldData({
|
||||
type: crudState.type,
|
||||
table: crudState.startData.table,
|
||||
sql: crudState.startData.sql,
|
||||
connection: crudState.startData.databaseConnection,
|
||||
})
|
||||
.then((res) => {
|
||||
let fields = []
|
||||
for (const key in res.data.columns) {
|
||||
@ -1134,12 +1176,13 @@ const loadData = () => {
|
||||
}
|
||||
state.fields = fields
|
||||
state.table.comment = res.data.comment
|
||||
state.table.databaseConnection = crudState.startData.databaseConnection
|
||||
if (res.data.empty) {
|
||||
state.table.rebuild = 'Yes'
|
||||
}
|
||||
if (crudState.type == 'db' && crudState.startData.db) {
|
||||
state.table.name = crudState.startData.db
|
||||
onTableChange(crudState.startData.db)
|
||||
if (crudState.type == 'db' && crudState.startData.table) {
|
||||
state.table.name = crudState.startData.table
|
||||
onTableChange(crudState.startData.table)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
|
@ -27,17 +27,19 @@ export const state: {
|
||||
step: 'Start' | 'Design'
|
||||
type: string
|
||||
startData: {
|
||||
db: string
|
||||
sql: string
|
||||
table: string
|
||||
logId: string
|
||||
databaseConnection: string
|
||||
}
|
||||
} = reactive({
|
||||
step: 'Start',
|
||||
type: '',
|
||||
startData: {
|
||||
db: '',
|
||||
sql: '',
|
||||
table: '',
|
||||
logId: '',
|
||||
databaseConnection: '',
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -13,7 +13,15 @@
|
||||
:buttons="['refresh', 'quickSearch', 'columnDisplay']"
|
||||
:quick-search-placeholder="t('Quick search placeholder', { fields: t('crud.log.quick Search Fields') })"
|
||||
/>
|
||||
<Table ref="tableRef" />
|
||||
<Table ref="tableRef">
|
||||
<template #tableName>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="table_name" align="center" :label="t('crud.log.table_name')">
|
||||
<template #default="scope">
|
||||
{{ (scope.row.table.databaseConnection ? scope.row.table.databaseConnection + '.' : '') + scope.row.table.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</Table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
@ -105,11 +113,10 @@ const baTable = new baTableClass(
|
||||
{ label: t('crud.log.id'), prop: 'id', align: 'center', width: 70, operator: '=', operatorPlaceholder: t('Id'), sortable: 'custom' },
|
||||
{
|
||||
label: t('crud.log.table_name'),
|
||||
prop: 'table_name',
|
||||
align: 'center',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
operator: 'LIKE',
|
||||
sortable: false,
|
||||
render: 'slot',
|
||||
slotName: 'tableName',
|
||||
},
|
||||
{
|
||||
label: t('crud.log.status'),
|
||||
|
@ -59,7 +59,7 @@
|
||||
</el-row>
|
||||
|
||||
<el-dialog
|
||||
class="ba-operate-dialog select-db-dialog"
|
||||
class="ba-operate-dialog select-table-dialog"
|
||||
v-model="state.dialog.visible"
|
||||
:title="state.dialog.type == 'sql' ? t('crud.crud.Please enter SQL') : t('crud.crud.Please select a data table')"
|
||||
:destroy-on-close="true"
|
||||
@ -67,7 +67,7 @@
|
||||
<el-form
|
||||
:label-width="140"
|
||||
@keyup.enter="onSubmit()"
|
||||
class="select-db-form"
|
||||
class="select-table-form"
|
||||
ref="formRef"
|
||||
:model="crudState.startData"
|
||||
:rules="rules"
|
||||
@ -87,22 +87,51 @@
|
||||
</template>
|
||||
<template v-else-if="state.dialog.type == 'db'">
|
||||
<FormItem
|
||||
:label="t('crud.crud.data sheet')"
|
||||
class="select-db"
|
||||
v-model="crudState.startData.db"
|
||||
type="select"
|
||||
:key="JSON.stringify(state.dialog.dbList)"
|
||||
:placeholder="t('crud.crud.Please select a data table')"
|
||||
:data="{
|
||||
content: state.dialog.dbList,
|
||||
}"
|
||||
:label="t('crud.crud.Database connection')"
|
||||
v-model="crudState.startData.databaseConnection"
|
||||
type="remoteSelect"
|
||||
:attr="{
|
||||
labelWidth: 140,
|
||||
blockHelp: t('crud.crud.Database connection help'),
|
||||
}"
|
||||
:input-attr="{
|
||||
pk: 'key',
|
||||
field: 'key',
|
||||
'remote-url': getDatabaseConnectionListUrl,
|
||||
onChange: onDatabaseChange,
|
||||
}"
|
||||
/>
|
||||
<FormItem
|
||||
:label="t('crud.crud.data sheet')"
|
||||
v-model="crudState.startData.table"
|
||||
type="remoteSelect"
|
||||
:key="crudState.startData.databaseConnection"
|
||||
:placeholder="t('crud.crud.Please select a data table')"
|
||||
:attr="{
|
||||
labelWidth: 140,
|
||||
blockHelp: t('crud.crud.data sheet help'),
|
||||
}"
|
||||
:input-attr="{
|
||||
onChange: onDbStartChange,
|
||||
pk: 'table',
|
||||
field: 'comment',
|
||||
params: {
|
||||
connection: crudState.startData.databaseConnection,
|
||||
samePrefix: 1,
|
||||
excludeTable: [
|
||||
'area',
|
||||
'token',
|
||||
'captcha',
|
||||
'admin_group_access',
|
||||
'config',
|
||||
'admin_log',
|
||||
'user_money_log',
|
||||
'user_score_log',
|
||||
],
|
||||
},
|
||||
'remote-url': getTableListUrl,
|
||||
onChange: onTableStartChange,
|
||||
}"
|
||||
prop="db"
|
||||
prop="table"
|
||||
/>
|
||||
<el-alert
|
||||
v-if="state.successRecord"
|
||||
@ -132,7 +161,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { getDatabaseList, checkCrudLog } from '/@/api/backend/crud'
|
||||
import { checkCrudLog } from '/@/api/backend/crud'
|
||||
import FormItem from '/@/components/formItem/index.vue'
|
||||
import { changeStep, state as crudState } from '/@/views/backend/crud/index'
|
||||
import { ElNotification } from 'element-plus'
|
||||
@ -140,6 +169,7 @@ import type { FormInstance, FormItemRule } from 'element-plus'
|
||||
import { buildValidatorData } from '/@/utils/validate'
|
||||
import CrudLog from '/@/views/backend/crud/log.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { getDatabaseConnectionListUrl, getTableListUrl } from '/@/api/common'
|
||||
|
||||
const { t } = useI18n()
|
||||
const sqlInputRef = ref()
|
||||
@ -148,7 +178,6 @@ const state = reactive({
|
||||
dialog: {
|
||||
type: '',
|
||||
visible: false,
|
||||
dbList: [],
|
||||
},
|
||||
showLog: false,
|
||||
loading: false,
|
||||
@ -164,15 +193,12 @@ const onShowDialog = (type: string) => {
|
||||
}, 200)
|
||||
} else if (type == 'db') {
|
||||
state.successRecord = 0
|
||||
crudState.startData.db = ''
|
||||
getDatabaseList().then((res) => {
|
||||
state.dialog.dbList = res.data.dbs
|
||||
})
|
||||
crudState.startData.table = ''
|
||||
}
|
||||
}
|
||||
|
||||
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
|
||||
db: [buildValidatorData({ name: 'required', message: t('crud.crud.Please select a data table') })],
|
||||
table: [buildValidatorData({ name: 'required', message: t('crud.crud.Please select a data table') })],
|
||||
})
|
||||
|
||||
const onSubmit = () => {
|
||||
@ -191,11 +217,16 @@ const onSubmit = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const onDbStartChange = () => {
|
||||
if (crudState.startData.db) {
|
||||
const onDatabaseChange = () => {
|
||||
state.successRecord = 0
|
||||
crudState.startData.table = ''
|
||||
}
|
||||
|
||||
const onTableStartChange = () => {
|
||||
if (crudState.startData.table) {
|
||||
// 检查是否有CRUD记录
|
||||
state.loading = true
|
||||
checkCrudLog(crudState.startData.db)
|
||||
checkCrudLog(crudState.startData.table, crudState.startData.databaseConnection)
|
||||
.then((res) => {
|
||||
state.successRecord = res.data.id
|
||||
})
|
||||
@ -218,16 +249,17 @@ const isDev = () => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.select-db-dialog) .el-dialog__body {
|
||||
:deep(.select-table-dialog) .el-dialog__body {
|
||||
height: unset;
|
||||
.select-db-form {
|
||||
.select-table-form {
|
||||
width: 88%;
|
||||
padding: 40px 0;
|
||||
}
|
||||
.success-record-alert {
|
||||
width: calc(100% - 140px);
|
||||
margin-left: 140px;
|
||||
margin-bottom: 30px;
|
||||
margin-top: -30px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
}
|
||||
.crud-title {
|
||||
@ -264,9 +296,6 @@ const isDev = () => {
|
||||
.sql-input {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.select-db {
|
||||
margin: 40px 0;
|
||||
}
|
||||
.crud-tips {
|
||||
margin-top: 60px;
|
||||
padding: 20px;
|
||||
|
Loading…
Reference in New Issue
Block a user