页面设计可下载、导入、在线安装

feat/task1-c-wallet
gongfuxiang 2022-04-19 18:38:30 +08:00
parent 0f8f18ce3b
commit 2b58cb510d
15 changed files with 568 additions and 12 deletions

View File

@ -13,6 +13,7 @@ namespace app\admin\controller;
use app\service\DesignService;
use app\service\GoodsService;
use app\service\BrandService;
use app\service\StoreService;
use app\layout\service\BaseLayout;
/**
@ -76,6 +77,9 @@ class Design extends Common
];
$ret = DesignService::DesignList($data_params);
// 应用商店
MyViewAssign('store_design_url', StoreService::StoreDesignUrl());
// 基础参数赋值
MyViewAssign('params', $this->data_request);
MyViewAssign('page_html', $page->GetPageHtml());
@ -163,6 +167,24 @@ class Design extends Common
return MyView();
}
/**
* 下载
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-17
* @desc description
*/
public function Download()
{
$ret = DesignService::DesignDownload($this->data_request);
if(isset($ret['code']) && $ret['code'] != 0)
{
MyViewAssign('msg', $ret['msg']);
return MyView('public/tips_error');
}
}
/**
* 保存
* @author Devil
@ -222,5 +244,45 @@ class Design extends Common
// 开始操作
return DesignService::DesignDelete($this->data_post);
}
/**
* 同步到首页
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-19
* @desc description
*/
public function Sync()
{
// 是否ajax请求
if(!IS_AJAX)
{
$this->error('非法访问');
}
// 开始操作
return DesignService::DesignSync($this->data_post);
}
/**
* 导入
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-19
* @desc description
*/
public function Upload()
{
// 是否ajax请求
if(!IS_AJAX)
{
$this->error('非法访问');
}
// 开始操作
return DesignService::DesignUpload($this->data_request);
}
}
?>

View File

