苹果cms批量数据替换优化版

功能:增加数据预览,记录替换历史替换记录

优化:字段改为中文显示。

苹果cms10程序版本 2023.1000.3051
  •  新增文件
    application/admin/view/database/rep_plus.html
    application/admin/view/database/rep_log.html
  •   修改文件
    application/admin/common/auth.php
    application/admin/controller/Database.php
苹果cms批量数据替换优化版

步骤1,找到 `application/admin/common/auth.php`文件找到代码

'102' => array("show"=>1,'name' => lang('menu/database_sql'), 'controller' => 'database',		'action' => 'sql'),
'103' => array("show"=>1,'name' => lang('menu/database_rep'), 'controller' => 'database',		'action' => 'rep'),

在代码下面添加代码:

'104' => array("show"=>1,'name' => '批量替换(优化版)', 'controller' => 'database',		'action' => 'repPlus'),
'10401' => array("show"=>0,'name' => '--查询替换数据', 'controller' => 'database',		'action' => 'queryRepData'),
'105' => array("show"=>1,'name' => '替换操作记录', 'controller' => 'database',		'action' => 'repLog'),
'10501' => array("show"=>0,'name' => '--清空替换记录', 'controller' => 'database',		'action' => 'clearRepLog'),

步骤2. 找到“application/admin/controller/Database.php”在文件内代码的最后,注意粘贴前先删除文件中最后一个大括号“}”然后再粘贴下面的代码,不然会报错

