作者 SHW\戥岁。。

商品详情图

{"files":["application\\admin\\controller\\Fastexport.php","application\\admin\\lang\\zh-cn\\fastexport.php","application\\admin\\model\\FastExport.php","application\\admin\\validate\\FastExport.php","application\\admin\\view\\fastexport\\add.html","application\\admin\\view\\fastexport\\edit.html","application\\admin\\view\\fastexport\\index.html","application\\admin\\view\\fastexport\\task_control.html","public\\assets\\js\\backend\\fastexport.js"],"license":"regular","licenseto":"10789","licensekey":"5zU6gFTMYsy74BpW X+r0pHo98zs53kEGZFCXBsR04a3rqg1u45AUojKPd50=","domains":[],"licensecodes":[],"validations":[],"menus":["fastexport","fastexport\/index","fastexport\/add","fastexport\/edit","fastexport\/del"]}
\ No newline at end of file
... ...
<?php
namespace addons\fastexport;
use app\common\library\Menu;
use think\Addons;
/**
* 插件
*/
class Fastexport extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
$menu = [
[
'name' => 'fastexport',
'title' => '数据导出管理',
'icon' => 'fa fa-database',
'remark' => '上次的文件包和上次执行进度,仅在大量数据导出时,才会记录;普通任务,执行时直接生成Excel文件下载',
'sublist' => [
['name' => 'fastexport/index', 'title' => '查看'],
['name' => 'fastexport/add', 'title' => '添加'],
['name' => 'fastexport/edit', 'title' => '编辑'],
['name' => 'fastexport/del', 'title' => '删除']
]
]
];
Menu::create($menu);
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
Menu::delete('fastexport');
return true;
}
/**
* 插件启用方法
* @return bool
*/
public function enable()
{
Menu::enable('fastexport');
return true;
}
/**
* 插件禁用方法
* @return bool
*/
public function disable()
{
Menu::disable('fastexport');
return true;
}
}
... ...
<?php
return [];
\ No newline at end of file
... ...
<?php
namespace addons\fastexport\controller;
use think\addons\Controller;
class Index extends Controller
{
public function index()
{
$this->error("当前插件暂无前台页面");
}
}
... ...
name = fastexport
title = 数据导出增强
intro = 支持任意表、任意字段,关联查询、字段识别/数据筛选,支持并发创建xls,实测百万级数据导出
author = 白衣素袖
website = https://www.fastadmin.net
version = 1.0.1
state = 1
url = /addons/fastexport
license = regular
licenseto = 10789
... ...
-- ---------------------------
-- 数据导出任务表
-- ---------------------------
CREATE TABLE IF NOT EXISTS `__PREFIX__fastexport` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '导出人',
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '任务名称',
`main_table` varchar(50) NOT NULL DEFAULT '' COMMENT '数据源表',
`field_config` text COMMENT '字段配置',
`join_table` text COMMENT '关联表配置',
`where_field` text COMMENT '筛选规则',
`order_field` varchar(50) NOT NULL DEFAULT '' COMMENT '排序字段',
`order_type` varchar(4) NOT NULL DEFAULT '' COMMENT '排序方式',
`xls_max_number` int(7) unsigned NOT NULL DEFAULT '10000' COMMENT '单个xls最大记录数',
`xls_create_concurrent` tinyint(3) unsigned NOT NULL DEFAULT '3' COMMENT 'xls创建并发',
`memory_limit` decimal(8,0) unsigned NOT NULL DEFAULT '128' COMMENT '脚本内存限制(Mb)',
`export_number` int(10) unsigned DEFAULT NULL COMMENT '导出记录数',
`subtask` text COMMENT '子任务资料',
`progress` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '导出进度',
`file` varchar(255) NOT NULL DEFAULT '' COMMENT '文件包',
`createtime` int(10) unsigned DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据导出表';
\ No newline at end of file
... ...
<?php
namespace addons\fastexport\library;
use think\Db;
/**
*
*/
class ExportLib
{
protected $_error = '';
protected $task;// 模型
public $fields = [];
public function __construct($task)
{
if (!$task) {
$this->setError('导出任务找不到啦~');
return false;
}
$this->task = $task;
// 准备所有的字段数据
if ($this->task['field_config'] && isset($this->task['field_config']['title'])) {
foreach ($this->task['field_config']['title'] as $key => $value) {
$as_name = $this->task['main_table'] . '.' . $key . ' as ' . $this->task['main_table'] . '_' . $key;
$this->fields[$as_name]['title'] = $value;
$this->fields[$as_name]['discerns'] = $this->task['field_config']['discerns'][$key];
$this->fields[$as_name]['scheme'] = $this->task['field_config']['scheme'][$key];
$this->fields[$as_name]['table'] = $this->task['main_table'];
$this->fields[$as_name]['field'] = $this->task['main_table'] . '_' . $key;
}
}
if (isset($this->task['join_table']) && is_array($this->task['join_table'])) {
foreach ($this->task['join_table'] as $key => $value) {
if (isset($this->task['join_table'][$key]['fields']) && isset($this->task['join_table'][$key]['fields']['title'])) {
$join_table_name = $value['join_as'] ? $value['join_as'] : $value['table'];
foreach ($this->task['join_table'][$key]['fields']['title'] as $fkey => $fvalue) {
$as_name = $join_table_name . '.' . $fkey . ' as ' . $join_table_name . '_' . $fkey;
$this->fields[$as_name]['title'] = $fvalue;
$this->fields[$as_name]['discerns'] = $this->task['join_table'][$key]['fields']['discerns'][$fkey];
$this->fields[$as_name]['scheme'] = $this->task['join_table'][$key]['fields']['scheme'][$fkey];
$this->fields[$as_name]['table'] = $join_table_name;
$this->fields[$as_name]['field'] = $join_table_name . '_' . $fkey;
}
}
}
}
}
public static function assignment($field_value, $scheme)
{
if ($scheme) {
$scheme = explode(',', $scheme);
foreach ($scheme as $key => $value) {
list($item_key, $item_value) = explode('=', $value);
if ($item_key == $field_value) {
return $item_value;
}
}
} else {
return $field_value;
}
}
public static function curlGet($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
public function getXlsTitle()
{
foreach ($this->fields as $key => $value) {
$title[] = $value['title'] ? $value['title'] : $key;
}
return $title;
}
/**
* 构建SQL
* @param string $type count=直接查询总数,limit=读取指定范围的记录,test=测试SQL(读取前10条记录)
* @param array $limit [mim, max]查询范围
* @return string SqlString
*/
public function buildSql($build_type = 'count', $limit = [0, 10])
{
$field = '';// 字段
$join_table = []; // 关联表
$where = [];
$order = [];
// 要查询的字段
foreach ($this->fields as $key => $value) {
$field .= $key . ',';
}
$field = trim($field, ',');
// 关联表
if (isset($this->task['join_table']) && is_array($this->task['join_table'])) {
foreach ($this->task['join_table'] as $key => $value) {
if ($value['table'] && $value['foreign_key'] && $value['local_key']) {
$join_table_name = $value['join_as'] ? $value['join_as'] : $value['table'];
$join = $value['join_as'] ? $value['table'] . ' ' . $value['join_as'] : $value['table'];
$condition = vsprintf('%s.%s = %s.%s', [
$this->task['main_table'],
$value['foreign_key'],
$join_table_name,
$value['local_key']
]);
$join_table[] = [$join, $condition, $value['join_type']];
}
}
}
// 筛选
if (isset($this->task['where_field']['op']) && is_array($this->task['where_field']['op'])) {
foreach ($this->task['where_field']['op'] as $key => $expression) {
if (isset($this->task['where_field']['condition'][$key])) {
$where[$key] = [$expression, $this->task['where_field']['condition'][$key]];
}
}
}
// 排序
if ($this->task['order_field'] && $this->task['order_type']) {
$order = [
$this->task['order_field'] => $this->task['order_type']
];
}
$res = Db::table($this->task['main_table'])->field($field)->join($join_table)->where($where);
if ($build_type == 'count') {
return $res->count();
} elseif ($build_type == 'limit') {
return $res->order($order)->limit($limit[0], $limit[1])->select(false);
} elseif ($build_type == 'test') {
return $res->order($order)->limit(10)->select(false);
}
}
/**
* 设置错误信息
*
* @param string $error 错误信息
* @return ExportLib
*/
public function setError($error)
{
$this->_error = $error;
return $this;
}
/**
* 获取错误信息
* @return string
*/
public function getError()
{
return $this->_error ? __($this->_error) : '';
}
}
\ No newline at end of file
... ...
<?php
namespace app\admin\controller;
use app\common\controller\Backend;
use addons\fastexport\library\ExportLib;
use think\Db;
use think\Exception;
use think\exception\PDOException;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
/**
* 数据导出管理
*
* @icon fa fa-circle-o
*/
class Fastexport extends Backend
{
/**
* FastExport模型对象
* @var \app\admin\model\FastExport
*/
protected $model = null;
protected $database = [];
public $save_dir = RUNTIME_PATH . 'fastexport' . DS;// 临时文件保存位置(每次开始任务时清空)
public $fastExportDir;// ZIP包保存位置(重新初始化/删除任务时删除ZIP包)
public function _initialize()
{
parent::_initialize();
$rootPath = str_replace('/', DS, ROOT_PATH);
$this->model = new \app\admin\model\FastExport;
$this->database = config('database');
$this->fastExportDir = $rootPath . 'public' . DS . 'fastexport';
$tableList = [];
$list = Db::query("SELECT TABLE_NAME,TABLE_COMMENT FROM information_schema.TABLES WHERE table_schema = ? ", [$this->database['database']]);
// 需要排除的数据表
$rule_out_table = [
$this->database['prefix'] . 'config',
$this->database['prefix'] . 'version',
// $this->database['prefix'] . 'admin',
$this->database['prefix'] . 'user_token',
$this->database['prefix'] . 'fast_auth_group_access',
];
foreach ($list as $key => $row) {
if (!in_array($row['TABLE_NAME'], $rule_out_table)) {
$tableList[] = [
'name' => $row['TABLE_NAME'],
'comment' => $row['TABLE_COMMENT'] ? str_replace('表', '', $row['TABLE_COMMENT']) : '',
];
}
}
$this->view->assign("tableList", $tableList);
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids) {
$pk = $this->model->getPk();
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
$this->model->where($this->dataLimitField, 'in', $adminIds);
}
$list = $this->model->where($pk, 'in', $ids)->select();
$count = 0;
Db::startTrans();
try {
foreach ($list as $k => $v) {
// 同时删除临时文件和zip包
if (is_dir($this->save_dir . $v->id)) {
$this->emptydir($this->save_dir . $v->id, true);
}
if ($v->file) {
$file_name = explode(DS, str_replace('/', DS, $v->file));
if (file_exists($this->fastExportDir . DS . end($file_name))) {
unlink($this->fastExportDir . DS . end($file_name));
}
}
$count += $v->delete();
}
Db::commit();
} catch (PDOException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($count) {
$this->success();
} else {
$this->error(__('No rows were deleted'));
}
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
/**
* 测试任务-导出前10条数据
* @param int $ids [description]
*/
public function testTask($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$ExportLib = new ExportLib($row);
try {
$export_number = $row['export_number'] ? $row['export_number'] : $ExportLib->buildSql('count');
$xls_max_number = ($export_number >= $row['xls_max_number']) ? $row['xls_max_number'] : $export_number;
if (!$export_number) {
$this->error('没有数据需要导出~');
}
$min = 0;
$max = 10;
$subtask = [
1 => [
'id' => 1,
'status' => 0,// 0准备好,1进行中,2完成,3失败
'min' => $min,
'max' => $max,
'sql' => $ExportLib->buildSql('limit', [$min, $max])
]
];
// 测试sql
Db::query($subtask[1]['sql']);
$row->subtask = $subtask;
$row->save();
} catch (PDOException $e) {
$this->error($e->getMessage());
} catch (Exception $e) {
$this->error($e->getMessage());
}
$this->success('导出任务测试准备好~', null, [
'direct_export' => true,
'subtask' => $subtask,
'id' => $ids
]);
}
/**
* 开始任务->初始化任务配置
* @return bool
*/
public function performTask($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$ExportLib = new ExportLib($row);
try {
$export_number = $row['export_number'] ? $row['export_number'] : $ExportLib->buildSql('count');
$xls_max_number = ($export_number >= $row['xls_max_number']) ? $row['xls_max_number'] : $export_number;
if (!$export_number) {
$this->error('没有数据需要导出~');
}
$subtask = [];
$subtask_count = ceil($export_number / $xls_max_number);// 子任务数量
for ($i = 1; $i <= $subtask_count; $i++) {
if ($i == 1) {
$min = 0;
} else {
$min = ($i - 1) * $xls_max_number;
}
$subtask[$i] = [
'id' => $i,
'status' => 0,// 0准备好,1进行中,2完成,3失败
'min' => $min,
'max' => $xls_max_number,
'sql' => $ExportLib->buildSql('limit', [$min, $xls_max_number])
];
// 测试sql
if ($i == 1) {
Db::query($subtask[$i]['sql']);
}
}
if ($subtask_count > 1) {
// 清理任务临时文件目录
if (!is_dir($this->save_dir . $ids)) {
mkdir($this->save_dir . $ids, 0777, true);
} else {
$this->emptydir($this->save_dir . $ids);
}
$row->progress = 5;
} else {
$row->progress = 0;
}
// 删除上次任务的zip包
if ($row->file) {
if ($row->file) {
$file_name = explode(DS, str_replace('/', DS, $row->file));
if (file_exists($this->fastExportDir . DS . end($file_name))) {
unlink($this->fastExportDir . DS . end($file_name));
}
}
$row->file = '';
}
$row->subtask = $subtask;
$row->save();
} catch (PDOException $e) {
$this->error($e->getMessage());
} catch (Exception $e) {
$this->error($e->getMessage());
}
$this->success('导出任务初始化成功!', null, [
'direct_export' => ($subtask_count == 1) ? true : false,
'subtask' => $subtask,
'id' => $ids
]);
}
/**
* 子任务控制器
* @param [type] $task_id 任务ID
*/
public function taskControl($task_id = null)
{
$row = $this->model->get($task_id);
if (!$row) {
$this->error(__('任务找不到啦~'));
}
if (!isset($row['subtask']) || !is_array($row['subtask'])) {
$this->error('子任务找不到啦,请重新开始任务~');
}
$row = $row->toArray();
// 子任务状态:0准备好,1进行中,2完成,3失败
foreach ($row['subtask'] as $key => $value) {
switch ($value['status']) {
case '0':
$row['subtask'][$key]['status_text'] = '等待中';
break;
case '1':
$row['subtask'][$key]['status_text'] = '进行中';
break;
case '2':
$row['subtask'][$key]['status_text'] = '已完成';
break;
default:
$row['subtask'][$key]['status_text'] = '失败';
break;
}
}
$current_page = 1;
$subtask_page = [];
foreach ($row['subtask'] as $key => $value) {
$value['status'] = 0;// 用户可能会刷新任务控制页面,js将重新确定状态
$subtask_page[$current_page][] = $value;
if (count($subtask_page[$current_page]) >= $row['xls_create_concurrent']) {
$current_page++;
}
}
$subtask_count = count($row['subtask']);
$row['item_subtask_progress'] = round(92 / $subtask_count, 2);
$row['progress'] = 5;
$this->assignconfig('subtask_page', $subtask_page);
$this->assignconfig('task', $row);
$this->view->assign("task", $row);
return $this->view->fetch();
}
/**
* 执行子任务
* @param int $task_id 任务ID
* @param int $subtask_id 子任务ID
* @param boolean $direct_export 是否直接反回文件
*/
public function performSubTask($task_id, $subtask_id, $direct_export = false)
{
\think\Config::set('default_return_type', 'json');
$row = $this->model->get($task_id);
if (!$row) {
$this->error(__('No Results were found'));
}
if (isset($row['subtask'][$subtask_id]) && is_array($row['subtask'][$subtask_id])) {
$subtask = $row['subtask'][$subtask_id];
$task_name = $row['name'];
} else {
$this->result(['subtask_id' => $subtask_id], 0, '子任务找不到啦~', 'json');
}
set_time_limit(0);// 脚本执行时间限制
ini_set('memory_limit', $row['memory_limit'] . 'M');// 脚本内存限制
$ExportLib = new ExportLib($row);
unset($row);
// 检查任务状态
if (!$direct_export) {
if ($subtask['status'] == 1) {
$this->result(['subtask_id' => $subtask_id], 0, '此子任务正在执行中~', 'json');
} else if ($subtask['status'] == 2) {
if (file_exists($this->save_dir . $task_id . DS . $subtask_id . '.xlsx')) {
$this->result(['subtask_id' => $subtask_id], 1, '此子任务已经处理过啦~', 'json');
}
}
}
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->setTitle($task_name);
//设置表头
$head = $ExportLib->getXlsTitle();
$head_count = count($head);
for ($i = 0; $i < $head_count; $i++) {
$worksheet->setCellValueByColumnAndRow($i + 1, 1, $head[$i]);
}
// 写人数据
try {
$data = Db::query($subtask['sql']);
} catch (PDOException $e) {
$this->result([
'subtask_id' => $subtask_id,
'error_msg' => $e->getMessage(),
], 0, '任务失败!', 'json');
}
$y = 2;
foreach ($data as $row_key => $row) {
$i = 1;
foreach ($ExportLib->fields as $key => $value) {
$field = $value['field'];
if ($value['discerns'] == 0) {
// 文本
$worksheet->setCellValueByColumnAndRow($i, $y, $row[$field], DataType::TYPE_STRING);
} else if ($value['discerns'] == 1) {
// 数字
$worksheet->setCellValueByColumnAndRow($i, $y, $row[$field] . "\t");
} else if ($value['discerns'] == 2) {
// 日期时间
if ($row[$field]) {
$excelDateValue = date('Y-m-d H:i:s', $row[$field]);
$worksheet->setCellValueByColumnAndRow($i, $y, $excelDateValue, DataType::TYPE_STRING);
/*$excelDateValue = PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $row[$field] );
if ($excelDateValue) {
// 以日期日期格式写入,没区别,有需要的可以打开
$worksheet->setCellValueByColumnAndRow($i, $y, $excelDateValue);
$worksheet->getStyleByColumnAndRow($i,$y)
->getNumberFormat()
->setFormatCode('yyyy-mm-dd h:mm:ss');
$worksheet->getStyleByColumnAndRow($i,$y)->getFont()->setBold(true);
$worksheet->getStyleByColumnAndRow($i,$y)->getFont()->setBold(false);
}*/
} else {
$worksheet->setCellValueByColumnAndRow($i, $y, '-', DataType::TYPE_STRING);
}
} else if ($value['discerns'] == 3) {
// 图片-太慢,太耗资源,弃用
/*$file_info = pathinfo($row[$field]);
$coorrow = $worksheet->getCellByColumnAndRow($i, $y)->getRow();
$coorcolumn = $worksheet->getCellByColumnAndRow($i, $y)->getColumn();
if ($y == 2) {
$worksheet->getColumnDimension($coorcolumn)->setWidth(8);
}
$worksheet->getRowDimension($coorrow)->setRowHeight(40);
if (!empty($file_info['basename'])) {
$basename = $file_info['basename'];
$drawing = new Drawing();
$drawing->setName('图片');
$drawing->setPath($remote_save_dir . $row[$field]);
$drawing->setWidth(40);
$drawing->setHeight(40);
$drawing->setCoordinates($coorcolumn . $coorrow);
$drawing->setWorksheet($worksheet);
} else {
$worksheet->setCellValueByColumnAndRow($i, $y, $row[$field], DataType::TYPE_STRING);
}*/
// 以链接导出图片
$field_value = cdnurl($row[$field], true);
$worksheet->setCellValueByColumnAndRow($i, $y, $field_value, DataType::TYPE_STRING);
$worksheet->getCellByColumnAndRow($i, $y)->getHyperlink()->setUrl($field_value);
} else if ($value['discerns'] == 4) {
// 文件
$field_value = cdnurl($row[$field], true);
$worksheet->setCellValueByColumnAndRow($i, $y, $field_value, DataType::TYPE_STRING);
$worksheet->getCellByColumnAndRow($i, $y)->getHyperlink()->setUrl($field_value);
} else if ($value['discerns'] == 5) {
// 赋值
$field_value = $ExportLib->assignment($row[$field], $value['scheme']);
$worksheet->setCellValueByColumnAndRow($i, $y, $field_value, DataType::TYPE_STRING);
}
$i++;
}
$y++;
unset($data[$row_key]); // 能节约一点内存
}
// xls文件处理
if ($direct_export) {
// 直接下载
ob_end_clean();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control:must-revalidate, post-check=0, pre-check=0");
header("Content-Type:application/force-download");
header("Content-Type:application/vnd.ms-execl");
header("Content-Type:application/octet-stream");
header("Content-Type:application/download");
$task_name = $task_id . '.' . $task_name . '.xlsx';
$encoded_filename = urlencode($task_name);
$ua = $_SERVER["HTTP_USER_AGENT"];
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header('Content-Disposition: attachment; filename*="utf8\'\'' . $task_name . '"');
} else {
header('Content-Disposition: attachment; filename="' . $task_name . '"');
}
header("Content-Transfer-Encoding:binary");
header('Cache-Control: max-age=0');// 禁止缓存
$writer = new Xlsx($spreadsheet);
$writer->save('php://output');
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
} else {
// 保存
$writer = new Xlsx($spreadsheet);
$writer->save($this->save_dir . $task_id . DS . $subtask_id . '.xlsx');
// 设置任务状态
$result = false;
Db::startTrans();
try {
// 读取最新的 subtask 数据
$row = $this->model->get($task_id);
$progress = $row->progress + round(92 / count($row['subtask']), 2);
$progress = ($progress > 100) ? 100 : $progress;
if (isset($row['subtask'][$subtask_id]) && is_array($row['subtask'][$subtask_id])) {
$subtask = $row['subtask'];
$subtask[$subtask_id]['status'] = 2;
$row->subtask = $subtask;
$row->progress = $progress;
$row->save();
$result = true;
}
Db::commit();
} catch (PDOException $e) {
Db::rollback();
$this->result([
'subtask_id' => $subtask_id,
'error_msg' => $e->getMessage(),
], 0, '任务失败!', 'json');
} catch (Exception $e) {
Db::rollback();
$this->result([
'subtask_id' => $subtask_id,
'error_msg' => $e->getMessage(),
], 0, '任务失败!', 'json');
}
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
if ($result) {
$this->result(['subtask_id' => $subtask_id], 1, '子任务处理成功!', 'json');
} else {
$this->result(['subtask_id' => $subtask_id], 0, '子任务找不到啦~', 'json');
}
}
}
/**
* 打包ZIP
*/
public function taskFilePack($task_id)
{
$row = $this->model->get($task_id);
if (!$row) {
$this->error(__('No Results were found'));
}
if (!isset($row['subtask']) || !is_array($row['subtask'])) {
$this->result(null, 0, '打包失败,任务找不到啦~', 'json');
}
if ($row['file']) {
$this->result(['file_name' => $row['file']], 1, '此任务已经打过包了!', 'json');
}
is_dir($this->fastExportDir) OR mkdir($this->fastExportDir, 0755, true);
foreach ($row['subtask'] as $key => $subtask) {
if (!file_exists($this->save_dir . $task_id . DS . $subtask['id'] . '.xlsx')) {
$this->result(null, 0, '子任务未处理完毕!', 'json');
}
}
$task_dir = $this->save_dir . $task_id . DS;// 导出任务的临时文件目录
$zipname = $task_id . '.export_' . \fast\Random::alnum(6) . '.zip';
$zipurl = request()->domain() . DS . 'fastexport' . DS . $zipname;// 绝对地址,以便各处直接点击下载
$zipname = $this->fastExportDir . DS . $zipname;
$zip = new \ZipArchive();
$res = $zip->open($zipname, \ZipArchive::CREATE);
if ($res === TRUE) {
$dh = opendir($task_dir);
while ($file = readdir($dh)) {
if ($file != "." && $file != "..") {
$fullpath = $task_dir . $file;
if (!is_dir($fullpath)) {
$zip->addFile($fullpath, $file);
}
}
}
closedir($dh);
}
$zip->close();
$row->file = $zipurl;
$row->progress = 100;
$row->save();
$this->result(['file_name' => $zipurl], 1, '文件打包完成!', 'json');
}
/**
* 获取字段列表、字段注释、表主键
* @internal
*/
public function getTableFields($table = null)
{
if ($this->request->isAjax()) {
$table = $this->request->request('table');
}
//从数据库中获取表字段信息
$sql = "SELECT * FROM `information_schema`.`columns` "
. "WHERE TABLE_SCHEMA = ? AND table_name = ? "
. "ORDER BY ORDINAL_POSITION";
//加载主表的列
$columnList = Db::query($sql, [$this->database['database'], $table]);
$fieldlist_comment = [];
foreach ($columnList as $index => $item) {
$fieldlist_comment[$item['COLUMN_NAME']] = $this->FieldComment($item['COLUMN_NAME'], $item['DATA_TYPE'], $item['COLUMN_COMMENT']);
}
if ($this->request->isAjax()) {
$this->success("", null, ['fieldlist_comment' => $fieldlist_comment]);
} else {
return $fieldlist_comment;
}
}
/**
* 字段分析
* @param String $field 字段名称
* @param String $data_type 字段数据类型
* @param String $comment 字段注释
* @return Array 可用的字段数据
*/
protected function FieldComment($field, $data_type, $comment)
{
// 字段类型识别
$data_type_int = [
'tinyint',
'int',
'smallint',
'mediumint',
'integer',
'bigint'
];
$discerns = 0;
if (in_array($data_type, $data_type_int)) {
$discerns = 1;// 数字
}
if (preg_match("/time$/i", $field)) {
$discerns = 2;// 日期时间
} else if (preg_match("/image$|avatar$/i", $field)) {
$discerns = 4;// 3为图片-导出慢弃用
} else if (preg_match("/file$/i", $field)) {
$discerns = 4;// 文件
}
/*else if (preg_match("/list$|data$|switch$/i", $field)) {
$discerns = 5;// 赋值
}*/
// 字段标题和赋值分析
if (!$comment) {
return [
'discerns' => $discerns,
'name' => $field,
'comment' => ''
];
}
$comment = str_replace(',', ',', $comment);
$comment = str_replace(['(多选)', '(单选)', '(多选)', '(单选)'], '', $comment);
if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) {
list($field_name, $field_comment) = explode(':', $comment);
return [
'discerns' => 5,
'name' => $field_name ? $field_name : $field,
'comment' => $field_comment
];
} else {
if (preg_match("/switch$/i", $field)) {
$discerns = 5;// 赋值
$comment_temp = '0=关闭,1=开启';
}
return [
'discerns' => $discerns,
'name' => $comment ? $comment : $field,
'comment' => isset($comment_temp) ? $comment_temp : ''
];
}
}
/**
* 查看
*/
public function index()
{
//当前是否为关联查询
$this->relationSearch = true;
//设置过滤方法
$this->request->filter(['strip_tags']);
if ($this->request->isAjax()) {
//如果发送的来源是Selectpage,则转发到Selectpage
if ($this->request->request('keyField')) {
return $this->selectpage();
}
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$total = $this->model
->with(['admin'])
->where($where)
->order($sort, $order)
->count();
$list = $this->model
->with(['admin'])
->where($where)
->order($sort, $order)
->limit($offset, $limit)
->select();
foreach ($list as $row) {
$row->getRelation('admin')->visible(['username']);
}
$list = collection($list)->toArray();
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$params = method_exists($this, 'preExcludeFields') ? $this->preExcludeFields($params) : $params;
$params['admin_id'] = $this->auth->id;// 记录导出人
if (isset($params['where_field']['op']) && is_array($params['where_field']['op'])) {
foreach ($params['where_field']['op'] as $key => $value) {
$params['where_field']['op'][$key] = trim($value);
}
}
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
$params[$this->dataLimitField] = $this->auth->id;
}
$result = false;
Db::startTrans();
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
$this->model->validateFailException(true)->validate($validate);
}
$result = $this->model->allowField(true)->save($params);
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (PDOException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success();
} else {
$this->error(__('No rows were inserted'));
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
if (!in_array($row[$this->dataLimitField], $adminIds)) {
$this->error(__('You have no permission'));
}
}
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$params = method_exists($this, 'preExcludeFields') ? $this->preExcludeFields($params) : $params;
$params['join_table'] = isset($params['join_table']) ? $params['join_table'] : [];
$params['where_field'] = isset($params['where_field']) ? $params['where_field'] : [];
if (isset($params['where_field']['op']) && is_array($params['where_field']['op'])) {
foreach ($params['where_field']['op'] as $key => $value) {
$params['where_field']['op'][$key] = trim($value);
}
}
$result = false;
Db::startTrans();
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
$row->validateFailException(true)->validate($validate);
}
$result = $row->allowField(true)->save($params);
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (PDOException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success();
} else {
$this->error(__('No rows were updated'));
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
$row = $row->toArray();
$row['join_table_number'] = count($row['join_table']);
// 源表字段列表
$table_field = $this->getTableFields($row['main_table']);
// 准备源表和关联表的所有字段-带表名-供筛选和排序
$allField = [];
foreach ($table_field as $key => $value) {
$field_name = $row['main_table'] . '.' . $key;
$value['field_name'] = $field_name . ' - ' . $value['name'];
$allField[$field_name] = $value;
}
// 要导出的字段
$fields = [];
if ($row['field_config'] && isset($row['field_config']['title'])) {
foreach ($row['field_config']['title'] as $key => $value) {
$fields[$key]['title'] = $value;
$fields[$key]['discerns'] = $row['field_config']['discerns'][$key];
$fields[$key]['scheme'] = $row['field_config']['scheme'][$key];
}
}
$row['fields'] = $fields;
// 源表和关联表的字段列表透传数据准备
$join_field_count = 0;
if (isset($row['join_table'])) {
foreach ($row['join_table'] as $key => $value) {
$join_table_name = isset($value['table']) ? $value['table'] : false;
if ($join_table_name) {
$row['join_table'][$key]['field_list'] = $join_table_field[$join_table_name] = $this->getTableFields($join_table_name);
$join_table_name_as = $value['join_as'] ? $value['join_as'] : $join_table_name;
foreach ($join_table_field[$join_table_name] as $join_field_key => $join_field_value) {
$field_name = $join_table_name_as . '.' . $join_field_key;
$join_field_value['field_name'] = $field_name . ' - ' . $join_field_value['name'];
$allField[$field_name] = $join_field_value;
}
}
// 关联表取值字段准备
$field_name_arr = [];
$field_name_arr_select = [];
if (isset($row['join_table'][$key]['fields']) && isset($row['join_table'][$key]['fields']['title'])) {
foreach ($row['join_table'][$key]['fields']['title'] as $fkey => $fvalue) {
$field_name_arr[$fkey]['title'] = $fvalue;
$field_name_arr[$fkey]['discerns'] = $row['join_table'][$key]['fields']['discerns'][$fkey];
$field_name_arr[$fkey]['scheme'] = $row['join_table'][$key]['fields']['scheme'][$fkey];
$field_name_arr_select[] = $fkey;
}
}
$row['join_table'][$key]['field_name_arr'] = $field_name_arr;
$row['join_table'][$key]['field_name_arr_select'] = $field_name_arr_select;// 供selectpicker初始化
$join_field_count += count($field_name_arr_select);
}
}
$row['where_field_arr_select'] = [];
$row['where_field_arr'] = [];
if ($row['where_field'] && isset($row['where_field']['op'])) {
foreach ($row['where_field']['op'] as $key => $value) {
$row['where_field_arr_select'][] = $key;
$row['where_field_arr'][$key] = [
'op' => $value,
'condition' => $row['where_field']['condition'][$key]
];
}
}
$this->assignconfig('fields', [
'table_field' => $table_field,
'join_table_field' => isset($join_table_field) ? $join_table_field : [],
'join_table_number' => $row['join_table_number'],
'field_count' => count($row['fields']) + $join_field_count,
]);
$this->view->assign("row", $row);
$this->view->assign("allField", $allField);
$this->view->assign("table_field", $table_field);
return $this->view->fetch();
}
public function emptydir($dir, $del_current_dir = false)
{
$dh = opendir($dir);
while ($file = readdir($dh)) {
if ($file != "." && $file != "..") {
$fullpath = $dir . DS . $file;
if (!is_dir($fullpath)) {
unlink($fullpath);
} else {
$this->emptydir($fullpath, true);
}
}
}
closedir($dh);
// 删除当前文件夹
if ($del_current_dir) {
rmdir($dir);
}
}
}
... ...
<?php
return [
'Id' => 'ID',
'Admin_id' => '导出人',
'Name' => '任务名称',
'Main Table' => '数据源表',
'Field_config' => '字段配置',
'Join_table' => '关联表配置',
'Where_field' => '筛选规则',
'Order_field' => '排序字段',
'Order_type' => '排序方式',
'Xls_max_number' => '单个xls最大记录数',
'Xls_create_concurrent' => 'xls创建并发',
'Export_number' => '导出记录数',
'Subtask' => '子任务资料',
'Progress' => '上次执行进度',
'File' => '上次执行文件包',
'Createtime' => '创建时间',
'Admin.username' => '用户名',
'testTask' => '测试任务',
'performTask' => '执行任务',
];
... ...
<?php
return [
'Goods_id' => 'ID',
'Goods_name' => '商品名称',
'Category_id' => '商品类别',
'Image' => '商品封面',
'Images' => '商品轮播图片',
'Spec_type' => '商品规格',
'Spec_type 10' => '单规格',
'Spec_type 20' => '多规格',
'Deduct_stock_type' => '库存计算方式',
'Deduct_stock_type 10' => '下单减库存',
'Deduct_stock_type 20' => '付款减库存',
'Content' => '描述详情',
'Sales_initial' => '初始销量',
'Sales_actual' => '实际销量',
'Goods_sort' => '权重',
'Delivery_id' => '运费模板ID',
'Goods_status' => '商品状态',
'Goods_status 10' => '上架',
'Goods_status 20' => '下架',
'Is_delete' => '是否删除',
'Is_delete 0' => '未删除',
'Is_delete 1' => '已删除',
'Createtime' => '创建时间',
'Updatetime' => '更新时间',
'Category.name' => '分类名称',
'Activity.name' => '活动名称',
'Freight.name' => '运费模版名称',
'Makefor' => '产地',
'Makefor 1' => '国产',
'Makefor 2' => '进口',
'Number' => '编号',
'Keep' => '保存条件',
'Packing' => '包装方式',
'Brand' => '品牌',
'Four_images' => '四宫格图',
'Down_image' => '底部图',
'Is_index' => '首页展示',
'Is_index 1' => '已展示',
'Is_index 0' => '未展示',
'Activity_id' => '活动类型',
'Price_description' => '价格说明',
'Keywords' => '搜索关键词',
'Goods_id' => 'ID',
'Goods_name' => '商品名称',
'Category_id' => '商品类别',
'Image' => '商品封面',
'Images' => '商品轮播图片',
'Spec_type' => '商品规格',
'Spec_type 10' => '单规格',
'Spec_type 20' => '多规格',
'Deduct_stock_type' => '库存计算方式',
'Deduct_stock_type 10' => '下单减库存',
'Deduct_stock_type 20' => '付款减库存',
'Content' => '描述详情',
'Sales_initial' => '初始销量',
'Sales_actual' => '实际销量',
'Goods_sort' => '权重',
'Delivery_id' => '运费模板ID',
'Goods_status' => '商品状态',
'Goods_status 10' => '上架',
'Goods_status 20' => '下架',
'Is_delete' => '是否删除',
'Is_delete 0' => '未删除',
'Is_delete 1' => '已删除',
'Createtime' => '创建时间',
'Updatetime' => '更新时间',
'Category.name' => '分类名称',
'Activity.name' => '活动名称',
'Freight.name' => '运费模版名称',
'Makefor' => '产地',
'Makefor 1' => '国产',
'Makefor 2' => '进口',
'Number' => '编号',
'Keep' => '保存条件',
'Packing' => '包装方式',
'Brand' => '品牌',
'Four_images' => '四宫格图',
'Down_image' => '底部图',
'Detail_images' => '详情图',
'Is_index' => '首页展示',
'Is_index 1' => '已展示',
'Is_index 0' => '未展示',
'Activity_id' => '活动类型',
'Price_description' => '价格说明',
'Keywords' => '搜索关键词',
];
... ...
<?php
namespace app\admin\model;
use think\Model;
class FastExport extends Model
{
// 表名
protected $name = 'fastexport';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
];
protected $type = [
'field_config' => 'array',
'join_table' => 'array',
'where_field' => 'array',
'subtask' => 'array'
];
public function admin()
{
return $this->belongsTo('Admin', 'admin_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
}
... ...
<?php
namespace app\admin\validate;
use think\Validate;
class FastExport extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}
... ...
<style type="text/css">
.field_title{
display: inline-block;
width: 19%;
}
.field_prompt{
display: inline-block;
text-align: center;
width: 14%;
}
.field_discerns{
display: inline-block;
width: 12%;
}
.field_scheme{
display: inline-block;
width: 37%;
}
.where_field_op{
display: inline-block;
text-align: center;
width: 32%;
}
.where_field_condition{
display: inline-block;
width: 52%;
}
.order_field{
display: inline-block;
width: 70%;
}
.order_field_condition{
display: inline-block;
width: 28%;
}
.tabbar_title{
padding: 15px;
background: #e8edf0;
border-color: #e8edf0;
margin-bottom: 15px;
}
.reduction_field{
color: #335B64;
margin-right: 6px;
}
.panel-body{
padding-top: 0;
}
.middle_inline{
display: inline-block;
vertical-align: middle;
margin-bottom: 0;
}
.kefu_form_control .sp_container {
width: 100% !important;
}
.panel{
box-shadow: none;
}
.xls_max_number .msg-wrap.n-error{
margin-left: 66px;
}
.clear_margin_bottom {
margin-bottom: 0px;
}
.clear_margin_top {
margin-top: 0px;
}
.memory_limit .msg-wrap.n-error{
margin-left: 50px;
}
</style>
<div class="row animated fadeInRight">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default panel-intro">
<div class="panel-heading tabbable">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#tab1" aria-controls="tab1" role="tab" data-toggle="tab">基础配置</a>
</li>
<li role="presentation">
<a href="#tab2" aria-controls="tab2" role="tab" data-toggle="tab">关联表配置</a>
</li>
<li role="presentation">
<a href="#tab3" aria-controls="tab3" role="tab" data-toggle="tab">数据筛选</a>
</li>
<li role="presentation">
<a href="#tab4" aria-controls="tab4" role="tab" data-toggle="tab">其他配置</a>
</li>
</ul>
</div>
<div class="panel-body">
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="{:url('fastexport/add')}">
{:token()}
<div class="box-body tab-content">
<div role="tabpanel" class="tab-pane fade in active" id="tab1">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Main Table')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-main_table" data-rule="required;checkTable" data-rule-checkTable="
function (e) {
if(e.value == 'none') {
return '请选择数据表';
}
}
" class="form-control" name="row[main_table]">
<option data-comment="" value="none">请选择</option>
{volist name="tableList" id="main_table"}
<option data-comment="{$main_table.comment}" value="{$main_table.name}">{$main_table.name}{if $main_table.comment} - {$main_table.comment}{/if}</option>
{/volist}
</select>
<span class="help-block">请先选择要导出的数据表,随后在生成的列表中配置要导出的字段</span>
</div>
</div>
<div id="field_config"></div>
</div>
<div role="tabpanel" class="tab-pane fade" id="tab2">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">关联表数量:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-join_table_number" data-rule="integer(+0)" class="form-control" type="number" value="0">
</div>
</div>
<div id="join_table"></div>
</div>
<div role="tabpanel" class="tab-pane fade" id="tab3">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">筛选字段:</label>
<div class="col-xs-12 col-sm-8">
<select multiple="true" class="form-control selectpicker" id="where_field">
<option value="0">请选择</option>
</select>
<span class="help-block">请先配置源表和关联表,随后可在此处选择一些字段设置筛选条件</span>
</div>
</div>
<div id="where_field_input"></div>
</div>
<div role="tabpanel" class="tab-pane fade" id="tab4">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">排序字段:</label>
<div class="col-xs-12 col-sm-7">
<select class="form-control order_field" name="row[order_field]" id="order_field">
<option value="0">请选择</option>
</select>
<select class="form-control order_field_condition" name="row[order_type]">
<option value="DESC">倒序(从大到小)</option>
<option value="ASC">正序(从小到大)</option>
</select>
<span class="help-block">请先配置源表和关联表,随后可在此处设置以某字段进行排序</span>
</div>
</div>
<div class="form-group xls_max_number">
<label for="c-xls_max_number" class="control-label col-xs-12 col-sm-3">单个xls文件保存:</label>
<div class="col-xs-12 col-sm-7">
<div class="input-group input-groupp-md">
<input id="c-xls_max_number" data-rule="required;range(1~30000)" class="form-control" name="row[xls_max_number]" type="number" value="10000">
<span class="input-group-addon">条记录</span>
</div>
<span class="help-block clear_margin_bottom">若导出记录数超出以上设置,则自动分为多个xls文件保存</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">xls文件创建并发:</label>
<div class="col-xs-12 col-sm-7">
<input data-rule="required;range(1~100)" class="form-control" name="row[xls_create_concurrent]" type="number" value="2">
<span class="help-block clear_margin_bottom">若有多个xls文件需要准备,此处设置<strong>同一时间</strong>准备的xls文件数</span>
<span class="help-block clear_margin_top">若单个xls文件需保存2万以上数据,普通服务器请设置为`1`(受磁盘IO限制)</span>
</div>
</div>
<div class="form-group">
<label for="c-memory_limit" class="control-label col-xs-12 col-sm-3">脚本内存限制:</label>
<div class="col-xs-12 col-sm-7">
<div class="input-group memory_limit input-groupp-md">
<input id="c-memory_limit" data-rule="required;checkMemory" data-rule-checkMemory="
function (e) {
var fieldCount = Number(Config.fieldCount);
if (fieldCount <= 0){
return '请选择要导出的字段';
}
var memory = (fieldCount * Number($('#c-xls_max_number').val())) / 1024;
var memory_limit = Number(e.value);
if (memory >= Number(memory_limit)){
return '需要更多内存 >' + (memory + 50).toFixed(0) + 'Mb';
}
}
" class="form-control" name="row[memory_limit]" type="number" value="256">
<span class="input-group-addon">Mb</span>
</div>
<span class="help-block clear_margin_bottom">创建单个xls文件时,允许使用的最大内存量</span>
<span class="help-block clear_margin_top">若提示需要更多的内存,请在硬件条件允许的情况下,设置更高的值;或降低单个xls文件保存的记录数</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">导出:</label>
<div class="col-xs-12 col-sm-7">
<div class="input-group input-groupp-md">
<input class="form-control" name="row[export_number]" type="number" value="">
<span class="input-group-addon">条记录</span>
</div>
<span class="help-block">需要导出的数据量,不填写为导出全部</span>
</div>
</div>
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
\ No newline at end of file
... ...
<style type="text/css">
.field_title{
display: inline-block;
width: 19%;
}
.field_prompt{
display: inline-block;
text-align: center;
width: 14%;
}
.field_discerns{
display: inline-block;
width: 12%;
}
.field_scheme{
display: inline-block;
width: 37%;
}
.where_field_op{
display: inline-block;
text-align: center;
width: 32%;
}
.where_field_condition{
display: inline-block;
width: 52%;
}
.order_field{
display: inline-block;
width: 70%;
}
.order_field_condition{
display: inline-block;
width: 28%;
}
.tabbar_title{
padding: 15px;
background: #e8edf0;
border-color: #e8edf0;
margin-bottom: 15px;
}
.reduction_field{
color: #335B64;
margin-right: 6px;
}
.panel-body{
padding-top: 0;
}
.middle_inline{
display: inline-block;
vertical-align: middle;
margin-bottom: 0;
}
.kefu_form_control .sp_container {
width: 100% !important;
}
.panel{
box-shadow: none;
}
.xls_max_number .msg-wrap.n-error{
margin-left: 66px;
}
.clear_margin_bottom {
margin-bottom: 0px;
}
.clear_margin_top {
margin-top: 0px;
}
.memory_limit .msg-wrap.n-error{
margin-left: 50px;
}
</style>
<div class="row animated fadeInRight">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default panel-intro">
<div class="panel-heading tabbable">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#tab1" aria-controls="tab1" role="tab" data-toggle="tab">基础配置</a>
</li>
<li role="presentation">
<a href="#tab2" aria-controls="tab2" role="tab" data-toggle="tab">关联表配置</a>
</li>
<li role="presentation">
<a href="#tab3" aria-controls="tab3" role="tab" data-toggle="tab">数据筛选</a>
</li>
<li role="presentation">
<a href="#tab4" aria-controls="tab4" role="tab" data-toggle="tab">其他配置</a>
</li>
</ul>
</div>
<div class="panel-body">
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="box-body tab-content">
<div role="tabpanel" class="tab-pane fade in active" id="tab1">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Main Table')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-main_table" data-rule="required;checkTable" data-rule-checkTable="
function (e) {
if(e.value == 'none') {
return '请选择数据表';
}
}
" class="form-control" name="row[main_table]">
<option data-comment="" value="none">请选择</option>
{volist name="tableList" id="main_table"}
<option data-comment="{$main_table.comment}" value="{$main_table.name}" {eq name="row.main_table" value="$main_table.name"}selected{/eq}>{$main_table.name}{if $main_table.comment} - {$main_table.comment}{/if}</option>
{/volist}
</select>
<span class="help-block">请先选择要导出的数据表,随后在生成的列表中配置要导出的字段</span>
</div>
</div>
<div id="field_config">
{foreach name="row.fields" item="field" key="field_name"}
<div class="form-group" data-field="{$field_name}">
<label class="control-label col-xs-12 col-sm-2"><a class="reduction_field" href="javascript:;">[-]</a>{$field_name}:</label>
<div class="col-xs-12 col-sm-9">
<input placeholder="字段标题" class="form-control field_title" name="row[field_config][title][{$field_name}]" value="{$field.title}">
<span class="field_prompt">数据识别:</span>
<select name="row[field_config][discerns][{$field_name}]" class="form-control field_discerns">
<option value="0" {eq name="field.discerns" value="0"}selected{/eq}>文本</option>
<option value="1" {eq name="field.discerns" value="1"}selected{/eq}>数字</option>
<option value="2" {eq name="field.discerns" value="2"}selected{/eq}>日期</option>
<!-- <option value="3" {eq name="field.discerns" value="3"}selected{/eq}>图片</option> -->
<option value="4" {eq name="field.discerns" value="4"}selected{/eq}>文件</option>
<option value="5" {eq name="field.discerns" value="5"}selected{/eq}>赋值</option>
</select>
<span class="field_prompt">赋值方案:</span>
<input placeholder="数据识别为“赋值”时,才需填写" class="form-control field_scheme" name="row[field_config][scheme][{$field_name}]" value="{$field.scheme}">
</div>
</div>
{/foreach}
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="tab2">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">关联表数量:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-join_table_number" data-rule="integer(+0)" class="form-control" type="number" value="{$row.join_table_number}">
</div>
</div>
<div id="join_table">
{volist name="row.join_table" id="jtable"}
<hr class="divider {$key}">
<div class="form-group {$key}">
<label class="control-label col-xs-12 col-sm-2">选择关联表:</label>
<div class="col-xs-12 col-sm-8">
<select data-id="{$i}" id="s-{$key}" class="form-control s-join_table" name="row[join_table][{$key}][table]">
<option data-comment="" value="none">请选择</option>
{foreach name="tableList" item="table" key="table_key"}
<option data-comment="{$table.comment}" value="{$table.name}" {eq name="jtable.table" value="$table.name"}selected{/eq}>{$table.name}{if $table.comment} - {$table.comment}{/if}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group {$key}">
<label class="control-label col-xs-12 col-sm-2">关联表别名:</label>
<div class="col-xs-12 col-sm-8">
<input placeholder="非必填,取好别名则关联表可与源表相同" class="form-control table_name_as" id="s-join_table_as_{$i}" name="row[join_table][{$key}][join_as]" type="text" value="{$jtable.join_as}">
</div>
</div>
<div class="form-group {$key}">
<label class="control-label col-xs-12 col-sm-2">关联外键:</label>
<div class="col-xs-12 col-sm-8">
<select class="form-control foreign_key foreign_key_table_{$i}" name="row[join_table][{$key}][foreign_key]">
{foreach name="table_field" item="table_field_item" key="table_field_key"}
<option value="{$table_field_key}" {eq name="jtable.foreign_key" value="$table_field_key"}selected{/eq}>{$table_field_key} - {$table_field_item.name}</option>
{/foreach}
</select>
<span class="help-block">若无字段可选,请先选择数据源表</span>
</div>
</div>
<div class="form-group {$key}">
<label class="control-label col-xs-12 col-sm-2">关联主键:</label>
<div class="col-xs-12 col-sm-8">
<select class="form-control local_key local_key_table_{$i}" name="row[join_table][{$key}][local_key]">
{foreach name="jtable.field_list" item="jtable_field" key="jtable_field_key"}
<option value="{$jtable_field_key}" {eq name="jtable.local_key" value="$jtable_field_key"}selected{/eq}>{$jtable_field_key} - {$jtable_field.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group {$key}">
<label class="control-label col-xs-12 col-sm-2">JOIN类型:</label>
<div class="col-xs-12 col-sm-8">
<select class="form-control" name="row[join_table][{$key}][join_type]">
<option value="INNER" {eq name="jtable.join_type" value="INNER"}selected{/eq}>INNER - 至少一个匹配</option>
<option value="LEFT" {eq name="jtable.join_type" value="LEFT"}selected{/eq}>LEFT - 左表有匹配</option>
<option value="RIGHT" {eq name="jtable.join_type" value="RIGHT"}selected{/eq}>RIGHT - 右表有匹配</option>
<option value="FULL" {eq name="jtable.join_type" value="FULL"}selected{/eq}>FULL - 任意表有匹配</option>
</select>
</div>
</div>
<div class="form-group {$key}">
<label class="control-label col-xs-12 col-sm-2">取值字段:</label>
<div class="col-xs-12 col-sm-8">
<select multiple="true" data-id="{$i}" class="form-control selectpicker s-join_table_field" id="fields_table_{$i}">
{foreach name="jtable.field_list" item="jtable_field_item" key="jtable_field_key"}
<option value="{$jtable_field_key}" {in name="jtable_field_key" value="$jtable.field_name_arr_select"}selected="selected"{/in}>{$jtable_field_key} - {$jtable_field_item.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="join_table_{$i}" id="field_list_table_{$i}">
{foreach name="jtable.field_name_arr" item="field" key="field_name"}
<div class="form-group" data-field="{$field_name}">
<label class="control-label col-xs-12 col-sm-2">{$field_name}:</label>
<div class="col-xs-12 col-sm-9">
<input placeholder="字段标题" class="form-control field_title" name="row[join_table][join_table_{$i}][fields][title][{$field_name}]" value="{$field.title}">
<span class="field_prompt">数据识别:</span>
<select name="row[join_table][join_table_{$i}][fields][discerns][{$field_name}]" class="form-control field_discerns">
<option value="0" {eq name="field.discerns" value="0"}selected{/eq}>文本</option>
<option value="1" {eq name="field.discerns" value="1"}selected{/eq}>数字</option>
<option value="2" {eq name="field.discerns" value="2"}selected{/eq}>日期</option>
<!-- <option value="3" {eq name="field.discerns" value="3"}selected{/eq}>图片</option> -->
<option value="4" {eq name="field.discerns" value="4"}selected{/eq}>文件</option>
<option value="5" {eq name="field.discerns" value="5"}selected{/eq}>赋值</option>
</select>
<span class="field_prompt">赋值方案:</span>
<input placeholder="数据识别为“赋值”时,才需填写" class="form-control field_scheme" name="row[join_table][join_table_{$i}][fields][scheme][{$field_name}]" value="{$field.scheme}">
</div>
</div>
{/foreach}
</div>
{/volist}
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="tab3">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">筛选字段:</label>
<div class="col-xs-12 col-sm-8">
<select multiple="true" class="form-control selectpicker" id="where_field">
{foreach name="allField" item="all_field" key="all_field_name"}
<option value="{$all_field_name}" {in name="all_field_name" value="$row.where_field_arr_select"}selected="selected"{/in}>{$all_field.field_name}</option>
{/foreach}
</select>
<span class="help-block">请先配置源表和关联表,随后可在此处选择一些字段设置筛选条件</span>
</div>
</div>
<div id="where_field_input">
{foreach name="row.where_field_arr" item="where_field" key="where_field_name"}
<div class="form-group" data-field="{$where_field_name}">
<label class="control-label col-xs-12 col-sm-3">{$where_field_name}:</label>
<div class="col-xs-12 col-sm-7">
<select name="row[where_field][op][{$where_field_name}]" class="form-control where_field_op">
<option value="=" {eq name="where_field.op" value="="}selected{/eq}>等于</option>
<option value="<>" {eq name="where_field.op" value="<>"}selected{/eq}>不等于</option>
<option value=">" {eq name="where_field.op" value=">"}selected{/eq}>大于</option>
<option value=">=" {eq name="where_field.op" value=">="}selected{/eq}>大于等于</option>
<option value="<" {eq name="where_field.op" value="<"}selected{/eq}>小于</option>
<option value="<=" {eq name="where_field.op" value="<="}selected{/eq}>小于等于</option>
<option value="LIKE" {eq name="where_field.op" value="LIKE"}selected{/eq}>LIKE - 模糊查询</option>
<!-- <option value="EXP" {eq name="where_field.op" value="EXP"}selected{/eq}>表达式 - 支持SQL语法</option> -->
</select>
<span class="field_prompt">查询条件:</span>
<input placeholder="" class="form-control where_field_condition" name="row[where_field][condition][{$where_field_name}]" value="{$where_field.condition}">
</div>
</div>
{/foreach}
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="tab4">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">排序字段:</label>
<div class="col-xs-12 col-sm-7">
<select class="form-control order_field" name="row[order_field]" id="order_field">
{foreach name="allField" item="all_field" key="all_field_name"}
<option value="{$all_field_name}" {eq name="row.order_field" value="$all_field_name" }selected{/eq}>{$all_field.field_name}</option>
{/foreach}
</select>
<select class="form-control order_field_condition" name="row[order_type]">
<option value="DESC" {eq name="row.order_type" value="DESC" }selected{/eq}>倒序(从大到小)</option>
<option value="ASC" {eq name="row.order_type" value="ASC" }selected{/eq}>正序(从小到大)</option>
</select>
<span class="help-block">请先配置源表和关联表,随后可在此处设置以某字段进行排序</span>
</div>
</div>
<div class="form-group xls_max_number">
<label for="c-xls_max_number" class="control-label col-xs-12 col-sm-3">单个xls文件保存:</label>
<div class="col-xs-12 col-sm-7">
<div class="input-group input-groupp-md">
<input id="c-xls_max_number" data-rule="required;range(1~30000)" class="form-control" name="row[xls_max_number]" type="number" value="{$row.xls_max_number}">
<span class="input-group-addon">条记录</span>
</div>
<span class="help-block">若导出记录数超出以上设置,则自动分为多个xls文件保存</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">xls文件创建并发:</label>
<div class="col-xs-12 col-sm-7">
<input data-rule="required;range(1~100)" class="form-control" name="row[xls_create_concurrent]" type="number" value="{$row.xls_create_concurrent}">
<span class="help-block clear_margin_bottom">若有多个xls文件需要准备,此处设置<strong>同一时间</strong>准备的xls文件数</span>
<span class="help-block clear_margin_top">若单个xls文件需保存2万以上数据,普通服务器请设置为`1`(受磁盘IO限制)</span>
</div>
</div>
<div class="form-group">
<label for="c-memory_limit" class="control-label col-xs-12 col-sm-3">脚本内存限制:</label>
<div class="col-xs-12 col-sm-7">
<div class="input-group memory_limit input-groupp-md">
<input id="c-memory_limit" data-rule="required;checkMemory" data-rule-checkMemory="
function (e) {
var fieldCount = Number(Config.fieldCount);
if (fieldCount <= 0){
return '请选择要导出的字段';
}
var memory = (fieldCount * Number($('#c-xls_max_number').val())) / 1024;
var memory_limit = Number(e.value);
if (memory >= Number(memory_limit)){
return '需要更多内存 >' + (memory + 50).toFixed(0) + 'Mb';
}
}
" class="form-control" name="row[memory_limit]" type="number" value="{$row.memory_limit}">
<span class="input-group-addon">Mb</span>
</div>
<span class="help-block clear_margin_bottom">创建单个xls文件时,允许使用的最大内存量</span>
<span class="help-block clear_margin_top">若提示需要更多的内存,请在硬件条件允许的情况下,设置更高的值;或降低单个xls文件保存的记录数</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">导出:</label>
<div class="col-xs-12 col-sm-7">
<div class="input-group input-groupp-md">
<input class="form-control" name="row[export_number]" type="number" value="{$row.export_number}">
<span class="input-group-addon">条记录</span>
</div>
<span class="help-block">需要导出的数据量,不填写为导出全部</span>
</div>
</div>
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
\ No newline at end of file
... ...
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
<a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('fast_export/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('fast_export/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('fast_export/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
<!-- <a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('fast_export/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a> -->
<!-- <div class="dropdown btn-group {:$auth->check('fast_export/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div> -->
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('fast_export/edit')}"
data-operate-del="{:$auth->check('fast_export/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
... ...
<style type="text/css">
#ribbon{
display: none;
}
.panel-body{
padding-top: 0px;
}
.badge-info {
background-color: #3498db;
}
.badge-success {
background-color: #18bc9c;
}
.badge-warning {
background-color: #e74c3c;
}
.retry_task{
float: right;
margin-left: 10px;
cursor: pointer;
}
.status_button{
width: 130px;
display: none;
}
</style>
<div class="container">
<div class="row">
<div class="box box-success">
<div class="panel-heading tabbable">
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{$task.name}</strong>任务进行中,关闭此页面可能导致任务无法完成!若意外关闭,您可以随时通过点击后台的`上次执行进度`回到此页继续执行任务。
</div>
</div>
<div class="panel-body">
<ul class="list-group">
{foreach $task['subtask'] as $subtask}
<li class="list-group-item">
<span class="badge subtask_{$subtask.id}_status{switch name='subtask.status'}
{case value='1'} badge-info{/case}
{case value='2'} badge-success{/case}
{case value='3'} badge-warning{/case}
{default /}
{/switch}">{$subtask.status_text}</span>
{$subtask.id}. 第{$subtask.min}至{$subtask.min + $subtask.max}条记录
</li>
{/foreach}
</ul>
<div class="progress">
<div id="task_progress" class="progress-bar progress-bar-success progress-bar-striped active" role="progressbar" aria-valuemin="5" aria-valuemax="100" style="min-width: 5%;"></div>
</div>
<a href="{$task.file}" class="btn btn-success center-block status_button">下载任务数据包</a>
</div>
</div>
</div>
</div>
\ No newline at end of file
... ...
... ... @@ -57,7 +57,7 @@
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Spec_type')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-spec_type" data-rule="required" class="form-control selectpicker" name="row[spec_type]">
{foreach name="specTypeList" item="vo"}
<option value="{$key}" {in name="key" value="10"}selected{/in}>{$vo}</option>
... ... @@ -171,7 +171,7 @@
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Deduct_stock_type')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-deduct_stock_type" data-rule="required" class="form-control selectpicker" name="row[deduct_stock_type]">
{foreach name="deductStockTypeList" item="vo"}
<option value="{$key}" {in name="key" value="20"}selected{/in}>{$vo}</option>
... ... @@ -250,43 +250,58 @@
<input id="c-number" data-rule="required" class="form-control form-control" name="row[number]" type="text" value="">
</div>
</div>
<!-- <div class="form-group">-->
<!-- <label class="control-label col-xs-12 col-sm-2">{:__('Down_image')}:</label>-->
<!-- <div class="col-xs-12 col-sm-8">-->
<!-- <div class="input-group">-->
<!-- <input id="c-down_image" data-rule="required" class="form-control form-control" size="50" name="row[down_image]" type="text">-->
<!-- <div class="input-group-addon no-border no-padding">-->
<!-- <span><button type="button" id="plupload-down_image" class="btn btn-danger plupload" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false" data-preview-id="p-down_image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>-->
<!-- <span><button type="button" id="fachoose-down_image" class="btn btn-primary fachoose" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>-->
<!-- </div>-->
<!-- <span class="msg-box n-right" for="c-down_image"></span>-->
<!-- </div>-->
<!-- <ul class="row list-inline plupload-preview" id="p-down_image"></ul>-->
<!-- <span class="help-block">建议尺寸:宽375,高267</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <label class="control-label col-xs-12 col-sm-2">{:__('Four_images')}:</label>-->
<!-- <div class="col-xs-12 col-sm-8">-->
<!-- <div class="input-group">-->
<!-- <input id="c-four_images" data-rule="required" class="form-control form-control" size="50" name="row[four_images]" type="text">-->
<!-- <div class="input-group-addon no-border no-padding">-->
<!-- <span><button type="button" id="plupload-four_images" class="btn btn-danger plupload" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true" data-preview-id="p-four_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>-->
<!-- <span><button type="button" id="fachoose-four_images" class="btn btn-primary fachoose" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>-->
<!-- </div>-->
<!-- <span class="msg-box n-right" for="c-four_images"></span>-->
<!-- </div>-->
<!-- <ul class="row list-inline plupload-preview" id="p-four_images"></ul>-->
<!-- <span class="help-block">建议尺寸:宽185,高185</span>-->
<!-- </div>-->
<!-- </div>-->
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Down_image')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-down_image" data-rule="required" class="form-control form-control" size="50" name="row[down_image]" type="text">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-down_image" class="btn btn-danger plupload" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false" data-preview-id="p-down_image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-down_image" class="btn btn-primary fachoose" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-down_image"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-down_image"></ul>
<span class="help-block">建议尺寸:宽375,高267</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Four_images')}:</label>
<label class="control-label col-xs-12 col-sm-2">{:__('Detail_images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-four_images" data-rule="required" class="form-control form-control" size="50" name="row[four_images]" type="text">
<input id="c-detail_images" data-rule="required" class="form-control form-control" size="50" name="row[detail_images]" placeholder="图片最多添加十张" type="text">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-four_images" class="btn btn-danger plupload" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true" data-preview-id="p-four_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-four_images" class="btn btn-primary fachoose" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
<span><button type="button" id="plupload-detail_images" data-maxcount="10" class="btn btn-danger plupload" data-input-id="c-detail_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true" data-preview-id="p-detail_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-detail_images" data-maxcount="10" class="btn btn-primary fachoose" data-input-id="c-detail_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-four_images"></span>
<span class="msg-box n-right" for="c-detail_images"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-four_images"></ul>
<ul class="row list-inline plupload-preview" id="p-detail_images"></ul>
<span class="help-block">建议尺寸:宽185,高185</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Goods_status')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="radio">
{foreach name="goodsStatusList" item="vo"}
<label for="row[goods_status]-{$key}"><input id="row[goods_status]-{$key}" name="row[goods_status]" type="radio" value="{$key}" {in name="key" value="10"}checked{/in} /> {$vo}</label>
<label for="row[goods_status]-{$key}"><input id="row[goods_status]-{$key}" name="row[goods_status]" type="radio" value="{$key}" {in name="key" value="10"}checked{/in} /> {$vo}</label>
{/foreach}
</div>
... ... @@ -295,7 +310,7 @@
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Is_delete')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-is_delete" data-rule="required" class="form-control selectpicker" name="row[is_delete]">
{foreach name="isDeleteList" item="vo"}
<option value="{$key}" {in name="key" value="0"}selected{/in}>{$vo}</option>
... ...
... ... @@ -250,33 +250,48 @@
<input id="c-number" data-rule="required" class="form-control form-control" name="row[number]" type="text" value="{$row.number}">
</div>
</div>
<!-- <div class="form-group">-->
<!-- <label class="control-label col-xs-12 col-sm-2">{:__('Down_image')}:</label>-->
<!-- <div class="col-xs-12 col-sm-8">-->
<!-- <div class="input-group">-->
<!-- <input id="c-down_image" data-rule="required" class="form-control form-control" size="50" name="row[down_image]" type="text" value="{$row.down_image}">-->
<!-- <div class="input-group-addon no-border no-padding">-->
<!-- <span><button type="button" id="plupload-down_image" class="btn btn-danger plupload" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false" data-preview-id="p-down_image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>-->
<!-- <span><button type="button" id="fachoose-down_image" class="btn btn-primary fachoose" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>-->
<!-- </div>-->
<!-- <span class="msg-box n-right" for="c-down_image"></span>-->
<!-- </div>-->
<!-- <ul class="row list-inline plupload-preview" id="p-down_image"></ul>-->
<!-- <span class="help-block">建议尺寸:宽375,高267</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <label class="control-label col-xs-12 col-sm-2">{:__('Four_images')}:</label>-->
<!-- <div class="col-xs-12 col-sm-8">-->
<!-- <div class="input-group">-->
<!-- <input id="c-four_images" data-rule="required" class="form-control form-control" size="50" name="row[four_images]" type="text" value="{$row.four_images}">-->
<!-- <div class="input-group-addon no-border no-padding">-->
<!-- <span><button type="button" id="plupload-four_images" class="btn btn-danger plupload" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true" data-preview-id="p-four_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>-->
<!-- <span><button type="button" id="fachoose-four_images" class="btn btn-primary fachoose" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>-->
<!-- </div>-->
<!-- <span class="msg-box n-right" for="c-four_images"></span>-->
<!-- </div>-->
<!-- <ul class="row list-inline plupload-preview" id="p-four_images"></ul>-->
<!-- <span class="help-block">建议尺寸:宽185,高185</span>-->
<!-- </div>-->
<!-- </div>-->
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Down_image')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-down_image" data-rule="required" class="form-control form-control" size="50" name="row[down_image]" type="text" value="{$row.down_image}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-down_image" class="btn btn-danger plupload" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false" data-preview-id="p-down_image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-down_image" class="btn btn-primary fachoose" data-input-id="c-down_image" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-down_image"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-down_image"></ul>
<span class="help-block">建议尺寸:宽375,高267</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Four_images')}:</label>
<label class="control-label col-xs-12 col-sm-2">{:__('Detail_images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-four_images" data-rule="required" class="form-control form-control" size="50" name="row[four_images]" type="text" value="{$row.four_images}">
<input id="c-detail_images" data-rule="required" placeholder="图片最多添加十张" class="form-control form-control" size="50" name="row[detail_images]" type="text" value="{$row.detail_images}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-four_images" class="btn btn-danger plupload" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true" data-preview-id="p-four_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-four_images" class="btn btn-primary fachoose" data-input-id="c-four_images" data-mimetype="image/jpeg,image/png,image/jpg" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
<span><button type="button" id="plupload-detail_images" class="btn btn-danger plupload" data-input-id="c-detail_images" data-mimetype="image/jpeg,image/png,image/jpg" data-maxcount="10" data-multiple="true" data-preview-id="p-detail_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-detail_images" class="btn btn-primary fachoose" data-input-id="c-detail_images" data-mimetype="image/jpeg,image/png,image/jpg" data-maxcount="10" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-four_images"></span>
<span class="msg-box n-right" for="c-detail_images"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-four_images"></ul>
<ul class="row list-inline plupload-preview" id="p-detail_images"></ul>
<span class="help-block">建议尺寸:宽185,高185</span>
</div>
</div>
... ...
... ... @@ -10,7 +10,7 @@
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('litestore/litestoregoods/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('litestore/litestoregoods/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('litestore/litestoregoods/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
<!-- <a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('litestore/litestoregoods/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>-->
<a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('litestore/litestoregoods/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>
<!-- <div class="dropdown btn-group {:$auth->check('litestore/litestoregoods/multi')?'':'hide'}">-->
<!-- <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>-->
... ...
... ... @@ -28,30 +28,30 @@ class Classification extends Api
'code':'1',
'msg':'返回成功'
'data':[
{
"id": 4,
"name": "电子产品",
"is_myself": "0", 0不高亮1高亮
"image_text": ""
},
{
"id": 6,
"name": "水果",
"is_myself": "0",
"image_text": ""
}
{
"id": 4,
"name": "电子产品",
"is_myself": "0", 0不高亮1高亮
"image_text": ""
},
{
"id": 6,
"name": "水果",
"is_myself": "0",
"image_text": ""
}
]
})
*/
public function sort()
{
$id = $this->request->post('id',0);
$id = $this->request->post('id', 0);
$model = new Category();
$list = $model->field('id,name')->order('weigh desc')->select();
foreach ($list as $key => &$value){
$value['is_myself'] = $value['id']==$id?'1':'0';
$list = $model->field('id,name')->order('weigh desc')->select();
foreach ($list as $key => &$value) {
$value['is_myself'] = $value['id'] == $id ? '1' : '0';
}
$this->success('分类列表',$list);
$this->success('分类列表', $list);
}
/**
... ... @@ -66,20 +66,20 @@ class Classification extends Api
'msg':'返回成功'
'data':{
"list": {
"total": 4, 总条数
"per_page": 10,每页数量
"current_page": 1,当前页
"last_page": 1,最后一页
"data": [
{
"goods_id": 21,
"goods_name": "小米Mix3",
"price": "100.00",
"line_price": "1000.00",划线价
"image_text": "图片路径",
"stock_num": 68, 商品总库存
}
]
"total": 4, 总条数
"per_page": 10,每页数量
"current_page": 1,当前页
"last_page": 1,最后一页
"data": [
{
"goods_id": 21,
"goods_name": "小米Mix3",
"price": "100.00",
"line_price": "1000.00",划线价
"image_text": "图片路径",
"stock_num": 68, 商品总库存
}
]
},
"image": "广告图"
}
... ... @@ -87,58 +87,58 @@ class Classification extends Api
*/
public function sortGoodsList()
{
$sort_id = $this->request->post('sort_id',0);
$page = $this->request->post('page',1);
$model = new Goods();
if ($sort_id > 0){
$sort_id = $this->request->post('sort_id', 0);
$page = $this->request->post('page', 1);
$model = new Goods();
if ($sort_id > 0) {
$list = $model
->where('is_delete','0')
->where('category_id',$sort_id)
->where('goods_status','10')
->where('is_delete', '0')
->where('category_id', $sort_id)
->where('goods_status', '10')
->field('goods_id,goods_name,image')
->paginate(10,false,['page'=>$page])
->each(function ($item,$key){
if ($this->auth->isLogin()){
->paginate(10, false, ['page' => $page])
->each(function ($item, $key) {
if ($this->auth->isLogin()) {
$item['cart_number'] = Db::name('cart')
->where('user_id',$this->auth->id)
->where('goods_id',$item['goods_id'])
->where('user_id', $this->auth->id)
->where('goods_id', $item['goods_id'])
->sum('number');
}else{
} else {
$item['cart_number'] = 0;
}
$goods_spec = Db::name('litestore_goods_spec')
->where('goods_id',$item['goods_id'])
$goods_spec = Db::name('litestore_goods_spec')
->where('goods_id', $item['goods_id'])
->find();
$item['price'] = $goods_spec['goods_price'];
$item['price'] = $goods_spec['goods_price'];
$item['line_price'] = $goods_spec['line_price'];
// 总库存
$item->append(['stock_num']);
});
}else{
} else {
$list = $model
->where('is_delete','0')
->where('goods_status','10')
->where('is_delete', '0')
->where('goods_status', '10')
->field('goods_id,goods_name,image')
->paginate(10,false,['page'=>$page])
->each(function ($item,$key){
if ($this->auth->isLogin()){
->paginate(10, false, ['page' => $page])
->each(function ($item, $key) {
if ($this->auth->isLogin()) {
$item['cart_number'] = Db::name('cart')
->where('user_id',$this->auth->id)
->where('goods_id',$item['goods_id'])
->where('user_id', $this->auth->id)
->where('goods_id', $item['goods_id'])
->sum('number');
}else{
} else {
$item['cart_number'] = 0;
}
$goods_spec = Db::name('litestore_goods_spec')
->where('goods_id',$item['goods_id'])
$goods_spec = Db::name('litestore_goods_spec')
->where('goods_id', $item['goods_id'])
->find();
$item['price'] = $goods_spec['goods_price'];
$item['price'] = $goods_spec['goods_price'];
$item['line_price'] = $goods_spec['line_price'];
// 总库存
$item->append(['stock_num']);
});
}
$this->success('分类商品列表',['list'=>$list,'image'=>cdnurl(Config::get('site.advert'),true)]);
$this->success('分类商品列表', ['list' => $list, 'image' => cdnurl(Config::get('site.advert'), true)]);
}
/**
... ... @@ -149,76 +149,76 @@ class Classification extends Api
'code':'1',
'msg':'返回成功'
'data':{
// 规格组合完毕的列表
// 如果sku是空数组 为单规格商品 只需要用list的值
"list": [
{
"goods_spec_id": 103, // 规格列表id
"goods_id": 22,
"goods_no": "SNHW001",
"goods_price": "4499.00",
"line_price": "0.00",
"stock_num": 941, // 库存
"goods_sales": 58,
"goods_weight": 500,
"spec_sku_id": "44_46", // 搜索字段 组合sku里面的id搜索 从小到大排序
"spec_image": "",
"create_time": 1542784591,
"update_time": 1543242861
}
],
// 规格展示的列表
"sku": [
{
"name": "颜色",
"second": [
{
"id": 44,
"name": "亮黑色"
}
]
},
{
"name": "内存",
"second": [
{
"id": 46,
"name": "6GB+64GB"
}
]
}
]
// 规格组合完毕的列表
// 如果sku是空数组 为单规格商品 只需要用list的值
"list": [
{
"goods_spec_id": 103, // 规格列表id
"goods_id": 22,
"goods_no": "SNHW001",
"goods_price": "4499.00",
"line_price": "0.00",
"stock_num": 941, // 库存
"goods_sales": 58,
"goods_weight": 500,
"spec_sku_id": "44_46", // 搜索字段 组合sku里面的id搜索 从小到大排序
"spec_image": "",
"create_time": 1542784591,
"update_time": 1543242861
}
],
// 规格展示的列表
"sku": [
{
"name": "颜色",
"second": [
{
"id": 44,
"name": "亮黑色"
}
]
},
{
"name": "内存",
"second": [
{
"id": 46,
"name": "6GB+64GB"
}
]
}
]
}
})
*/
public function goodsSku()
{
$goods_id = $this->request->post('goods_id');
$goods_id = $this->request->post('goods_id');
$goodsspecrelmodel = new GoodsSpecRel();
$list = $goodsspecrelmodel
->where('goods_id',$goods_id)
$list = $goodsspecrelmodel
->where('goods_id', $goods_id)
->select();
$array = [];
$goods = \app\api\model\Goods::get($goods_id);
if ($goods['spec_type'] == 20){
foreach ($list as $key => $value){
if (!isset($array[$value['spec_id']])){
$array = [];
$goods = \app\api\model\Goods::get($goods_id);
if ($goods['spec_type'] == 20) {
foreach ($list as $key => $value) {
if (!isset($array[$value['spec_id']])) {
$array[$value['spec_id']]['name'] = Db::name('litestore_spec')
->where('id',$value['spec_id'])
->where('id', $value['spec_id'])
->value('spec_name');
}
$spec_value =Db::name('litestore_spec_value')
->where('id',$value['spec_value_id'])
$spec_value = Db::name('litestore_spec_value')
->where('id', $value['spec_value_id'])
->value('spec_value');
$array[$value['spec_id']]['second'][] = [
'id' => $value['spec_value_id'],
'name' => $spec_value
'id' => $value['spec_value_id'],
'name' => $spec_value,
];
}
$array = array_values($array);
}
$goods_spec = GoodsSpec::all(['goods_id'=>$goods_id]);
$this->success('商品规格',['list'=>$goods_spec,'sku'=>$array]);
$goods_spec = GoodsSpec::all(['goods_id' => $goods_id]);
$this->success('商品规格', ['list' => $goods_spec, 'sku' => $array]);
}
}
}
\ No newline at end of file
... ...
... ... @@ -18,43 +18,53 @@ class Goods extends Model
'image_text',
'images_text',
'down_image_text',
'four_image_text'
'four_image_text',
'detail_images_text',
];
public function getImageTextAttr($value,$data)
public function getImageTextAttr($value, $data)
{
$value = !empty($data['image']) ? cdnurl($data['image'],true):'';
$value = !empty($data['image']) ? cdnurl($data['image'], true) : '';
return $value;
}
public function getFourImageTextAttr($value,$data)
public function getFourImageTextAttr($value, $data)
{
$value = !empty($data['four_images']) ? explode(',',$data['four_images']):[];
foreach ($value as $key => &$val){
$val = cdnurl($val,true);
$value = !empty($data['four_images']) ? explode(',', $data['four_images']) : [];
foreach ($value as $key => &$val) {
$val = cdnurl($val, true);
}
return $value;
}
public function getImagesTextAttr($value,$data)
public function getDetailImageTextAttr($value, $data)
{
$value = !empty($data['images']) ? explode(',',$data['images']):[];
foreach ($value as $key => &$val){
$val = cdnurl($val,true);
$value = !empty($data['detail_images']) ? explode(',', $data['detail_images']) : [];
foreach ($value as $key => &$val) {
$val = cdnurl($val, true);
}
return $value;
}
public function getDownImageTextAttr($value,$data)
public function getImagesTextAttr($value, $data)
{
$value = !empty($data['down_image']) ? cdnurl($data['down_image'],true):'';
$value = !empty($data['images']) ? explode(',', $data['images']) : [];
foreach ($value as $key => &$val) {
$val = cdnurl($val, true);
}
return $value;
}
public function getDownImageTextAttr($value, $data)
{
$value = !empty($data['down_image']) ? cdnurl($data['down_image'], true) : '';
return $value;
}
// 总库存
public function getStockNumAttr($value,$data)
public function getStockNumAttr($value, $data)
{
$value = GoodsSpec::where('goods_id',$data['goods_id'])->sum('stock_num');
$value = GoodsSpec::where('goods_id', $data['goods_id'])->sum('stock_num');
return $value;
}
}
\ No newline at end of file
... ...
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]
</IfModule>
\ No newline at end of file
... ...
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'bootstrap-select', 'bootstrap-select-lang'], function ($, undefined, Backend, Table, Form) {
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: 'fastexport/index' + location.search,
add_url: 'fastexport/add',
edit_url: 'fastexport/edit',
del_url: 'fastexport/del',
multi_url: 'fastexport/multi',
testTask: 'fastexport/testTask',
performTask: 'fastexport/performTask',
table: 'fastexport',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
pk: 'id',
sortName: 'id',
columns: [
[
{checkbox: true},
{field: 'id', title: __('Id')},
{field: 'admin.username', title: __('Admin_id')},
{field: 'name', title: __('Name')},
{field: 'main_table', title: __('Main Table')},
{field: 'file', title: __('File'), formatter: Table.api.formatter.url, operate:false},
{field: 'progress', title: __('Progress'), operate:'BETWEEN', formatter: Controller.api.formatter.progress},
{field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', formatter: Table.api.formatter.datetime},
{
field: 'operate', title: __('Operate'),
table: table,
events: Table.api.events.operate,
formatter: Table.api.formatter.operate,
buttons: [
{
name: 'testTask',
title: __('testTask'),
icon: 'fa fa-wrench',
classname: 'btn btn-success btn-xs btn-ajax',
url: $.fn.bootstrapTable.defaults.extend.testTask,
confirm: '将导出前10条数据,请目检数据是否正常且完整~',
refresh: true,
success: function (data, ret) {
if (ret.code == 1 && data.direct_export) {
window.location = 'fastexport/performSubTask/subtask_id/1/direct_export/true/task_id/' + data.id;
} else if (ret.msg) {
Layer.alert(ret.msg);
} else {
Layer.alert('未知错误!');
}
return false;
},
error: function (data, ret) {
Layer.alert(ret.msg);
return false;
}
},
{
name: 'performTask',
title: __('performTask'),
icon: 'fa fa-play-circle',
classname: 'btn btn-xs btn-danger btn-ajax',
url: $.fn.bootstrapTable.defaults.extend.performTask,
confirm: '确认开始执行任务吗?(数据导出属高IO操作,若本任务数据量超过2万,请择服务器相对闲时执行!)',
refresh: true,
success: function (data, ret) {
if (ret.code == 1) {
if (data.direct_export) {
Fast.api.msg('任务已开始,请稍等片刻...')
window.location = 'fastexport/performSubTask/subtask_id/1/direct_export/true/task_id/' + data.id;
} else {
window.open("fastexport/taskControl/task_id/" + data.id,"_blank");
}
} else {
Layer.alert(ret.msg);
}
return false;
},
error: function (data, ret) {
Layer.alert(ret.msg);
return false;
}
},
]
}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},
add: function () {
Controller.api.bindevent();
Config.fieldCount = 0;
},
edit: function () {
Controller.api.bindevent();
// 接受透传过来的数据表字段数据
Controller.api.tablieField = Config.fields.table_field;
Controller.api.joinTablieField = Config.fields.join_table_field;
Controller.api.readyJoinTable = Config.fields.join_table_number;
Config.fieldCount = Config.fields.field_count;
},
taskcontrol: function () {
if (Config.subtask_page) {
$('#task_progress').width(Config.task.progress + '%').html(Config.task.progress + '%');
Controller.api.performSubTask(1);
// 弹窗使用户触发鼠标点击事件以激活 beforeunload 事件
// Chrome必须是鼠标点击事件,其他浏览器键盘按下事件也可以激活
layer.alert('任务进行中,您可以最小化此页,但关闭此页可能导致任务失败!');
$(window).on('beforeunload', function () {
return '导出任务尚未完成,您确定要离开此页吗?';
});
}
$(document).on('click', '.retry_task', function () {
var subtask_id = $(this).data('subtask_id');
$.ajax({
url: "fastexport/performSubTask/task_id/" + Config.task.id + "/subtask_id/" + subtask_id,
dataType: "json",
complete: function (ret) {
ret = ret.responseJSON;
try {
if (ret.code == 1) {
$('.subtask_' + ret.data.subtask_id + '_status').removeClass('badge-warning').addClass('badge-success').html('已完成');
Controller.api.toggleError('hide');
Controller.api.checkTask();
} else {
$('.subtask_' + ret.data.subtask_id + '_status')
.removeClass('badge-info')
.addClass('badge-warning')
.html(ret.msg);
Controller.api.toggleError('show');
}
if (ret.data.error_msg) {
Layer.alert(ret.data.subtask_id + '号子任务出错:' + ret.data.error_msg);
}
} catch (err){
Layer.alert('出错啦,请检查网络请求中的报错详情!');
return ;
}
return false;
}
});
})
},
api: {
formatter: {
progress: function (value, row) {
var span_class = 'text-info';
if (value >= 100) {
span_class = 'text-success';
}
if (value >= 5) {
return '<a target="_blank" href="fastexport/taskControl/task_id/' + row.id + '" class="searchit ' + span_class + '" data-toggle="tooltip" title="" data-original-title="点击进入任务控制器">' + value + '%</a>';
} else {
return '<a href="javascript:;" class="searchit ' + span_class + '" data-toggle="tooltip" title="" data-original-title="请先开始任务">' + value + '%</a>';
}
}
},
checkTask: function () {
$.ajax({
url: "fastexport/taskFilePack/task_id/" + Config.task.id,
complete: function (ret) {
ret = ret.responseJSON;
try {
if (ret.code == 1) {
Layer.alert('任务完成,文件已打包,请点击页底按钮进行下载。');
$('#task_progress').width('100%').html('100%');
Config.task.progress = 100;
$(window).unbind('beforeunload');
$('.status_button').attr('href', ret.data.file_name).css('display', 'block').fadeIn();
} else {
$('.status_button').hide();
}
} catch (err){
Layer.alert('出错啦,请检查网络请求中的报错详情!');
return ;
}
}
})
},
toggleError: function (type = 'show') {
if (type == 'show') {
Controller.api.task_error_count++;
$('#task_progress').removeClass('progress-bar-success').addClass('progress-bar-danger');
$('.status_button').hide();
} else {
// 检查能否取消显示
Controller.api.task_error_count = (Controller.api.task_error_count - 1) < 0 ? 0:Controller.api.task_error_count - 1;
if (Controller.api.task_error_count <= 0) {
$('#task_progress').removeClass('progress-bar-danger').addClass('progress-bar-success');
}
}
},
performSubTask: function (current_page) {
if (Config.subtask_page[current_page]) {
for (let i in Config.subtask_page[current_page]){
var subtask_id = Config.subtask_page[current_page][i]['id'];
$('.subtask_' + subtask_id + '_status').removeClass('badge-success').addClass('badge-info').html('进行中');
$.ajax({
url: "fastexport/performSubTask/task_id/" + Config.task.id + "/subtask_id/" + subtask_id,
dataType: "json",
complete: function (ret) {
ret = ret.responseJSON;
try {
if (ret.code == 1) {
$('.subtask_' + ret.data.subtask_id + '_status').removeClass('badge-info').addClass('badge-success').html('已完成');
Controller.api.toggleError('hide');
} else {
$('.subtask_' + ret.data.subtask_id + '_status')
.removeClass('badge-info')
.addClass('badge-warning')
.html(ret.msg)
.before('<a href="javascript:;" data-subtask_id="' + ret.data.subtask_id + '" class="retry_task">重试</a>');
Controller.api.toggleError('show');
}
if (ret.data.error_msg) {
Layer.alert(ret.data.subtask_id + '号子任务出错:' + ret.data.error_msg);
}
} catch (err){
Layer.alert('出错啦,请检查网络请求中的报错详情!');
return ;
}
Config.task.progress = parseFloat(Config.task.progress) + parseFloat(Config.task.item_subtask_progress);
Config.task.progress = (Config.task.progress > 100) ? 100 : Config.task.progress.toFixed(2);
$('#task_progress').width(Config.task.progress + '%').html(Config.task.progress + '%');
// 检查同页的所有子任务是否都已完成
var subtasks = Config.subtask_page[current_page];
var is_done = true;
for (let i in subtasks){
if (subtasks[i].id == ret.data.subtask_id) {
Config.subtask_page[current_page][i]['status'] = 2;
}
if (Config.subtask_page[current_page][i]['status'] != 2) {
is_done = false;
}
}
if (is_done) {
Controller.api.performSubTask(current_page + 1);
}
return false;
}
});
}
} else {
Controller.api.checkTask()
}
},
task_error_count: 0,
readyJoinTable: 0,
tablieField: [],
joinTablieField: [],
bulidField: function (field, item, name, list_el, show_reduction = true) {
if (list_el.find("input[name='" + name[0] + "']").length) {
return ;
}
Config.fieldCount++;
// <option value="3"' + (item.discerns == 3 ? 'selected="selected"':'') + '>图片</option>\ // 图片导出过慢,弃用
var el = $('\
<div class="form-group" data-field="' + field + '">\
<label class="control-label col-xs-12 col-sm-2">' + (show_reduction ? '<a class="reduction_field" href="javascript:;">[-]</a>':'') + field + ':</label>\
<div class="col-xs-12 col-sm-9">\
<input placeholder="字段标题" class="form-control field_title" name="' + name[0] + '" value="'+item.name+'">\
<span class="field_prompt">数据识别:</span>\
<select name="' + name[1] + '" class="form-control field_discerns">\
<option value="0"' + (item.discerns == 0 ? 'selected="selected"':'') + '>文本</option>\
<option value="1"' + (item.discerns == 1 ? 'selected="selected"':'') + '>数字</option>\
<option value="2"' + (item.discerns == 2 ? 'selected="selected"':'') + '>日期</option>\
<option value="4"' + (item.discerns == 4 ? 'selected="selected"':'') + '>文件</option>\
<option value="5"' + (item.discerns == 5 ? 'selected="selected"':'') + '>赋值</option>\
</select>\
<span class="field_prompt">赋值方案:</span>\
<input placeholder="数据识别为“赋值”时,才需填写" class="form-control field_scheme" name="' + name[2] + '" value="'+item.comment+'">\
</div>\
</div>\
');
list_el.append(el);
},
bulidWhereFieldInput: function (field) {
let input_op_name = 'row[where_field][op][' + field + ']';
let input_condition_name = 'row[where_field][condition][' + field + ']';
if ($('#where_field_input').find("input[name='" + input_condition_name + "']").length) {
return ;
}
// <option value="EXP">表达式 - 支持SQL语法</option>\
var el = $('\
<div class="form-group" data-field="' + field + '">\
<label class="control-label col-xs-12 col-sm-3">' + field + ':</label>\
<div class="col-xs-12 col-sm-7">\
<select name="' + input_op_name + '" class="form-control where_field_op">\
<option value="=">等于</option>\
<option value="<>">不等于</option>\
<option value=">">大于</option>\
<option value=">=">大于等于</option>\
<option value="<">小于</option>\
<option value="<=">小于等于</option>\
<option value="LIKE">LIKE - 模糊查询</option>\
</select>\
<span class="field_prompt">查询条件:</span>\
<input placeholder="" class="form-control where_field_condition" name="' + input_condition_name + '" value="">\
</div>\
</div>\
');
$('#where_field_input').append(el);
},
bulidFieldOption: function (select_el, field, item) {
let option_text = field + ' - ' + item.name
$(select_el).append("<option value='" + field + "'>" + option_text + "</option>");
},
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
$(document).on('click', '.reduction_field', function () {
$(this).parent().parent().remove()
Config.fieldCount--;
});
$(document).on('change', '.table_name_as', function () {
// 可筛选字段和排序字段更新
Controller.api.bulidWhereField();
});
/*选择关联表*/
$(document).on('change', '.s-join_table', function () {
// 获取被选表的字段信息->加载到对应的关联主键和取值字段 select 控件上->返回无需额外记录
var table_number_id = $(this).data('id');
$('#field_list_table_' + table_number_id).html('');
var table_name = $(this).val();
Fast.api.ajax({
url: "fastexport/getTableFields",
data: {
table: table_name
},
}, function (data, ret) {
Controller.api.joinTablieField[table_name] = data.fieldlist_comment;
// 可筛选字段和排序字段更新
Controller.api.bulidWhereField();
// 构建关联表字段的 option
$('.local_key_table_' + table_number_id).empty();
for (let y in data.fieldlist_comment){
Controller.api.bulidFieldOption('.local_key_table_' + table_number_id, y, data.fieldlist_comment[y]);
}
// 构建关联表字段的 option
var fields_table_id = '#fields_table_' + table_number_id;
$(fields_table_id).empty();
for (let y in data.fieldlist_comment){
Controller.api.bulidFieldOption(fields_table_id, y, data.fieldlist_comment[y]);
}
$(fields_table_id).attr('multiple','true');
$(fields_table_id).find("option:selected").attr("selected", false);
$(fields_table_id).trigger("change");
if ($(fields_table_id).data("selectpicker")) {
$(fields_table_id).selectpicker('refresh');
} else {
$(fields_table_id, $("form[role=form]")).selectpicker();
}
return false;
});
});
/*选择关联表 end*/
/*选择关联表字段*/
$(document).on('change', 'select.s-join_table_field', function (e) {
var select_fields = $(this).val();
var table_number_id = $(this).data('id');
var join_table_name = $('#s-join_table_' + table_number_id).val();
// 找到取消勾选的字段,删除配置框
var has_been_building_fields = $('#field_list_table_' + table_number_id).children(".form-group");
for (var x = 0; x < has_been_building_fields.length; x++) {
if (select_fields) {
if (!select_fields.includes($(has_been_building_fields[x]).data('field'))) {
$(has_been_building_fields[x]).remove()
Config.fieldCount--;
}
} else {
$(has_been_building_fields[x]).remove();
Config.fieldCount--;
}
}
for(let i in select_fields) {
let field = select_fields[i]
if (parseInt(field) != 0) {
if (!Controller.api.joinTablieField[join_table_name][field]) {
Fast.api.msg('字段未找不到啦~');
continue;
}
Controller.api.bulidField(field, Controller.api.joinTablieField[join_table_name][field], {
0: 'row[join_table][join_table_'+ table_number_id +'][fields][title][' + field + ']',
1: 'row[join_table][join_table_'+ table_number_id +'][fields][discerns][' + field + ']',
2: 'row[join_table][join_table_'+ table_number_id +'][fields][scheme][' + field + ']'
}, $('#field_list_table_' + table_number_id), false);
}
}
});
/*选择关联表字段 end*/
/*选择筛选项*/
$(document).on('change', '#where_field', function () {
var where_field = $(this).val();
// 找到取消勾选的字段,删除配置框
var has_been_building_fields = $('#where_field_input').children(".form-group");
for (var x = 0; x < has_been_building_fields.length; x++) {
if (where_field) {
if (!where_field.includes($(has_been_building_fields[x]).data('field'))) {
$(has_been_building_fields[x]).remove()
}
} else {
$(has_been_building_fields[x]).remove();
}
}
for(let i in where_field){
if (where_field[i] != '0') {
Controller.api.bulidWhereFieldInput(where_field[i]);
}
}
});
/*选择筛选项 end*/
/*选择源表*/
$('#c-main_table').on('change', function (e) {
var table = $(this).val();
var comment = $("#c-main_table option:selected").data('comment');
if (table == 'none') {
$('#field_config').html('');
}
// 加载table的字段数据
Fast.api.ajax({
url: "fastexport/getTableFields",
data: {
table: table
},
}, function (data, ret) {
Controller.api.tablieField = data.fieldlist_comment;
$('#field_config').html('');
// 构建字段配置输入框
for (let i in data.fieldlist_comment){
Controller.api.bulidField(i, data.fieldlist_comment[i], {
0:'row[field_config][title][' + i + ']',
1:'row[field_config][discerns][' + i + ']',
2:'row[field_config][scheme][' + i + ']',
}, $('#field_config'));
}
// 关联表配置的源表字段更新
$('.foreign_key').empty();
Controller.api.bulidForeignKeyOption();
// 可筛选字段和排序字段更新
Controller.api.bulidWhereField();
return false;
});
});
/*选择源表 end*/
/*设置关联表数量*/
$('#c-join_table_number').on('change', function () {
var join_table_number = $(this).val();
if (join_table_number == Controller.api.readyJoinTable) {
return ;
} else if (join_table_number < Controller.api.readyJoinTable) {
for (let i = Controller.api.readyJoinTable; i > join_table_number; i--) {
$('.join_table_' + i).remove();
}
Controller.api.readyJoinTable = join_table_number;
return ;
}
// 构建关联表的输入框
for (var i = (Number(Controller.api.readyJoinTable) + 1); i <= join_table_number; i++) {
// 构建关联表输入框
Controller.api.bulidJoinDiv(i);
Controller.api.bulidForeignKeyOption('.foreign_key_table_' + i);
}
});
/*设置关联表数量 end*/
},
// 可筛选字段和排序字段更新
bulidWhereField: function () {
var source_table = $('#c-main_table').val();
var where_field = $('#where_field');
where_field.empty();
for(let i in Controller.api.tablieField){
let option_text = source_table + '.' + i + ' - ' + Controller.api.tablieField[i].name;
where_field.append("<option value='" + source_table + '.' + i + "'>" + option_text + "</option>");
}
// 查找已选择关联表->获取该表的所有字段渲染
var join_table_number = $('#c-join_table_number').val();
for(let i=1; i <= join_table_number; i++) {
let table_name = $('#s-join_table_' + i).val();
let table_name_as = $('#s-join_table_as_' + i).val();
table_name_as = table_name_as ? table_name_as : table_name;
if (table_name != 'none') {
for(let y in Controller.api.joinTablieField[table_name]){
let option_text = table_name_as + '.' + y + ' - ' + Controller.api.joinTablieField[table_name][y].name;
where_field.append("<option value='" + table_name_as + '.' + y + "'>" + option_text + "</option>");
}
}
}
// 更新可选的排序字段
$('#order_field').empty();
$('#order_field').html(where_field.html());
$(where_field).trigger("change");
if ($(where_field).data("selectpicker")) {
$(where_field).selectpicker('refresh');
}
},
bulidForeignKeyOption: function (foreign_key_class = null) {
if (foreign_key_class) {
let last_option = $(foreign_key_class +" option:last").val();
if (!last_option || last_option == '0') {
for (let i in Controller.api.tablieField) {
let option_text = i + ' - ' + Controller.api.tablieField[i].name;
$(foreign_key_class).append("<option value='" + i + "'>" + option_text + "</option>");
}
}
} else {
// 更新全部
for (let i in Controller.api.tablieField) {
let option_text = i + ' - ' + Controller.api.tablieField[i].name;
$('.foreign_key').append("<option value='" + i + "'>" + option_text + "</option>");
}
}
},
bulidJoinDiv: function (table_id) {
var el = $('\
<hr class="divider join_table_' + table_id + '">\
<div class="form-group join_table_' + table_id + '">\
<label class="control-label col-xs-12 col-sm-2">选择关联表:</label>\
<div class="col-xs-12 col-sm-8">\
<select data-id="'+table_id+'" id="s-join_table_'+table_id+'" class="form-control s-join_table" name="row[join_table][join_table_'+table_id+'][table]">\
' + $('#c-main_table').html() + '\
</select>\
</div>\
</div>\
<div class="form-group join_table_' + table_id + '">\
<label class="control-label col-xs-12 col-sm-2">关联表别名:</label>\
<div class="col-xs-12 col-sm-8">\
<input placeholder="非必填,取好别名则关联表可与源表相同" class="form-control table_name_as" id="s-join_table_as_'+table_id+'" name="row[join_table][join_table_'+table_id+'][join_as]" type="text" value="">\
</div>\
</div>\
<div class="form-group join_table_' + table_id + '">\
<label class="control-label col-xs-12 col-sm-2">关联外键:</label>\
<div class="col-xs-12 col-sm-8">\
<select class="form-control foreign_key foreign_key_table_'+table_id+'" name="row[join_table][join_table_'+table_id+'][foreign_key]">\
<option value="0">请选择</option>\
</select>\
<span class="help-block">若无字段可选,请先选择数据源表</span>\
</div>\
</div>\
<div class="form-group join_table_' + table_id + '">\
<label class="control-label col-xs-12 col-sm-2">关联主键:</label>\
<div class="col-xs-12 col-sm-8">\
<select class="form-control local_key local_key_table_'+table_id+'" name="row[join_table][join_table_'+table_id+'][local_key]">\
<option value="0">请先选择关联表</option>\
</select>\
</div>\
</div>\
<div class="form-group join_table_' + table_id + '">\
<label class="control-label col-xs-12 col-sm-2">JOIN类型:</label>\
<div class="col-xs-12 col-sm-8">\
<select class="form-control" name="row[join_table][join_table_'+table_id+'][join_type]">\
<option value="INNER">INNER - 至少一个匹配</option>\
<option value="LEFT">LEFT - 左表有匹配</option>\
<option value="RIGHT">RIGHT - 右表有匹配</option>\
<option value="FULL">FULL - 任意表有匹配</option>\
</select>\
</div>\
</div>\
<div class="form-group join_table_' + table_id + '">\
<label class="control-label col-xs-12 col-sm-2">取值字段:</label>\
<div class="col-xs-12 col-sm-8">\
<select data-id="' + table_id + '" class="form-control s-join_table_field" id="fields_table_'+table_id+'">\
<option value="0">请先选择关联表</option>\
</select>\
</div>\
</div>\
<div class="join_table_' + table_id + '" id="field_list_table_'+table_id+'"></div>\
');
$('#join_table').append(el)//插入
Controller.api.readyJoinTable = table_id;
},
}
};
return Controller;
});
\ No newline at end of file
... ...
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
break;
}
\ No newline at end of file