@ -44,6 +44,7 @@ class Design
'is_delete' => 1,
'delete_url' => MyUrl('admin/design/delete'),
'delete_key' => 'ids',
'is_middle' => 0,
],
// 表单配置
'form' => [
@ -55,6 +56,17 @@ class Design
'align' => 'center',
'width' => 80,
],
[
'label' => '数据ID',
'view_type' => 'field',
'view_key' => 'id',
'width' => 105,
'is_sort' => 1,
'search_config' => [
'form_type' => 'input',
'where_type' => '=',
],
],
[
'label' => '基础信息',
'view_type' => 'module',

View File

@ -39,6 +39,7 @@ class Payment
'base' => [
'key_field' => 'payment',
'status_field' => 'is_enable',
'is_middle' => 0,
],
// 表单配置
'form' => [

View File

@ -3,7 +3,43 @@
<!-- 表单顶部操作栏 -->
{{block name="form_operate_top"}}
<button type="button" class="am-btn am-btn-success am-btn-xs am-icon-cloud-upload am-radius" data-am-modal="{target: '#popup-design-upload'}"> 导入</button>
<a href="{{:MyUrl('admin/design/saveinfo')}}" class="am-btn am-btn-secondary am-radius am-btn-xs am-icon-plus" target="_blank"> 新增</a>
<!-- 父级内容 -->
{__block__}
<a class="am-margin-left-sm" href="{{$store_design_url}}" target="_blank">更多设计模板下载 <i class="am-icon-external-link"></i></a>
{{/block}}
<!-- 扩展 -->
{{block name="form_extend"}}
<!-- upload start -->
<div class="am-popup am-radius" id="popup-design-upload">
<div class="am-popup-inner">
<div class="am-popup-hd">
<h4 class="am-popup-title">导入</h4>
<span data-am-modal-close class="am-close">&times;</span>
</div>
<div class="am-popup-bd">
<!-- win form start -->
<form class="am-form form-validation" action="{{:MyUrl('admin/design/upload')}}" method="POST" request-type="ajax-reload" enctype="multipart/form-data">
<div class="am-alert am-radius am-alert-tips">
<p class="am-margin-top-xs">1. 选择已下载的页面设计zip包</p>
<p class="am-margin-top-xs">2. 导入将自动新增一条数据</p>
</div>
<div class="am-form-group">
<div class="am-form-file">
<button type="button" class="am-btn am-btn-default am-btn-sm am-radius"><i class="am-icon-cloud-upload"></i> 选择文件</button>
<input type="file" name="file" class="file-event" data-tips-tag="#form-payment-tips" data-validation-message="请选择需要上传的文件" accept=".zip" multiple required />
<div id="form-payment-tips" class="am-margin-top-xs"></div>
</div>
</div>
<div class="am-form-group am-form-group-refreshing am-margin-top-lg am-padding-horizontal-0">
<button type="submit" class="am-btn am-btn-primary am-radius btn-loading-example am-btn-sm am-btn-block" data-am-loading="{loadingText:'处理中...'}">确认</button>
</div>
</form>
<!-- win form end -->
</div>
</div>
</div>
<!-- upload end -->
{{/block}}

View File

@ -4,6 +4,14 @@
<i class="am-icon-eye"></i>
<span>查看</span>
</a>
<button type="button" class="am-btn am-btn-warning am-btn-xs am-radius am-btn-block submit-ajax" data-url="{{:MyUrl('admin/design/sync')}}" data-id="{{$module_data.id}}" data-msg="数据同步到首页拖拽可视化中、之后再修改数据不受影响、但是不要删除相关附件">
<i class="am-icon-refresh"></i>
<span>同步</span>
</button>
<a class="am-btn am-btn-success am-btn-xs am-radius am-btn-block" href="{{:MyUrl('admin/design/download', array_merge($params, ['id'=>$module_data['id']]))}}" target="_blank">
<i class="am-icon-download"></i>
<span>下载</span>
</a>
<a class="am-btn am-btn-secondary am-btn-xs am-radius am-btn-block" href="{{:MyUrl('admin/design/saveinfo', array_merge($params, ['id'=>$module_data['id']]))}}" target="_blank">
<i class="am-icon-edit"></i>
<span>编辑</span>

View File

@ -3,7 +3,7 @@
<!-- 表单顶部操作栏 -->
{{block name="form_operate_top"}}
<button type="button" class="am-btn am-btn-secondary am-btn-xs am-icon-cloud-upload am-radius" data-am-modal="{target: '#payment-upload-win'}"> 上传</button>
<button type="button" class="am-btn am-btn-success am-btn-xs am-icon-cloud-upload am-radius" data-am-modal="{target: '#payment-upload-win'}"> 上传</button>
<a class="am-margin-left-sm" href="{{$store_payment_url}}" target="_blank">更多支付插件下载 <i class="am-icon-external-link"></i></a>
<!-- 父级内容 -->
{__block__}
@ -21,7 +21,7 @@
<div class="am-popup-bd">
<!-- win form start -->
<form class="am-form form-validation" action="{{:MyUrl('admin/payment/upload')}}" method="POST" request-type="ajax-reload" enctype="multipart/form-data">
<div class="am-alert am-radius am-alert-tips m-t-0" data-am-alert>
<div class="am-alert am-radius am-alert-tips">
<p>1 类名必须于文件名一致(去除 .php ),如 Alipay.php 则取 Alipay</p>
<p>2 类必须定义的方法</p>
<p class="am-margin-left-lg">2.1 Config 配置方法</p>
@ -32,7 +32,7 @@
<p>3 可自定义输出内容方法</p>
<p class="am-margin-left-lg">3.1 SuccessReturn 支付成功(可选)</p>
<p class="am-margin-left-lg">3.2 ErrorReturn 支付失败(可选)</p>
<p class="am-margin-top-xs cr-red">PS以上条件不满足则无法查看插件将插件放入.zip压缩包中上传、支持一个压缩中包含多个支付插件</p>
<p class="am-margin-top-xs am-text-danger">PS以上条件不满足则无法查看插件将插件放入.zip压缩包中上传、支持一个压缩中包含多个支付插件</p>
</div>
<div class="am-form-group">
<div class="am-form-file">

View File

@ -37,7 +37,7 @@ class Design extends Common
}
/**
* 状态更新
* 详情
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
@ -51,7 +51,8 @@ class Design extends Common
{
$data_params = [
'where' => [
'id' => intval($this->data_request['id']),
['id', '=', intval($this->data_request['id'])],
['is_enable', '=', 1],
],
'm' => 0,
'n' => 1,

View File

@ -884,7 +884,14 @@ class BaseLayout
// 商品分类
case 'category' :
$ret = GoodsService::GoodsDataHandle(Db::name('Goods')->alias('g')->join('goods_category_join gci', 'g.id=gci.goods_id')->field($field)->where($where)->group('g.id')->order($order_by)->limit($m, $n)->select()->toArray());
$request_params = [
'where' => $where,
'm' => $m,
'n' => $n,
'field' => $field,
'order_by' => $order_by,
];
$ret = GoodsService::CategoryGoodsList($request_params);
break;
}
if(!empty($ret) && isset($ret['code']) && $ret['code'] == 0 && !empty($ret['data']))

View File

@ -92,6 +92,7 @@ class ConstService
'2.2.4' => ['value' => '2.2.4', 'name' => 'v2.2.4'],
'2.2.5' => ['value' => '2.2.5', 'name' => 'v2.2.5'],
'2.2.6' => ['value' => '2.2.6', 'name' => 'v2.2.6'],
'2.2.7' => ['value' => '2.2.7', 'name' => 'v2.2.7'],
],
// 用户注册类型列表

View File

@ -249,5 +249,400 @@ class DesignService
{
return 'design-'.$data_id;
}
/**
* 同步到首页
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-19
* @desc description
* @param [array] $params [输入参数]
*/
public static function DesignSync($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '操作id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 获取数据
$data = Db::name('Design')->where(['id'=>intval($params['id'])])->field('config')->find();
if(empty($data))
{
return DataReturn('数据不存在', -1);
}
return LayoutService::LayoutConfigSave('home', $data);
}
/**
* 下载
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-17
* @desc description
* @param [array] $params [输入参数]
*/
public static function DesignDownload($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => '操作id有误',
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 获取数据
$data = Db::name('Design')->where(['id'=>intval($params['id'])])->find();
if(empty($data))
{
return DataReturn('数据不存在', -1);
}
// 目录不存在则创建
$dir = ROOT.'runtime'.DS.'data'.DS.'design'.DS.$data['id'];
\base\FileUtil::CreateDir($dir);
// 临时数据id
$data_id = GetNumberCode(6).time().GetNumberCode(6);
// 解析下载数据
$config = self::ConfigDownloadHandle($data_id, $data['config'], $dir);
// 基础信息
$base = [
'data_id' => $data_id,
'name' => $data['name'],
'logo' => self::FileSave($data_id, $data['logo'], 'images', $dir),
'is_header' => $data['is_header'],
'is_footer' => $data['is_footer'],
'config' => $config,
];
if(@file_put_contents($dir.DS.'config.json', JsonFormat($base)) === false)
{
return DataReturn('配置文件生成失败', -1);
}
// 生成压缩包
$dir_zip = $dir.'.zip';
$zip = new \base\ZipFolder();
if(!$zip->zip($dir_zip, $dir))
{
return DataReturn('压缩包生成失败', -2);
}
// 生成成功删除目录
\base\FileUtil::UnlinkDir($dir);
// 开始下载
if(\base\FileUtil::DownloadFile($dir_zip, $data['name'].'_v'.date('YmdHis').'.zip'))
{
// 删除文件
@unlink($dir_zip);
} else {
return DataReturn('下载失败', -100);
}
return DataReturn('下载成功', 0);
}
/**
* 配置数据下载处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-17
* @desc description
* @param [int] $data_id [数据id]
* @param [array] $config [配置数据]
* @param [string] $dir [存储目录]
*/
public static function ConfigDownloadHandle($data_id, $config, $dir)
{
if(!empty($config))
{
// 非数组则解析
if(!is_array($config))
{
$config = json_decode($config, true);
}
// 开始处理数据
foreach($config as &$v)
{
if(empty($v['children']))
{
continue;
}
foreach($v['children'] as &$vs)
{
if(empty($vs['children']))
{
continue;
}
foreach($vs['children'] as &$vss)
{
if(empty($vss['config']))
{
continue;
}
switch($vss['value'])
{
// 单图
case 'images' :
$vss['config']['content_images'] = self::FileSave($data_id, $vss['config']['content_images'], 'images', $dir);
break;
// 多图
case 'many-images' :
if(!empty($vss['config']['data_list']))
{
foreach($vss['config']['data_list'] as &$iv)
{
$iv['images'] = self::FileSave($data_id, $iv['images'], 'images', $dir);
}
}
break;
// 视频
case 'video' :
$vss['config']['content_video'] = self::FileSave($data_id, $vss['config']['content_video'], 'video', $dir);
break;
}
}
}
}
}
return $config;
}
/**
* 文件保存
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-18
* @desc description
* @param [int] $data_id [数据id]
* @param [string] $file [文件地址]
* @param [string] $type [类型]
* @param [string] $dir [存储路径]
*/
public static function FileSave($data_id, $file, $type, $dir)
{
if(!empty($file))
{
$arr = explode('/', $file);
$path = 'static'.DS.'upload'.DS.$type.DS.'design'.DS.$data_id.DS.date('Y/m/d');
$filename = $path.DS.$arr[count($arr)-1];
\base\FileUtil::CreateDir($dir.DS.$path);
$status = false;
if(substr($file, 0, 4) == 'http')
{
$temp = ResourcesService::AttachmentPathHandle($file);
if(substr($temp, 0, 4) == 'http' || !file_exists(ROOT.'public'.$temp))
{
// 远程下载
$temp_data = RequestGet($file);
if(!empty($temp_data))
{
file_put_contents($dir.DS.$filename, $temp_data);
$status = true;
}
} else {
$file = $temp;
}
}
if(!$status)
{
\base\FileUtil::CopyFile(ROOT.'public'.$file, $dir.DS.$filename);
}
return DS.$filename;
}
return '';
}
/**
* 导入
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-19
* @desc description
* @param [array] $params [输入参数]
*/
public static function DesignUpload($params = [])
{
// 文件上传校验
$error = FileUploadError('file');
if($error !== true)
{
return DataReturn($error, -1);
}
// 文件格式化校验
$type = ResourcesService::ZipExtTypeList();
if(!in_array($_FILES['file']['type'], $type))
{
return DataReturn('文件格式有误请上传zip压缩包', -2);
}
// 上传处理
return self::DesignUploadHandle($_FILES['file']['tmp_name'], $params);
}
/**
* 导入处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-19
* @desc description
* @param [string] $package_file [软件包地址]
* @param [array] $params [输入参数]
*/
public static function DesignUploadHandle($package_file, $params = [])
{
// 应用upload目录权限校验
$app_upload_dir = ROOT.'public'.DS.'static'.DS.'upload';
if(!is_writable($app_upload_dir))
{
return DataReturn('应用upload目录没有操作权限'.'['.$app_upload_dir.']', -3);
}
// 开始解压文件
$zip = new \ZipArchive();
$resource = $zip->open($package_file);
if($resource != true)
{
return DataReturn('压缩包打开失败['.$resource.']', -11);
}
// 文件处理
$config = [];
$data_id = 0;
$success = 0;
for($i=0; $i<$zip->numFiles; $i++)
{
// 资源文件
$file = $zip->getNameIndex($i);
// 排除临时文件和临时目录
if(strpos($file, '/.') === false && strpos($file, '__') === false)
{
// 去除第一个目录为原始数据的id
$temp_file = substr($file, strpos($file, '/')+1);
if(empty($temp_file) || in_array($temp_file, ['static/', 'static/upload/']))
{
continue;
}
// 是否配置文件
if($temp_file == 'config.json')
{
$stream = $zip->getStream($file);
if($stream === false)
{
$zip->close();
return DataReturn('配置信息读取失败', -1);
}
// 获取配置信息并解析
$file_content = stream_get_contents($stream);
$config = empty($file_content) ? [] : json_decode($file_content, true);
if(empty($config) || empty($config['data_id']) || empty($config['name']))
{
$zip->close();
return DataReturn('配置信息为空或有误', -1);
}
// 数据添加
$data = [
'name' => $config['name'],
'is_header' => (isset($config['is_header']) && $config['is_header'] == 1) ? 1 : 0,
'is_footer' => (isset($config['is_footer']) && $config['is_footer'] == 1) ? 1 : 0,
'add_time' => time(),
];
$data_id = Db::name('Design')->insertGetId($data);
if($data_id <= 0)
{
$zip->close();
return DataReturn('数据添加失败', -1);
}
// 更新配置信息和logo
if(!empty($config['config']) || !empty($config['logo']))
{
$upd_data = [
'logo' => empty($config['logo']) ? '' : str_replace($config['data_id'], $data_id, $config['logo']),
'config' => empty($config['config']) ? '' : str_replace($config['data_id'], $data_id, json_encode($config['config'], JSON_UNESCAPED_UNICODE)),
'upd_time' => time(),
];
if(!Db::name('Design')->where(['id'=>$data_id])->update($upd_data))
{
$zip->close();
return DataReturn('数据更新失败', -1);
}
}
continue;
}
// 配置信息和新的数据id必须存在
if(!empty($config) && !empty($data_id))
{
// 截取文件路径
$new_file = ROOT.'public'.DS.str_replace($config['data_id'], $data_id, $temp_file);
$file_path = substr($new_file, 0, strrpos($new_file, '/'));
// 路径不存在则创建
\base\FileUtil::CreateDir($file_path);
// 如果不是目录则写入文件
if(!is_dir($new_file))
{
// 读取这个文件
$stream = $zip->getStream($file);
if($stream !== false)
{
$file_content = stream_get_contents($stream);
if($file_content !== false)
{
if(file_put_contents($new_file, $file_content))
{
$success++;
}
}
fclose($stream);
}
}
}
}
}
// 关闭zip
$zip->close();
// 附件同步到数据库
ResourcesService::AttachmentDiskFilesToDb('design', self::AttachmentPathTypeValue($data_id));
return DataReturn('导入成功');
}
}
?>