/**
     * 批量数据替换(优化版)- 专门用于视频数据的图片和链接替换
     */
    public function repPlus()
    {
        if($this->request->isPost()){
            $param = input();
            $table = $param['table'];
            $field = $param['field'];
            $findstr = $param['findstr'];
            $tostr = $param['tostr'];
            $where = $param['where'];

            $validate = \think\Loader::validate('Token');
            if(!$validate->check($param)){
                return $this->error($validate->getError());
            }
            if (!empty($table) && !$this->isValidTable($table)) {
                return $this->error('数据表无效');
            }
            if(!empty($field) && !empty($findstr)){
               
                $count_sql = "SELECT COUNT(*) as total FROM ".$table." WHERE ".$field." LIKE '%".$findstr."%' AND 1=1 ". $where;
                $count_result = Db::query($count_sql);
                $affected_rows = $count_result[0]['total'] ?? 0;
                
               
                $sql = "UPDATE ".$table." set ".$field."=Replace(".$field.",'".$findstr."','".$tostr."') where 1=1 ". $where;
                Db::execute($sql);
                
                
                if($table == config('database.prefix').'vod'){
                    Cache::clear();
                }
                
              
                $this->saveRepLog([
                    'table_name' => $table,
                    'field_name' => $field,
                    'find_str' => $findstr,
                    'replace_str' => $tostr,
                    'where_condition' => $where,
                    'affected_rows' => $affected_rows,
                ]);
                
                return $this->success('执行成功!已替换 '.$affected_rows.' 条数据');
            }

            return $this->error(lang('param_err'));
        }
        
      
        $list = Db::query("SHOW TABLE STATUS");
        
       
        $vod_fields = [
            ['field' => 'vod_pic', 'name' => '视频封面图'],
            ['field' => 'vod_pic_thumb', 'name' => '视频缩略图'],
            ['field' => 'vod_pic_slide', 'name' => '视频轮播图'],
            ['field' => 'vod_pic_screenshot', 'name' => '视频截图'],
            ['field' => 'vod_play_url', 'name' => '播放地址'],
            ['field' => 'vod_play_from', 'name' => '播放来源'],
            ['field' => 'vod_play_server', 'name' => '播放服务器'],
            ['field' => 'vod_down_url', 'name' => '下载地址'],
            ['field' => 'vod_down_from', 'name' => '下载来源'],
            ['field' => 'vod_down_server', 'name' => '下载服务器'],
            ['field' => 'vod_content', 'name' => '视频简介内容'],
            ['field' => 'vod_blurb', 'name' => '视频简介摘要'],
        ];
        
        // 文章表常用字段
        $art_fields = [
            ['field' => 'art_pic', 'name' => '文章封面图'],
            ['field' => 'art_pic_thumb', 'name' => '文章缩略图'],
            ['field' => 'art_pic_slide', 'name' => '文章轮播图'],
            ['field' => 'art_pic_screenshot', 'name' => '文章截图'],
            ['field' => 'art_content', 'name' => '文章内容'],
            ['field' => 'art_blurb', 'name' => '文章简介'],
        ];
        
        // 演员表字段
        $actor_fields = [
            ['field' => 'actor_pic', 'name' => '演员图片'],
            ['field' => 'actor_pic_thumb', 'name' => '演员缩略图'],
            ['field' => 'actor_content', 'name' => '演员简介'],
        ];
        
        // 角色表字段
        $role_fields = [
            ['field' => 'role_pic', 'name' => '角色图片'],
            ['field' => 'role_content', 'name' => '角色简介'],
        ];
        
        // 网址表字段
        $website_fields = [
            ['field' => 'website_pic', 'name' => '网址图片'],
            ['field' => 'website_content', 'name' => '网址简介'],
        ];
        
        $this->assign('list', $list);
        $this->assign('vod_fields', $vod_fields);
        $this->assign('art_fields', $art_fields);
        $this->assign('actor_fields', $actor_fields);
        $this->assign('role_fields', $role_fields);
        $this->assign('website_fields', $website_fields);
        $this->assign('title', '批量数据替换(优化版)');
        return $this->fetch('admin@database/rep_plus');
    }

  
    public function queryRepData()
    {
        if($this->request->isPost()){
            $param = input();
            $table = $param['table'] ?? '';
            $field = $param['field'] ?? '';
            $findstr = $param['findstr'] ?? '';
            $page = isset($param['page']) ? intval($param['page']) : 1;
            $limit = 50; // 每页显示50条
            
            if(empty($table) || empty($field) || empty($findstr)){
                return json(['code' => 0, 'msg' => '参数不完整']);
            }
            
           
            if (!$this->isValidTable($table)) {
                return json(['code' => 0, 'msg' => '数据表无效']);
            }
            
            try {
               
                $count_sql = "SELECT COUNT(*) as total FROM `".$table."` WHERE `".$field."` LIKE ?";
                $count_result = Db::query($count_sql, ['%'.$findstr.'%']);
                $total = $count_result[0]['total'] ?? 0;
                
                if($total == 0){
                    return json([
                        'code' => 1,
                        'msg' => '未找到匹配的数据',
                        'data' => [
                            'total' => 0,
                            'list' => [],
                            'page' => $page,
                            'page_size' => $limit,
                            'total_page' => 0
                        ]
                    ]);
                }
                
              
                $offset = ($page - 1) * $limit;
                
                
                $pk_field = $this->getTablePrimaryKey($table);
                
               
                $name_field = $this->getTableNameField($table);
                
               
                if($name_field && !$this->checkFieldExists($table, $name_field)){
                    $name_field = null;
                }
                
              
                if($name_field && $name_field != $field && $name_field != $pk_field){
                  
                    $query_sql = "SELECT `".$pk_field."`, `".$name_field."`, `".$field."` FROM `".$table."` WHERE `".$field."` LIKE ? LIMIT ?, ?";
                } else {
                   
                    $query_sql = "SELECT `".$pk_field."`, `".$field."` FROM `".$table."` WHERE `".$field."` LIKE ? LIMIT ?, ?";
                }
                $list = Db::query($query_sql, ['%'.$findstr.'%', $offset, $limit]);
                
                return json([
                    'code' => 1,
                    'msg' => '查询成功',
                    'data' => [
                        'total' => $total,
                        'list' => $list,
                        'page' => $page,
                        'page_size' => $limit,
                        'total_page' => ceil($total / $limit),
                        'field' => $field,
                        'pk_field' => $pk_field,
                        'name_field' => $name_field 
                    ]
                ]);
                
            } catch (\Exception $e) {
                return json(['code' => 0, 'msg' => '查询失败:'.$e->getMessage()]);
            }
        }
        
        return json(['code' => 0, 'msg' => '非法请求']);
    }
    
    
    private function getTablePrimaryKey($table)
    {
        try {
            $columns = Db::query("SHOW COLUMNS FROM `".$table."`");
            foreach($columns as $column){
                if($column['Key'] == 'PRI'){
                    return $column['Field'];
                }
            }
           
            return $columns[0]['Field'] ?? 'id';
        } catch (\Exception $e) {
            return 'id';
        }
    }
    
   
    private function checkFieldExists($table, $field)
    {
        try {
            $columns = Db::query("SHOW COLUMNS FROM `".$table."`");
            foreach($columns as $column){
                if($column['Field'] == $field){
                    return true;
                }
            }
            return false;
        } catch (\Exception $e) {
            return false;
        }
    }
    
    
    private function getTableNameField($table)
    {
        try {
            
            if(strpos($table, 'vod') !== false){
                return 'vod_name'; 
            }
            else if(strpos($table, 'art') !== false){
                return 'art_title';  
            }
            else if(strpos($table, 'actor') !== false){
                return 'actor_name';  
            }
            else if(strpos($table, 'role') !== false){
                return 'role_name';  
            }
            else if(strpos($table, 'website') !== false){
                return 'website_name';  
            }
            else if(strpos($table, 'topic') !== false){
                return 'topic_name';  
            }
            else if(strpos($table, 'type') !== false){
                return 'type_name';  
            }
            else if(strpos($table, 'user') !== false){
                return 'user_name';  
            }
            else if(strpos($table, 'comment') !== false){
                return 'comment_content'; 
            }
            else if(strpos($table, 'gbook') !== false){
                return 'gbook_content';  
            }
            
            
            $columns = Db::query("SHOW COLUMNS FROM `".$table."`");
            $column_names = array_column($columns, 'Field');
            
            // 优先级顺序:name > title > content
            $possible_name_fields = ['name', 'title', 'content'];
            foreach($possible_name_fields as $field_suffix){
                foreach($column_names as $col){
                    if(strpos($col, $field_suffix) !== false){
                        return $col;
                    }
                }
            }
            
            return null;  
        } catch (\Exception $e) {
            return null;
        }
    }

   
    private function saveRepLog($data)
    {
        $log_file = APP_PATH . 'data/backup/rep_log.txt';
        $log_dir = dirname($log_file);
        
        
        if (!is_dir($log_dir)) {
            Dir::create($log_dir, 0755, true);
        }
        
       
        $log_data = [
            'time' => date('Y-m-d H:i:s'),
            'admin' => $this->_admin['admin_name'] ?? 'system',
            'admin_id' => $this->_admin['admin_id'] ?? 0,
            'ip' => request()->ip(),
            'table' => $data['table_name'],
            'field' => $data['field_name'],
            'find' => mb_substr($data['find_str'], 0, 200), 
            'replace' => mb_substr($data['replace_str'], 0, 200),
            'where' => $data['where_condition'],
            'rows' => $data['affected_rows'],
        ];
        
       
        $log_line = json_encode($log_data, JSON_UNESCAPED_UNICODE) . "\n";
        file_put_contents($log_file, $log_line, FILE_APPEND | LOCK_EX);
    }

   
    public function repLog()
    {
        $param = input();
        $page = isset($param['page']) ? intval($param['page']) : 1;
        $limit = 20;
        $keyword = isset($param['keyword']) ? trim($param['keyword']) : '';
        $date_start = isset($param['date_start']) ? trim($param['date_start']) : '';
        $date_end = isset($param['date_end']) ? trim($param['date_end']) : '';
        
        $log_file = APP_PATH . 'data/backup/rep_log.txt';
        $list = [];
        $total = 0;
        
        if (file_exists($log_file)) {
            // 读取所有记录(倒序)
            $lines = file($log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            $lines = array_reverse($lines); 
            
            $all_logs = [];
            foreach ($lines as $line) {
                $log = json_decode($line, true);
                if ($log) {
                   
                    if (!empty($keyword)) {
                        $search_fields = [
                            $log['table'], 
                            $log['field'], 
                            $log['find'], 
                            $log['replace'], 
                            $log['admin']
                        ];
                        $match = false;
                        foreach ($search_fields as $field) {
                            if (stripos($field, $keyword) !== false) {
                                $match = true;
                                break;
                            }
                        }
                        if (!$match) continue;
                    }
                    
                  
                    if (!empty($date_start) && $log['time'] < $date_start.' 00:00:00') {
                        continue;
                    }
                    if (!empty($date_end) && $log['time'] > $date_end.' 23:59:59') {
                        continue;
                    }
                    
                    $all_logs[] = $log;
                }
            }
            
            $total = count($all_logs);
            $list = array_slice($all_logs, ($page - 1) * $limit, $limit);
        }
        
        $pagecount = ceil($total / $limit);
        
        $this->assign('list', $list);
        $this->assign('total', $total);
        $this->assign('page', $page);
        $this->assign('limit', $limit);
        $this->assign('pagecount', $pagecount);
        $this->assign('keyword', $keyword);
        $this->assign('date_start', $date_start);
        $this->assign('date_end', $date_end);
        $this->assign('title', '批量替换操作记录');
        return $this->fetch('admin@database/rep_log');
    }

  
    public function clearRepLog()
    {
        $log_file = APP_PATH . 'data/backup/rep_log.txt';
        if (file_exists($log_file)) {
            
            $backup_file = APP_PATH . 'data/backup/rep_log_backup_' . date('YmdHis') . '.txt';
            copy($log_file, $backup_file);
            
            
            file_put_contents($log_file, '');
            return $this->success('已清空记录,备份文件:' . basename($backup_file));
        }
        return $this->error('日志文件不存在');
    }
}

步骤3.在“application/admin/view/database”文件夹增加文件“rep_plus.html”复制下面代码放入文件

{include file="../../../application/admin/view/public/head" /}
<style>
    .layui-form-select ul {max-height:300px}
    .layui-btn+.layui-btn{margin-left:0px; }
    .field-group {
        display: none;
        padding: 10px;
        background: #f8f8f8;
        border-radius: 4px;
        margin-top: 10px;
    }
    .field-btn {
        margin: 5px 5px 5px 0;
    }
    .tips-box {
        background: #ecf5ff;
        padding: 15px;
        border-left: 4px solid #409eff;
        margin-bottom: 20px;
        border-radius: 4px;
    }
    .tips-box h4 {
        margin: 0 0 10px 0;
        color: #409eff;
    }
    .tips-box p {
        margin: 5px 0;
        font-size: 13px;
        color: #606266;
    }
</style>
<div class="page-container">
    <form class="layui-form layui-form-pane" action="">
        <input type="hidden" name="__token__" value="{$Request.token}" />
        <div class="layui-tab">
            <ul class="layui-tab-title">
                <li class="layui-this">批量数据替换(优化版)</li>
                <li style="float:right;">
                    <a href="{:url('database/repLog')}" class="layui-btn layui-btn-sm layui-btn-normal" style="margin-top: 3px;">
                        <i class="layui-icon layui-icon-file"></i> 查看替换记录
                    </a>
                </li>
            </ul>
            <div class="layui-tab-content">
                <div class="layui-tab-item layui-show">
                    
                    <!-- 操作提示 -->
                    <div class="tips-box">
                        <h4>💡 使用说明</h4>
                        <p>1. 选择要操作的数据表(视频表、文章表、演员表等)</p>
                        <p>2. 选择要替换的字段(系统已预设常用字段,支持中文显示)</p>
                        <p>3. 输入要查找的内容和替换后的内容</p>
                        <p>4. 可选:添加WHERE条件限制替换范围(如:AND vod_id > 100)</p>
                        <p style="color: #e6a23c;">⚠️ 重要:替换操作不可逆,建议先备份数据库!</p>
                    </div>

                    
                    <div class="layui-form-item">
                        <label class="layui-form-label"><span style="color:red">*</span> 选择数据表:</label>
                        <div class="layui-input-inline w400" >
                            <select name="table" lay-filter="table" lay-verify="required">
                                <option value="">请选择数据表</option>
                                {volist name="list" id="vo"}
                                    <option value="{$vo.Name}" {if $vo.Name == $Request.param.prefix.'vod'}selected{/if}>{$vo.Name}{notempty name="vo.Comment"}【{$vo.Comment}】{/notempty}</option>
                                {/volist}
                            </select>
                        </div>
                        <div class="layui-form-mid layui-word-aux">选择要批量替换的数据表(视频表:mac_vod)(文章表:mac_art)</div>
                    </div>

                   
                    <div class="field-group" id="vod-fields">
                        <label class="layui-form-label">常用字段:</label>
                        <div class="layui-input-block">
                            {volist name="vod_fields" id="field"}
                                <button type="button" class="layui-btn layui-btn-sm layui-btn-normal field-btn" onclick="setField('{$field.field}')">{$field.name}</button>
                            {/volist}
                        </div>
                    </div>

                    
                    <div class="field-group" id="art-fields">
                        <label class="layui-form-label">常用字段:</label>
                        <div class="layui-input-block">
                            {volist name="art_fields" id="field"}
                                <button type="button" class="layui-btn layui-btn-sm layui-btn-normal field-btn" onclick="setField('{$field.field}')">{$field.name}</button>
                            {/volist}
                        </div>
                    </div>

                    <!-- 演员表字段 -->
                    <div class="field-group" id="actor-fields">
                        <label class="layui-form-label">常用字段:</label>
                        <div class="layui-input-block">
                            {volist name="actor_fields" id="field"}
                                <button type="button" class="layui-btn layui-btn-sm layui-btn-normal field-btn" onclick="setField('{$field.field}')">{$field.name}</button>
                            {/volist}
                        </div>
                    </div>

                   
                    <div class="field-group" id="role-fields">
                        <label class="layui-form-label">常用字段:</label>
                        <div class="layui-input-block">
                            {volist name="role_fields" id="field"}
                                <button type="button" class="layui-btn layui-btn-sm layui-btn-normal field-btn" onclick="setField('{$field.field}')">{$field.name}</button>
                            {/volist}
                        </div>
                    </div>

                    
                    <div class="field-group" id="website-fields">
                        <label class="layui-form-label">常用字段:</label>
                        <div class="layui-input-block">
                            {volist name="website_fields" id="field"}
                                <button type="button" class="layui-btn layui-btn-sm layui-btn-normal field-btn" onclick="setField('{$field.field}')">{$field.name}</button>
                            {/volist}
                        </div>
                    </div>

                   
                    <div class="layui-form-item">
                        <label class="layui-form-label"><span style="color:red">*</span> 字段名:</label>
                        <div class="layui-input-block" >
                            <input type="text" id="field" name="field" placeholder="请选择上方字段或手动输入字段名" lay-verify="required" class="layui-input">
                        </div>
                    </div>

                   
                    <div class="layui-form-item">
                        <label class="layui-form-label"><span style="color:red">*</span> 查找内容:</label>
                        <div class="layui-input-block" >
                            <textarea name="findstr" placeholder="输入要查找的内容,如:http://old.com" lay-verify="required" class="layui-textarea" rows="3"></textarea>
                        </div>
                    </div>

                  
                    <div class="layui-form-item">
                        <label class="layui-form-label"><span style="color:red">*</span> 替换为:</label>
                        <div class="layui-input-block" >
                            <textarea name="tostr" placeholder="输入替换后的内容,如:https://new.com(可留空)" class="layui-textarea" rows="3"></textarea>
                        </div>
                    </div>

                    <!-- WHERE条件 -->
                    <div class="layui-form-item">
                        <label class="layui-form-label">WHERE条件:</label>
                        <div class="layui-input-block" >
                            <input type="text" name="where" placeholder="可选,如:AND vod_id > 100 AND vod_id < 200" value="" class="layui-input">
                            <div class="layui-form-mid layui-word-aux" style="margin-top:5px;">限制替换范围,留空则替换所有匹配数据</div>
                        </div>
                    </div>

                   
                    <div class="layui-form-item">
                        <label class="layui-form-label">使用示例:</label>
                        <div class="layui-input-block">
                            <div style="background:#fff3cd;padding:10px;border-radius:4px;font-size:12px;color:#856404;">
                                <p><strong>示例1:</strong>替换视频封面图的域名</p>
                                <p>字段名:vod_pic  |  查找:http://old.com  |  替换:https://new.com</p>
                                <hr style="margin:10px 0;border:none;border-top:1px solid #ddd;">
                                <p><strong>示例2:</strong>替换播放地址中的链接</p>
                                <p>字段名:vod_play_url  |  查找:https://player1.com  |  替换:https://player2.com</p>
                                <hr style="margin:10px 0;border:none;border-top:1px solid #ddd;">
                                <p><strong>示例3:</strong>只替换ID大于100的视频</p>
                                <p>字段名:vod_pic  |  查找:http://  |  替换:https://  |  WHERE:AND vod_id > 100</p>
                            </div>
                        </div>
                    </div>

                </div>
            </div>
        </div>
        
       
        <div class="layui-form-item center">
            <div class="layui-input-block">
                <button type="button" class="layui-btn layui-btn-normal layui-btn-lg" id="queryBtn">
                    <i class="layui-icon layui-icon-search"></i> 查询数据
                </button>
                <button type="submit" class="layui-btn layui-btn-lg" lay-submit="" lay-filter="formSubmit">
                    <i class="layui-icon layui-icon-ok"></i> 执行替换
                </button>
                <button class="layui-btn layui-btn-warm layui-btn-lg" type="reset">
                    <i class="layui-icon layui-icon-refresh"></i> 重置
                </button>
            </div>
        </div>
    </form>
    
   
    <div id="queryResultBox" style="display:none; margin-top:30px; padding:20px; background:#fff; border-radius:8px; box-shadow:0 2px 12px rgba(0,0,0,0.1);">
        <div style="border-bottom:2px solid #409eff; padding-bottom:10px; margin-bottom:20px;">
            <h3 style="margin:0; color:#409eff;">
                <i class="layui-icon layui-icon-search" style="font-size:20px;"></i> 查询结果
            </h3>
        </div>
        
       
        <div id="queryStats" style="padding:15px; background:#ecf5ff; border-radius:4px; margin-bottom:20px;"></div>
        
       
        <div style="overflow-x:auto;">
            <table class="layui-table" lay-skin="line">
                <thead>
                    <tr id="tableHeader">
                       
                    </tr>
                </thead>
                <tbody id="tableBody">
                   
                </tbody>
            </table>
        </div>
        
        <!-- 分页 -->
        <div id="queryPagination" style="text-align:center; margin-top:20px;"></div>
    </div>