View File

@ -15,6 +15,7 @@ use app\service\PluginsAdminService;
use app\service\PaymentService;
use app\service\ThemeService;
use app\service\AppMiniService;
use app\service\DesignService;
/**
* 软件安装服务层
@ -74,6 +75,11 @@ class PackageInstallService
$url = MyUrl('admin/app/index');
break;
// 页面设计
case 'design' :
$url = MyUrl('admin/design/index');
break;
default :
$url = '';
}
@ -172,6 +178,11 @@ class PackageInstallService
$ret = AppMiniService::ThemeUploadHandle($res['url'], $params);
break;
// 页面设计
case 'design' :
$ret = DesignService::DesignUploadHandle($res['url'], $params);
break;
// 默认
default :
$ret = DataReturn('插件操作类型未定义['.$params['type'].']', -1);

View File

@ -464,10 +464,17 @@ class ResourcesService
* @version 1.0.0
* @date 2019-08-02
* @desc description
* @param [string] $path_type [附件路径类型]
* @param [string] $dir_path [附件路径类型]
* @param [string] $path_type [附件路径值类型]
*/
public static function AttachmentDiskFilesToDb($path_type)
public static function AttachmentDiskFilesToDb($dir_path, $path_type = '')
{
// 未指定类型值则使用路径值
if(empty($path_type))
{
$path_type = $dir_path;
}
// 处理状态总数
$count = 0;
$success = 0;
@ -475,9 +482,9 @@ class ResourcesService
// 视频/文件/图片
$path_all = [
'video' => __MY_ROOT_PUBLIC__.'static/upload/video/'.$path_type.'/',
'file' => __MY_ROOT_PUBLIC__.'static/upload/file/'.$path_type.'/',
'image' => __MY_ROOT_PUBLIC__.'static/upload/images/'.$path_type.'/',
'video' => __MY_ROOT_PUBLIC__.'static/upload/video/'.$dir_path.'/',
'file' => __MY_ROOT_PUBLIC__.'static/upload/file/'.$dir_path.'/',
'image' => __MY_ROOT_PUBLIC__.'static/upload/images/'.$dir_path.'/',
];
foreach($path_all as $type=>$path)
{

View File

@ -51,6 +51,20 @@ class StoreService
{
return MyConfig('shopxo.store_payment_url').self::RequestParamsString($params);
}
/**
* 应用商店页面设计地址
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-02-12
* @desc description
* @param [array] $params [输入参数]
*/
public static function StoreDesignUrl($params = [])
{
return MyConfig('shopxo.store_design_url').self::RequestParamsString($params);
}
/**
* 应用商店主题地址

View File

@ -117,6 +117,7 @@ return [
'website_url' => $store_http.'shopxo.net/',
'store_url' => $store_http.'store.shopxo.net/',
'store_payment_url' => $store_http.'store.shopxo.net/',
'store_design_url' => $store_http.'store.shopxo.net/',
'store_theme_url' => $store_http.'store.shopxo.net/',
'store_regster_url' => $store_http.'store.shopxo.net/regster.html',

View File

@ -86,7 +86,7 @@ function FormTableHeightHandle()
* @date 2021-02-22
* @desc description
* @param {[string]} url [url地址]
* @param {[string]} type [操作类型pluginspaymentwebthememinitheme]
* @param {[string]} type [操作类型pluginspaymentwebthememinithemedesign]
* @param {[string]} value [操作标识值]
* @param {[string]} opt [操作类型url 获取下载地址 download 下载插件包 upgrade 安装插件包]
* @param {[string]} key [操作key仅download和install需要]