</div>

{include file="../../../application/admin/view/public/foot" /}
<script type="text/javascript">
    layui.use(['form', 'layer'], function(){
        var form = layui.form,
            layer = layui.layer,
            $ = layui.jquery;

       
        form.on('select(table)', function(data){
         
            $('.field-group').hide();
            
            var tableName = data.value;
            if(tableName != ''){
                var prefix = '{$Request.param.prefix}';
                
               
                if(tableName.indexOf('vod') !== -1){
                    $('#vod-fields').show();
                }
                else if(tableName.indexOf('art') !== -1){
                    $('#art-fields').show();
                }
                else if(tableName.indexOf('actor') !== -1){
                    $('#actor-fields').show();
                }
                else if(tableName.indexOf('role') !== -1){
                    $('#role-fields').show();
                }
                else if(tableName.indexOf('website') !== -1){
                    $('#website-fields').show();
                }
            }
        });

       
        form.on('submit(formSubmit)', function(data){
            var field = data.field;
            
           
            layer.confirm('确定要执行批量替换操作吗?<br><br>表:' + field.table + '<br>字段:' + field.field + '<br>查找:' + field.findstr + '<br>替换:' + field.tostr + '<br><br><span style="color:red;">此操作不可逆,请确认已备份数据!</span>', {
                icon: 3,
                title: '操作确认',
                btn: ['确定执行', '我再想想']
            }, function(index){
                
                layer.close(index);
                
               
                var loadIndex = layer.load(1, {shade: [0.3, '#fff']});
                
                
                $.post("{:url('database/repPlus')}", data.field, function(res) {
                    layer.close(loadIndex);
                    if (res.code == 1) {
                        layer.msg(res.msg, {icon: 1, time: 2000}, function(){
                            location.reload();
                        });
                    } else {
                        layer.msg(res.msg, {icon: 2});
                    }
                });
                
                return false;
            });
            
            return false;
        });

        
        var defaultTable = $('select[name="table"]').val();
        if(defaultTable){
            if(defaultTable.indexOf('vod') !== -1){
                $('#vod-fields').show();
            }
        }
        
       
        $('#queryBtn').on('click', function(){
            var table = $('select[name="table"]').val();
            var field = $('input[name="field"]').val();
            var findstr = $('textarea[name="findstr"]').val();
            
           
            if(!table){
                layer.msg('请选择数据表', {icon: 2});
                return;
            }
            if(!field){
                layer.msg('请输入字段名', {icon: 2});
                return;
            }
            if(!findstr){
                layer.msg('请输入查找内容', {icon: 2});
                return;
            }
            
           
            queryData(table, field, findstr, 1);
        });
    });

   
    function setField(fieldName){
        $('#field').val(fieldName);
    }
    
    
    var currentQueryParams = {}; 
    
    function queryData(table, field, findstr, page){
        var loadIndex = layer.load(1, {shade: [0.3, '#fff']});
        
       
        currentQueryParams = {
            table: table,
            field: field,
            findstr: findstr
        };
        
        $.post("{:url('database/queryRepData')}", {
            table: table,
            field: field,
            findstr: findstr,
            page: page
        }, function(res){
            layer.close(loadIndex);
            
            if(res.code == 1){
                if(res.data.total == 0){
                    layer.msg('未找到匹配的数据', {icon: 2});
                    $('#queryResultBox').hide();
                    return;
                }
                
               
                displayQueryResult(res.data);
                
               
                $('html, body').animate({
                    scrollTop: $('#queryResultBox').offset().top - 50
                }, 500);
            } else {
                layer.msg(res.msg, {icon: 2});
            }
        }).fail(function(){
            layer.close(loadIndex);
            layer.msg('查询失败,请重试', {icon: 2});
        });
    }
    
    
    function displayQueryResult(data){
       
        var statsHtml = '<p style="margin:5px 0; font-size:14px; color:#606266;">' +
                       '📊 共找到 <strong style="color:#f56c6c; font-size:18px;">' + data.total + '</strong> 条匹配数据' +
                       '</p>' +
                       '<p style="margin:5px 0; font-size:13px; color:#909399;">' +
                       '数据表:<strong>' + currentQueryParams.table + '</strong> | ' +
                       '字段:<strong>' + data.field + '</strong> | ' +
                       '查找内容:<strong>' + currentQueryParams.findstr + '</strong>' +
                       '</p>';
        $('#queryStats').html(statsHtml);
        
        
        var headerHtml = '<th style="width:80px;">序号</th>' +
                        '<th style="width:100px;">' + data.pk_field + '</th>';
        
       
        var hasNameField = data.name_field && data.name_field != data.field && data.name_field != data.pk_field;
        if(hasNameField){
            headerHtml += '<th style="min-width:150px;">' + data.name_field + '</th>';
        }
        
        headerHtml += '<th>' + data.field + '</th>';
        $('#tableHeader').html(headerHtml);
        
        
        var bodyHtml = '';
        var startIndex = (data.page - 1) * data.page_size;
        
        $.each(data.list, function(index, item){
            var rowNum = startIndex + index + 1;
            var pkValue = item[data.pk_field];
            var fieldValue = item[data.field] || '';
            
            
            var highlightedValue = highlightKeyword(fieldValue, currentQueryParams.findstr);
            
            bodyHtml += '<tr>' +
                       '<td style="text-align:center;">' + rowNum + '</td>' +
                       '<td style="text-align:center;">' + pkValue + '</td>';
            
           
            if(hasNameField){
                var nameValue = item[data.name_field] || '-';
                
                if(nameValue.length > 50){
                    nameValue = nameValue.substring(0, 50) + '...';
                }
                bodyHtml += '<td style="word-break:break-all;">' + nameValue + '</td>';
            }
            
            
            var displayValue = formatFieldValue(fieldValue, data.field, currentQueryParams.findstr);
            
            bodyHtml += '<td style="word-break:break-all;">' + displayValue + '</td>' +
                       '</tr>';
        });
        
        $('#tableBody').html(bodyHtml);
        
        
        if(data.total_page > 1){
            var paginationHtml = '';
            
           
            if(data.page > 1){
                paginationHtml += '<button class="layui-btn layui-btn-sm" onclick="queryData(\'' + 
                                 currentQueryParams.table + '\', \'' + 
                                 currentQueryParams.field + '\', \'' + 
                                 currentQueryParams.findstr + '\', ' + 
                                 (data.page - 1) + ')">上一页</button> ';
            }
            
           
            paginationHtml += '<span style="margin:0 10px; color:#909399;">第 <strong>' + 
                             data.page + '</strong> / ' + data.total_page + ' 页</span>';
            
           
            if(data.page < data.total_page){
                paginationHtml += ' <button class="layui-btn layui-btn-sm" onclick="queryData(\'' + 
                                 currentQueryParams.table + '\', \'' + 
                                 currentQueryParams.field + '\', \'' + 
                                 currentQueryParams.findstr + '\', ' + 
                                 (data.page + 1) + ')">下一页</button>';
            }
            
            $('#queryPagination').html(paginationHtml);
        } else {
            $('#queryPagination').html('');
        }
        
        
        $('#queryResultBox').show();
    }
    
    
    function formatFieldValue(value, fieldName, keyword){
        if(!value){
            return '-';
        }
        
        var originalValue = value;
        var maxLength = 200; 
        
       
        if(fieldName.indexOf('content') !== -1 || fieldName.indexOf('text') !== -1){
            
            maxLength = 200;
            value = stripHtmlTags(value);
        }
        else if(fieldName.indexOf('pic') !== -1 || fieldName.indexOf('url') !== -1 || 
                fieldName.indexOf('link') !== -1 || fieldName.indexOf('http') !== -1){
           
            maxLength = 300;
        }
        else if(fieldName.indexOf('remarks') !== -1 || fieldName.indexOf('blurb') !== -1 || 
                fieldName.indexOf('desc') !== -1){
           
            maxLength = 150;
            value = stripHtmlTags(value);
        }
        else {
            
            maxLength = 100;
        }
        
        
        if(value.length > maxLength){
          
            if(keyword){
                var keywordPos = value.toLowerCase().indexOf(keyword.toLowerCase());
                if(keywordPos !== -1){
                    
                    var start = Math.max(0, keywordPos - 50);
                    var end = Math.min(value.length, keywordPos + keyword.length + 150);
                    
                    var excerpt = value.substring(start, end);
                    if(start > 0) excerpt = '...' + excerpt;
                    if(end < value.length) excerpt = excerpt + '...';
                    
                    value = excerpt;
                } else {
                   
                    value = value.substring(0, maxLength) + '...';
                }
            } else {
                value = value.substring(0, maxLength) + '...';
            }
        }
        
        
        value = highlightKeyword(value, keyword);
        
       
        if(originalValue.length > maxLength){
            value += '<br><a href="javascript:void(0);" onclick="showFullContent(\'' + 
                     escapeHtml(originalValue) + '\')" style="color:#409eff; font-size:12px;">' +
                     '[查看完整内容 ' + originalValue.length + ' 字符]</a>';
        }
        
        return value;
    }
    
   
    function stripHtmlTags(html){
        if(!html) return '';
        
     
        var tmp = document.createElement('div');
        tmp.innerHTML = html;
        
      
        var text = tmp.textContent || tmp.innerText || '';
        
      
        text = text.replace(/\s+/g, ' ').trim();
        
        return text;
    }
    
   
    function escapeHtml(text){
        if(!text) return '';
        
        return text.replace(/'/g, '&#39;')
                   .replace(/"/g, '&quot;')
                   .replace(/</g, '&lt;')
                   .replace(/>/g, '&gt;');
    }
    
   
    function showFullContent(content){
       
        content = content.replace(/&#39;/g, "'")
                        .replace(/&quot;/g, '"')
                        .replace(/&lt;/g, '<')
                        .replace(/&gt;/g, '>');
        
        layer.open({
            type: 1,
            title: '完整内容',
            area: ['800px', '600px'],
            content: '<div style="padding:20px; max-height:520px; overflow-y:auto; word-break:break-all;">' + 
                    '<pre style="white-space:pre-wrap; font-family:inherit;">' + content + '</pre>' +
                    '</div>',
            btn: ['复制内容', '关闭'],
            yes: function(index, layero){
                // 复制内容到剪贴板
                var textarea = document.createElement('textarea');
                textarea.value = content;
                document.body.appendChild(textarea);
                textarea.select();
                try {
                    document.execCommand('copy');
                    layer.msg('内容已复制到剪贴板', {icon: 1});
                } catch (err) {
                    layer.msg('复制失败,请手动复制', {icon: 2});
                }
                document.body.removeChild(textarea);
            }
        });
    }
    
   
    function highlightKeyword(text, keyword){
        if(!text || !keyword){
            return text;
        }
        
        
        var escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        var regex = new RegExp(escapedKeyword, 'gi');
        
        return text.replace(regex, function(match){
            return '<span style="background:#ffeb3b; color:#000; padding:2px 4px; border-radius:2px;">' + match + '</span>';
        });
    }
</script>

</body>
</html>

步骤4.找到文件夹“application/admin/view/database”在文件夹内增加文件“rep_log.html”把下面代码复制到文件内

{include file="../../../application/admin/view/public/head" /}
<style>
    .search-box {
        background: #fff;
        padding: 15px;
        margin-bottom: 15px;
        border-radius: 4px;
        box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .layui-table td {
        word-break: break-all;
    }
    .log-detail {
        font-size: 12px;
        color: #666;
        line-height: 1.6;
    }
    .log-content {
        max-width: 300px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .badge {
        display: inline-block;
        padding: 2px 8px;
        border-radius: 3px;
        font-size: 12px;
        background: #409eff;
        color: #fff;
    }
    .stats-box {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: #fff;
        padding: 15px 20px;
        border-radius: 4px;
        margin-bottom: 15px;
    }
    .stats-box h3 {
        margin: 0;
        font-size: 24px;
    }
</style>

<div class="page-container p15">
    
   
    <div class="stats-box">
        <h3><i class="layui-icon layui-icon-chart"></i> 共 {$total} 条替换记录</h3>
        <p style="margin: 5px 0 0 0; opacity: 0.9;">记录文件:application/data/backup/rep_log.txt</p>
    </div>

    
    <div class="search-box">
        <form class="layui-form" action="{:url('database/repLog')}" method="get">
            <div class="layui-form-item" style="margin-bottom: 0;">
                <div class="layui-inline">
                    <label class="layui-form-label" style="width: 80px;">关键词:</label>
                    <div class="layui-input-inline" style="width: 200px;">
                        <input type="text" name="keyword" value="{$keyword}" placeholder="搜索表名/字段/内容/操作人" class="layui-input">
                    </div>
                </div>
                <div class="layui-inline">
                    <label class="layui-form-label" style="width: 80px;">开始日期:</label>
                    <div class="layui-input-inline" style="width: 150px;">
                        <input type="text" name="date_start" id="date_start" value="{$date_start}" placeholder="开始日期" class="layui-input">
                    </div>
                </div>
                <div class="layui-inline">
                    <label class="layui-form-label" style="width: 80px;">结束日期:</label>
                    <div class="layui-input-inline" style="width: 150px;">
                        <input type="text" name="date_end" id="date_end" value="{$date_end}" placeholder="结束日期" class="layui-input">
                    </div>
                </div>
                <div class="layui-inline">
                    <button class="layui-btn layui-btn-normal" type="submit">
                        <i class="layui-icon layui-icon-search"></i> 搜索
                    </button>
                    <a href="{:url('database/repLog')}" class="layui-btn layui-btn-primary">
                        <i class="layui-icon layui-icon-refresh"></i> 重置
                    </a>
                    <button type="button" class="layui-btn layui-btn-danger" onclick="clearLog()">
                        <i class="layui-icon layui-icon-delete"></i> 清空记录
                    </button>
                </div>
            </div>
        </form>
    </div>

    
    <div style="background: #fff; padding: 15px; border-radius: 4px;">
        {empty name="list"}
        <div style="text-align: center; padding: 50px 0; color: #999;">
            <i class="layui-icon layui-icon-file" style="font-size: 60px;"></i>
            <p style="margin-top: 10px;">暂无替换记录</p>
        </div>
        {else/}
        <table class="layui-table" lay-skin="line">
            <thead>
                <tr>
                    <th width="160">操作时间</th>
                    <th width="100">操作人</th>
                    <th width="150">数据表</th>
                    <th width="120">字段</th>
                    <th>查找内容</th>
                    <th>替换内容</th>
                    <th width="100">影响行数</th>
                    <th width="120">操作IP</th>
                </tr>
            </thead>
            <tbody>
                {volist name="list" id="vo"}
                <tr>
                    <td>{$vo.time}</td>
                    <td>
                        <span class="badge">{$vo.admin}</span>
                    </td>
                    <td>
                        <strong style="color: #409eff;">{$vo.table}</strong>
                    </td>
                    <td>
                        <code style="background: #f4f4f5; padding: 2px 6px; border-radius: 3px;">{$vo.field}</code>
                    </td>
                    <td>
                        <div class="log-content" title="{$vo.find}">
                            {$vo.find|default='(空)'}
                        </div>
                    </td>
                    <td>
                        <div class="log-content" title="{$vo.replace}">
                            {$vo.replace|default='(空)'}
                        </div>
                    </td>
                    <td>
                        <span style="color: #67c23a; font-weight: bold;">{$vo.rows}</span> 条
                    </td>
                    <td>{$vo.ip}</td>
                </tr>
                {if !empty($vo.where)}
                <tr style="background: #fafafa;">
                    <td colspan="8">
                        <div class="log-detail">
                            <strong>WHERE条件:</strong> {$vo.where|default='无'}
                        </div>
                    </td>
                </tr>
                {/if}
                {/volist}
            </tbody>
        </table>

       
        {if $pagecount > 1}
        <div id="page" style="text-align: right; padding-top: 10px;"></div>
        {/if}
        {/empty}
    </div>
</div>

{include file="../../../application/admin/view/public/foot" /}
<script type="text/javascript">
    layui.use(['laydate', 'laypage', 'layer'], function(){
        var laydate = layui.laydate,
            laypage = layui.laypage,
            layer = layui.layer,
            $ = layui.jquery;

        
        laydate.render({
            elem: '#date_start',
            type: 'date'
        });
        laydate.render({
            elem: '#date_end',
            type: 'date'
        });

        // 分页
        {if $pagecount > 1}
        laypage.render({
            elem: 'page',
            count: {$total},
            limit: {$limit},
            curr: {$page},
            layout: ['count', 'prev', 'page', 'next', 'skip'],
            jump: function(obj, first){
                if(!first){
                    var url = '{:url("database/repLog")}?page=' + obj.curr;
                    {if !empty($keyword)}
                    url += '&keyword={$keyword}';
                    {/if}
                    {if !empty($date_start)}
                    url += '&date_start={$date_start}';
                    {/if}
                    {if !empty($date_end)}
                    url += '&date_end={$date_end}';
                    {/if}
                    location.href = url;
                }
            }
        });
        {/if}
    });

    
    function clearLog() {
        layui.layer.confirm('确定要清空所有替换记录吗?<br><br><span style="color:#ff5722;">原记录将被备份,不会丢失</span>', {
            icon: 3,
            title: '清空确认',
            btn: ['确定清空', '取消']
        }, function(index){
            layui.layer.close(index);
            
            var loadIndex = layui.layer.load(1);
            layui.jquery.post("{:url('database/clearRepLog')}", {}, function(res) {
                layui.layer.close(loadIndex);
                if (res.code == 1) {
                    layui.layer.msg(res.msg, {icon: 1}, function(){
                        location.reload();
                    });
                } else {
                    layui.layer.msg(res.msg, {icon: 2});
                }
            });
        });
    }
</script>

</body>
</html>

最后在苹果cms10后台的“数据库”中的批量替换(优化版)使用

版权声明:图图 发表于 2025-11-20 22:48:47。
转载请注明:苹果cms批量数据替换优化版 | 站长资源站Pro|站长资源|建站教程

暂无评论

暂无评论...