李帅

1.会员模板页面完成。

2.订单还在构思。
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
2 2
3 namespace App\Admin\Controllers; 3 namespace App\Admin\Controllers;
4 4
5 +use App\Admin\Renderable\MembershipGoodsEasyTable;
5 use App\Admin\Renderable\MembershipGoodsTable; 6 use App\Admin\Renderable\MembershipGoodsTable;
6 -use App\Admin\Repositories\Membership; 7 +use App\Models\Membership;
7 use App\Models\MembershipGood; 8 use App\Models\MembershipGood;
8 use Dcat\Admin\Form; 9 use Dcat\Admin\Form;
9 use Dcat\Admin\Grid; 10 use Dcat\Admin\Grid;
10 use Dcat\Admin\Show; 11 use Dcat\Admin\Show;
11 use Dcat\Admin\Http\Controllers\AdminController; 12 use Dcat\Admin\Http\Controllers\AdminController;
12 use Dcat\Admin\Form\NestedForm; 13 use Dcat\Admin\Form\NestedForm;
14 +use Illuminate\Support\Facades\DB;
15 +use Dcat\Admin\Widgets\Table;
16 +
13 17
14 class MembershipController extends AdminController 18 class MembershipController extends AdminController
15 { 19 {
...@@ -27,28 +31,27 @@ class MembershipController extends AdminController ...@@ -27,28 +31,27 @@ class MembershipController extends AdminController
27 $grid->setActionClass(Grid\Displayers\Actions::class); 31 $grid->setActionClass(Grid\Displayers\Actions::class);
28 32
29 $grid->column('id',__('ID'))->sortable(); 33 $grid->column('id',__('ID'))->sortable();
30 - 34 + $grid->column('title');
31 - $grid->column('name');
32 - $grid->column('price');
33 - $grid->column('line_price');
34 - $grid->column('limited_days');
35 - $grid->column('limit_unit');
36 $grid->column('intro'); 35 $grid->column('intro');
37 - $grid->column('state'); 36 + $grid->column('expand','展开')
38 - $grid->column('sn'); 37 + ->display('会员商品')
39 - 38 + ->expand(function (){
39 + $th = ['id','price','line_price','limit_days','limit_unit'];
40 + $data = MembershipGood::query()->where('membership_id',$this->id)->get($th)->toArray();
41 + return Table::make($th, $data);
42 + });
40 $grid->column('video_url'); 43 $grid->column('video_url');
41 - $grid->column('video_cover'); 44 + $grid->column('video_cover')->image('/storage/');
42 - $grid->column('bg_images'); 45 + $grid->column('bg_images')->gallery('/storage/');
43 - $grid->column('visits'); 46 + $grid->column('terminal')->using([1 => '安卓', 2 => 'iOS'], '未知');
44 - $grid->column('virtual_sales'); 47 + $grid->column('state','线上展示')->switch();
45 - $grid->column('sales');
46 -
47 $grid->column('created_at'); 48 $grid->column('created_at');
48 $grid->column('updated_at')->sortable(); 49 $grid->column('updated_at')->sortable();
49 50
50 $grid->filter(function (Grid\Filter $filter) { 51 $grid->filter(function (Grid\Filter $filter) {
51 - $filter->equal('id'); 52 + $filter->equal('id')->width(2);
53 + $filter->like('terminal')->width(3);
54 + $filter->panel();
52 55
53 }); 56 });
54 }); 57 });
...@@ -94,16 +97,29 @@ class MembershipController extends AdminController ...@@ -94,16 +97,29 @@ class MembershipController extends AdminController
94 */ 97 */
95 protected function form() 98 protected function form()
96 { 99 {
100 + $css = <<<CSS
101 +.table tr td:first-child{
102 + padding-left: 0 ;
103 +}
104 +.table tr td{
105 + padding-left: 0 ;
106 +}
107 +.form-field .input-group .price{
108 + width: 1% !important;
109 +}
110 +CSS;
111 + \Admin::style($css);
112 +
97 return Form::make(new Membership(), function (Form $form) { 113 return Form::make(new Membership(), function (Form $form) {
98 $form->display('id'); 114 $form->display('id');
99 - $form->block(12, function (Form\BlockForm $form) { 115 +
116 + $form->block(8, function (Form\BlockForm $form) {
100 // 设置标题 117 // 设置标题
101 $form->title('基本设置'); 118 $form->title('基本设置');
102 // 显示底部提交按钮 119 // 显示底部提交按钮
103 $form->showFooter(); 120 $form->showFooter();
104 // 设置字段宽度 121 // 设置字段宽度
105 - $form->width(8, 2); 122 + $form->width(9, 2);
106 -
107 123
108 $form->radio('terminal')->addElementClass('terminal') 124 $form->radio('terminal')->addElementClass('terminal')
109 ->options([1 => 'Android', 2 => 'IOS'])->default(2); 125 ->options([1 => 'Android', 2 => 'IOS'])->default(2);
...@@ -112,7 +128,7 @@ class MembershipController extends AdminController ...@@ -112,7 +128,7 @@ class MembershipController extends AdminController
112 $form->textarea('intro')->addElementClass('intro'); 128 $form->textarea('intro')->addElementClass('intro');
113 129
114 $form->radio('bg_type')->addElementClass('bg_type') 130 $form->radio('bg_type')->addElementClass('bg_type')
115 - ->options([1 => '单图', 2 => '轮播图', 3 => '视频'])->default(1) 131 + ->options([1 => '单图', 2 => '轮播图', 3 => '视频'])
116 ->when([1,2],function (Form\BlockForm $form){ 132 ->when([1,2],function (Form\BlockForm $form){
117 $form->multipleImage('bg_images') 133 $form->multipleImage('bg_images')
118 ->limit(5) 134 ->limit(5)
...@@ -129,36 +145,91 @@ class MembershipController extends AdminController ...@@ -129,36 +145,91 @@ class MembershipController extends AdminController
129 $form->image('video_cover') 145 $form->image('video_cover')
130 ->uniqueName() 146 ->uniqueName()
131 ->addElementClass('video_cover'); 147 ->addElementClass('video_cover');
132 - }); 148 + })->default(1);
133 149
134 $form->radio('state')->options(['不显示', '显示'])->default(0); 150 $form->radio('state')->options(['不显示', '显示'])->default(0);
135 151
136 - 152 + $form->radio('is_bind_old')->options(['新增会员商品', '选择已有未上架的商品'])->default(0)
137 - $form->radio('is_bind_old')->options(['新增会员商品', '选择已有商品'])->default(0)
138 ->when(0,function (Form\BlockForm $form){ 153 ->when(0,function (Form\BlockForm $form){
139 - $form->table('membership_goods','新赠会员商品', function (NestedForm $table) { 154 + $form->table('new','新增', function (NestedForm $table) {
140 - $table->currency('price')->symbol('¥')->addElementClass('price'); 155 + $table->text('price')->addElementClass('price');
141 - $table->currency('line_price')->symbol('¥')->addElementClass('line_price'); 156 + $table->text('line_price')->addElementClass('line_price');
142 $table->text('limited_days')->addElementClass('limited_days'); 157 $table->text('limited_days')->addElementClass('limited_days');
143 $table->text('limit_unit')->addElementClass('limit_unit'); 158 $table->text('limit_unit')->addElementClass('limit_unit');
144 -
145 }); 159 });
146 }) 160 })
147 ->when(1,function (Form\BlockForm $form){ 161 ->when(1,function (Form\BlockForm $form){
148 - $form->selectTable('membership_goods') 162 + $form->multipleSelectTable('old','已有')
149 ->title('会员商品') 163 ->title('会员商品')
150 ->from(MembershipGoodsTable::make()) 164 ->from(MembershipGoodsTable::make())
151 ->model(MembershipGood::class, 'id', 'price'); 165 ->model(MembershipGood::class, 'id', 'price');
152 }); 166 });
153 }); 167 });
154 168
155 -// $form->block(4, function (Form\BlockForm $form) { 169 + $form->block(4, function (Form\BlockForm $form) {
156 -// $form->width(9, 1); 170 + $form->width(9, 1);
157 -// $form->html(view('admin.form.phone')); 171 + $form->html(view('admin.form.membership'));
158 -// }); 172 + });
159 173
160 $form->display('created_at'); 174 $form->display('created_at');
161 $form->display('updated_at'); 175 $form->display('updated_at');
162 }); 176 });
163 } 177 }
178 +
179 + public function store()
180 + {
181 + $all = request()->all();
182 +
183 + //
184 + if (isset($all['upload_column'])) return $this->form()->store();
185 +
186 + // 检测是否已经有该平台的数据
187 + $membership = Membership::query()->where('terminal',$all['terminal'])->first();
188 + if ($membership && $all['state'] == '1') return $this->form()->response()->error('状态为显示会覆盖线上的设置,请修改后重试');
189 +
190 + try{
191 + DB::transaction(function ()use ($all){
192 + $membership = Membership::query()->create([
193 + 'title' => $all['title'],
194 + 'intro' => $all['intro'],
195 + 'bg_type' => $all['bg_type'],
196 + 'bg_images' => $all['bg_images'],
197 + 'video_url' => $all['video_url'] ?? '',
198 + 'video_cover' => $all['video_cover'] ?? '',
199 + 'terminal' => $all['terminal'],
200 + 'state' => $all['state'],
201 + ]);
202 +
203 + if ($all['is_bind_old'] === '0'){
204 + // 新增
205 + foreach ($all['new'] as $item) {
206 + if ($item['_remove_'] === '1') continue;
207 +
208 + MembershipGood::query()->create([
209 + 'membership_id' => $membership->id,
210 + 'price' => $item['price'],
211 + 'line_price' => $item['line_price'],
212 + 'limited_days' => $item['limited_days'],
213 + 'limit_unit' => $item['limit_unit'] ?? '天',
214 + 'terminal' => $all['terminal'],
215 + 'state' => 1,
216 + ]);
217 + }
218 + }elseif ($all['is_bind_old'] === '1'){
219 + $old_ids = explode(',',$all['old']);
220 + $membership_goods = MembershipGood::query()->whereIn('id',$old_ids)->get();
221 + foreach ($membership_goods as $membership_good){
222 + $membership_good->membership_id = $membership->id;
223 + $membership_good->state = 1;
224 + $membership_good->save();
225 + }
226 + }
227 + });
228 +
229 + }catch (\Exception $exception){
230 + return $this->form()->response()->error($exception->getMessage());
231 + }
232 +
233 + return $this->form()->response()->refresh()->success(trans('admin.save_succeeded'));
234 + }
164 } 235 }
......
1 +<?php
2 +/**
3 + * Created by PhpStorm.
4 + * User: lishuai
5 + * Date: 2022/2/11
6 + * Time: 5:30 PM
7 + */
8 +namespace App\Admin\Extensions;
9 +
10 +use Dcat\Admin\Admin;
11 +use Dcat\Admin\Grid\Displayers\AbstractDisplayer;
12 +use Dcat\Admin\Support\Helper;
13 +use Illuminate\Contracts\Support\Arrayable;
14 +use Illuminate\Support\Facades\Storage;
15 +
16 +class Gallery extends AbstractDisplayer
17 +{
18 + public function display($server = '', $width = 100, $height = 200)
19 + {
20 +
21 + Admin::js('@js/viewer.js');
22 +
23 + Admin::css([
24 + '@css/gallery.css',
25 + '@css/viewer.css'
26 + ]);
27 +
28 + $defaultArgs = [
29 + 'server' => $server,
30 + 'width' => $width,
31 + 'height' => $height,
32 + ];
33 +
34 + ['server' => $server, 'width' => $width, 'height' => $height] = $defaultArgs;
35 +
36 + if ($this->value instanceof Arrayable) {
37 + $this->value = $this->value->toArray();
38 + }
39 +
40 + $groupId = $this->grid->getTableId().'_'.$this->getKey().'_'.$this->column->getName().'_gallery_group';
41 +
42 + $src = []; // 避免 $src 未定义
43 +
44 + foreach (Helper::array($this->value) as $k => $v) {
45 + if (url()->isValidUrl($v) || mb_strpos($v, 'data:image') === 0) {
46 + $src[] = $v;
47 + } elseif ($server) {
48 + $src[] = rtrim($server, '/').'/'.ltrim($v, '/');
49 + } else {
50 + $src[] = Storage::disk(config('admin.upload.disk'))->url($v);
51 + }
52 +
53 + }
54 + return Admin::view('admin.grid.gallery', ['src' => $src, 'width' => $width, 'height' => $height, 'id' => $groupId]);
55 + }
56 +}
1 +<?php
2 +/**
3 + * Created by PhpStorm.
4 + * User: lishuai
5 + * Date: 2022/2/11
6 + * Time: 5:30 PM
7 + */
8 +namespace App\Admin\Extensions;
9 +
10 +use Dcat\Admin\Admin;
11 +use Dcat\Admin\Grid\Displayers\AbstractDisplayer;
12 +
13 +use Illuminate\Contracts\Support\Arrayable;
14 +use Illuminate\Support\Facades\Storage;
15 +
16 +class Swiper extends AbstractDisplayer
17 +{
18 + public function display($server = '', $width = 200, $height = 200)
19 + {
20 + // todo
21 +// if ($this->value instanceof Arrayable) {
22 +// $this->value = $this->value->toArray();
23 +// }
24 +//
25 +// return collect((array) $this->value)->filter()->map(function ($path) use ($server, $width, $height) {
26 +// if (url()->isValidUrl($path) || mb_strpos($path, 'data:image') === 0) {
27 +// $src = $path;
28 +// } elseif ($server) {
29 +// $src = rtrim($server, '/').'/'.ltrim($path, '/');
30 +// } else {
31 +// $src = Storage::disk(config('admin.upload.disk'))->url($path);
32 +// }
33 +//
34 +// return "<img data-action='preview-img' src='$src' style='max-width:{$width}px;max-height:{$height}px;cursor:pointer' class='img img-thumbnail' />";
35 +// })->implode('&nbsp;');
36 + }
37 +}
...@@ -17,6 +17,8 @@ class MembershipGoodsTable extends LazyRenderable ...@@ -17,6 +17,8 @@ class MembershipGoodsTable extends LazyRenderable
17 public function grid(): Grid 17 public function grid(): Grid
18 { 18 {
19 return Grid::make(new MembershipGood(), function (Grid $grid) { 19 return Grid::make(new MembershipGood(), function (Grid $grid) {
20 + $grid->model()->where('state',0);
21 +
20 $grid->column('id', 'ID')->sortable(); 22 $grid->column('id', 'ID')->sortable();
21 $grid->column('membership_id'); 23 $grid->column('membership_id');
22 $grid->column('price'); 24 $grid->column('price');
......
1 +<?php
2 +
3 +namespace App\Admin\Repositories;
4 +
5 +use App\Models\MembershipGood as Model;
6 +use Dcat\Admin\Repositories\EloquentRepository;
7 +
8 +class MembershipGood extends EloquentRepository
9 +{
10 + /**
11 + * Model.
12 + *
13 + * @var string
14 + */
15 + protected $eloquentClass = Model::class;
16 +}
1 +<?php
2 +
3 +namespace App\Admin\Repositories;
4 +
5 +use App\Models\OrderGood as Model;
6 +use Dcat\Admin\Repositories\EloquentRepository;
7 +
8 +class OrderGood extends EloquentRepository
9 +{
10 + /**
11 + * Model.
12 + *
13 + * @var string
14 + */
15 + protected $eloquentClass = Model::class;
16 +}
1 +<?php
2 +
3 +namespace App\Admin\Repositories;
4 +
5 +use App\Models\VideoTemp as Model;
6 +use Dcat\Admin\Repositories\EloquentRepository;
7 +
8 +class VideoTemp extends EloquentRepository
9 +{
10 + /**
11 + * Model.
12 + *
13 + * @var string
14 + */
15 + protected $eloquentClass = Model::class;
16 +}
...@@ -5,6 +5,8 @@ use Dcat\Admin\Grid; ...@@ -5,6 +5,8 @@ use Dcat\Admin\Grid;
5 use Dcat\Admin\Form; 5 use Dcat\Admin\Form;
6 use Dcat\Admin\Grid\Filter; 6 use Dcat\Admin\Grid\Filter;
7 use Dcat\Admin\Show; 7 use Dcat\Admin\Show;
8 +use Dcat\Admin\Grid\Column;
9 +use App\Admin\Extensions\Gallery;
8 10
9 /** 11 /**
10 * Dcat-admin - admin builder based on Laravel. 12 * Dcat-admin - admin builder based on Laravel.
...@@ -24,3 +26,7 @@ use Dcat\Admin\Show; ...@@ -24,3 +26,7 @@ use Dcat\Admin\Show;
24 * Admin::js('/packages/prettydocs/js/main.js'); 26 * Admin::js('/packages/prettydocs/js/main.js');
25 * 27 *
26 */ 28 */
29 +Admin::asset()->alias('@js', '/asset/js');
30 +Admin::asset()->alias('@css', '/asset/css');
31 +
32 +Column::extend('gallery', Gallery::class); //grid列扩展 - 多图浏览
...\ No newline at end of file ...\ No newline at end of file
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
...@@ -10,4 +10,21 @@ use Illuminate\Routing\Controller as BaseController; ...@@ -10,4 +10,21 @@ use Illuminate\Routing\Controller as BaseController;
10 class Controller extends BaseController 10 class Controller extends BaseController
11 { 11 {
12 use AuthorizesRequests, DispatchesJobs, ValidatesRequests; 12 use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
13 +
14 + public function isAppleClient()
15 + {
16 + $ua = request()->header('user-agent');
17 +
18 + if (strpos($ua, 'iPhone') || strpos($ua, 'iPad') || strpos($ua,'Mac OS X')) {
19 + return true;
20 + }
21 + return false;
22 + }
23 +
24 + public function getClientTerminal()
25 + {
26 + $ua = request()->header('user-agent');
27 +
28 + return 'ios';
29 + }
13 } 30 }
......
1 +<?php
2 +
3 +namespace App\Http\Controllers\V1;
4 +
5 +use App\Http\Controllers\Controller;
6 +use App\Models\Membership;
7 +use Illuminate\Http\Request;
8 +use Jiannei\Response\Laravel\Support\Facades\Response;
9 +
10 +class MembershipController extends Controller
11 +{
12 + /**
13 + * Display a listing of the resource.
14 + * @param \Illuminate\Http\Request $request
15 + * @return \Illuminate\Http\JsonResponse
16 + */
17 + public function index(Request $request)
18 + {
19 + $membership = Membership::query()->where('state',1);
20 + // 获取会员介绍页内容
21 + if ($this->isAppleClient()){
22 + $membership = $membership->where('terminal',2);
23 + }else{
24 + $membership = $membership->where('terminal',1);
25 + }
26 +
27 + $membership = $membership->first();
28 +
29 + $membership->bg_images = $membership->getImage();
30 + $membership->goods_list = $membership->getMembershipGoods()->get();
31 +
32 + return Response::success($membership);
33 + }
34 +
35 + /**
36 + * Show the form for creating a new resource.
37 + *
38 + * @return \Illuminate\Http\Response
39 + */
40 + public function create()
41 + {
42 + //
43 + }
44 +
45 + /**
46 + * Store a newly created resource in storage.
47 + *
48 + * @param \Illuminate\Http\Request $request
49 + * @return \Illuminate\Http\Response
50 + */
51 + public function store(Request $request)
52 + {
53 + //
54 + }
55 +
56 + /**
57 + * Display the specified resource.
58 + *
59 + * @param int $id
60 + * @return \Illuminate\Http\Response
61 + */
62 + public function show($id)
63 + {
64 + //
65 + }
66 +
67 + /**
68 + * Show the form for editing the specified resource.
69 + *
70 + * @param int $id
71 + * @return \Illuminate\Http\Response
72 + */
73 + public function edit($id)
74 + {
75 + //
76 + }
77 +
78 + /**
79 + * Update the specified resource in storage.
80 + *
81 + * @param \Illuminate\Http\Request $request
82 + * @param int $id
83 + * @return \Illuminate\Http\Response
84 + */
85 + public function update(Request $request, $id)
86 + {
87 + //
88 + }
89 +
90 + /**
91 + * Remove the specified resource from storage.
92 + *
93 + * @param int $id
94 + * @return \Illuminate\Http\Response
95 + */
96 + public function destroy($id)
97 + {
98 + //
99 + }
100 +}
...@@ -3,7 +3,13 @@ ...@@ -3,7 +3,13 @@
3 namespace App\Http\Controllers\V1; 3 namespace App\Http\Controllers\V1;
4 4
5 use App\Http\Controllers\Controller; 5 use App\Http\Controllers\Controller;
6 +use App\Models\MembershipGood;
7 +use App\Models\Order;
8 +use App\Models\OrderGood;
9 +use Carbon\Carbon;
6 use Illuminate\Http\Request; 10 use Illuminate\Http\Request;
11 +use Illuminate\Support\Facades\Auth;
12 +use Illuminate\Support\Facades\DB;
7 use Illuminate\Support\Facades\Validator; 13 use Illuminate\Support\Facades\Validator;
8 use Jiannei\Response\Laravel\Support\Facades\Response; 14 use Jiannei\Response\Laravel\Support\Facades\Response;
9 15
...@@ -11,27 +17,104 @@ class OrderController extends Controller ...@@ -11,27 +17,104 @@ class OrderController extends Controller
11 { 17 {
12 public function index(Request $request) 18 public function index(Request $request)
13 { 19 {
14 - $validator = Validator::make($request->all(),[ 20 +
21 + }
22 +
23 + public function store(Request $request)
24 + {
25 + $data = $request->all();
26 +
27 + $validator = Validator::make($data,[
15 'goods_id' => 'required|integer', 28 'goods_id' => 'required|integer',
16 - 'source' => 'required|string',
17 ]); 29 ]);
18 30
19 - if ($validator->fails()){ 31 + if ($validator->fails()) return Response::fail('缺少参数',500,$validator->errors());
20 - return Response::fail('缺少参数',500,$validator->errors()); 32 +
21 - } 33 + $user_id = Auth::user()->getAuthIdentifier();
22 34
35 + $order = $this->build($user_id, $data['goods_id'], $this->getClientTerminal());
23 36
37 + return Response::success($order);
24 } 38 }
25 39
26 - public function store(Request $request) 40 + public function show($id)
27 { 41 {
28 - $validator = Validator::make($request->all(),[
29 - 'goods_id' => 'required|integer',
30 - 'source' => 'required|string',
31 - ]);
32 42
33 - if ($validator->fails()){ 43 + }
34 - return Response::fail('',500,$validator->errors()); 44 +
45 + /**
46 + * 创建订单
47 + * @param $user_id
48 + * @param $member_id
49 + * @param $source
50 + * @param $number = 1
51 + * @return string $order_sn
52 + */
53 + public function build($user_id, $member_id, $source, $number = 1)
54 + {
55 + try{
56 + return DB::transaction(function ()use ($user_id, $member_id, $source, $number){
57 + // 获取商品信息
58 + $membership_good = MembershipGood::query()->where('id',$member_id)->first();
59 + $membership = $membership_good->membership()->first();
60 +
61 + // 实付金额 = 商品金额
62 + $pay_amount = $membership_good->price * $number;
63 +
64 + // 创建订单
65 + $order = new Order();
66 + $order_sn = $order::get_sn('osn');
67 + $order->order_sn = $order_sn;
68 + $order->user_id = $user_id;
69 + $order->pay_amount = $pay_amount;
70 + $order->description = '一言会员' . $membership_good->limit_days . $membership_good->limit_unit;
71 + $order->goods_amount = $membership_good->price;
72 + $order->status = Order::UNPAID;
73 + $order->source = $source;
74 +
75 + $order->save();
76 +
77 + $order_good = new OrderGood();
78 + $order_good->order_sn = $order_sn;
79 + $order_good->goods_id = $member_id;
80 + $order_good->goods_type = OrderGood::MemberShip;
81 + $order_good->goods_name = $membership_good->limit_days . $membership_good->limit_unit;
82 + $order_good->goods_image = $membership->getSingleImage();
83 + $order_good->goods_price = $membership_good->price;
84 + $order_good->goods_number = $number;
85 +
86 + $order_good->save();
87 +
88 + $order->goods = $order_good;
89 +
90 +
91 + // todo 超时处理,建议给Job处理
92 +
93 +// if ($pay_amount == 0) { //0元购就不执行回调了
94 +// $this->freePay($order);
95 +// }
96 + return $order;
97 + });
98 + }catch (\Exception $exception){
99 + return Response::fail('', 500, $exception->getMessage());
35 } 100 }
36 } 101 }
102 +
103 + public function freePay($order)
104 + {
105 + $order = Order::query()->find($order->id);
106 +
107 + if ($order->status < Order::PAID){
108 + $order->status = Order::PAID;
109 + $order->pay_type = '';
110 + $order->pay_time = Carbon::now();
111 + if ($order->save()){
112 + // 执行一些本来属于回调的逻辑
113 +
114 + return true;
115 + }
116 + }
117 + return false;
118 + }
119 +
37 } 120 }
......
1 +<?php
2 +
3 +namespace App\Http\Controllers\V1;
4 +
5 +use App\Http\Controllers\Controller;
6 +use App\Models\Order;
7 +use App\Payment\PaymentFactory;
8 +use Illuminate\Http\Request;
9 +use Jiannei\Response\Laravel\Support\Facades\Response;
10 +
11 +class PayController extends Controller
12 +{
13 +
14 + public function index(Request $request,PaymentFactory $factory)
15 + {
16 + $order_sn = $request->get('order_sn');
17 +
18 + $pay_type = $request->get('pay_type');
19 +
20 +
21 + $order = Order::query()->where('order_sn', $order_sn)->first();
22 +
23 + if ($order->status !== Order::UNPAID) return false;
24 +
25 +// if ($order->pay_amount <= 0) return $this->paid($order_sn); 0元购应该单独写一套
26 +
27 + $payment = $factory->init($pay_type)->prepare($order);
28 +
29 + return Response::success($payment);
30 + }
31 +
32 + /**
33 + * Store a newly created resource in storage.
34 + *
35 + * @param \Illuminate\Http\Request $request
36 + * @return \Illuminate\Http\Response
37 + */
38 + public function store(Request $request)
39 + {
40 + //
41 + }
42 +
43 + /**
44 + * Display the specified resource.
45 + *
46 + * @param int $id
47 + * @return \Illuminate\Http\Response
48 + */
49 + public function show($id)
50 + {
51 + //
52 + }
53 +
54 + /**
55 + * Show the form for editing the specified resource.
56 + *
57 + * @param int $id
58 + * @return \Illuminate\Http\Response
59 + */
60 + public function edit($id)
61 + {
62 + //
63 + }
64 +
65 + /**
66 + * Update the specified resource in storage.
67 + *
68 + * @param \Illuminate\Http\Request $request
69 + * @param int $id
70 + * @return \Illuminate\Http\Response
71 + */
72 + public function update(Request $request, $id)
73 + {
74 + //
75 + }
76 +
77 + /**
78 + * Remove the specified resource from storage.
79 + *
80 + * @param int $id
81 + * @return \Illuminate\Http\Response
82 + */
83 + public function destroy($id)
84 + {
85 + //
86 + }
87 +}
...@@ -5,20 +5,33 @@ namespace App\Models; ...@@ -5,20 +5,33 @@ namespace App\Models;
5 use Dcat\Admin\Traits\HasDateTimeFormatter; 5 use Dcat\Admin\Traits\HasDateTimeFormatter;
6 6
7 use Illuminate\Database\Eloquent\Model; 7 use Illuminate\Database\Eloquent\Model;
8 +use Illuminate\Support\Arr;
9 +use Illuminate\Support\Facades\Storage;
8 10
9 class Membership extends Model 11 class Membership extends Model
10 { 12 {
11 use HasDateTimeFormatter; 13 use HasDateTimeFormatter;
12 protected $table = 'membership'; 14 protected $table = 'membership';
13 15
14 - public function getBgImagesAttribute($value) 16 + protected $guarded = [''];
17 +
18 +
19 + public function getImage()
15 { 20 {
16 - return explode(',', $value); 21 + return collect(explode(',', $this->bg_images))->map(function ($item){
22 + return Storage::disk('public')->url($item);
23 + });
17 } 24 }
18 25
19 - public function getSingleImgAttribute() 26 + public function getSingleImage()
20 { 27 {
21 $array = explode(',', $this->bg_images); 28 $array = explode(',', $this->bg_images);
22 - return $array[0]; 29 +
30 + return Storage::disk('public')->url($array[0]);
31 + }
32 +
33 + public function getMembershipGoods()
34 + {
35 + return $this->hasMany('App\Models\MembershipGood','membership_id');
23 } 36 }
24 } 37 }
......
...@@ -5,10 +5,25 @@ namespace App\Models; ...@@ -5,10 +5,25 @@ namespace App\Models;
5 use Dcat\Admin\Traits\HasDateTimeFormatter; 5 use Dcat\Admin\Traits\HasDateTimeFormatter;
6 6
7 use Illuminate\Database\Eloquent\Model; 7 use Illuminate\Database\Eloquent\Model;
8 +use Spatie\EloquentSortable\Sortable;
9 +use Spatie\EloquentSortable\SortableTrait;
8 10
9 -class MembershipGood extends Model 11 +class MembershipGood extends Model implements Sortable
10 { 12 {
11 use HasDateTimeFormatter; 13 use HasDateTimeFormatter;
12 protected $table = 'membership_goods'; 14 protected $table = 'membership_goods';
13 - 15 +
16 + protected $guarded = [''];
17 +
18 + use SortableTrait;
19 +
20 + public $sortable = [
21 + 'order_column_name' => 'sn',
22 + 'sort_when_creating' => true,
23 + ];
24 +
25 + public function membership()
26 + {
27 + return $this->belongsTo(Membership::class);
28 + }
14 } 29 }
......
...@@ -3,57 +3,36 @@ ...@@ -3,57 +3,36 @@
3 namespace App\Models; 3 namespace App\Models;
4 4
5 use Dcat\Admin\Traits\HasDateTimeFormatter; 5 use Dcat\Admin\Traits\HasDateTimeFormatter;
6 -use Illuminate\Database\Eloquent\SoftDeletes;
7 use Illuminate\Database\Eloquent\Model; 6 use Illuminate\Database\Eloquent\Model;
8 -use Illuminate\Support\Facades\Auth;
9 7
10 class Order extends Model 8 class Order extends Model
11 { 9 {
12 use HasDateTimeFormatter; 10 use HasDateTimeFormatter;
13 - use SoftDeletes;
14 11
15 protected $table = 'order'; 12 protected $table = 'order';
16 13
17 - public function order_goods() 14 + /** 未支付*/
18 - { 15 + const UNPAID = 100;
19 - return $this->hasOne('App\Models\OrderGood','order_sn');
20 - }
21 16
22 - /** 17 + /** 用户取消*/
23 - * 预创建订单 18 + const USER_CANCEL = 101;
24 - * @param $member_id
25 - * @param $source
26 - */
27 - public function build($member_id,$source)
28 - {
29 - // 获取商品信息
30 - $membership = Membership::query()->where('id',$member_id)->first();
31 19
32 - // 实付金额 = 商品金额 20 + /** 超时取消*/
33 - $pay_amount = $membership->price; 21 + const TIMEOUT_CANCEL = 102;
34 22
35 - // 创建订单 23 + /** 商户取消*/
36 - $order = new Order(); 24 + const MERCHANT_CANCEL = 102;
37 - $order_sn = $this->get_sn('osn');
38 - $order->order_sn = $order_sn;
39 - $order->user_id = Auth::user()->getAuthIdentifier();
40 - $order->pay_amount = $pay_amount;
41 - $order->goods_amount = $membership->price;
42 - $order->status = 100;
43 - $order->source = $source;
44 25
45 - $order->save(); 26 + /** 已支付*/ // 回调
27 + const PAID = 201;
46 28
47 - $order_good = new OrderGood(); 29 + /** 已完成*/ // 回调并且业务逻辑(加天数、加销量)执行完毕
48 - $order_good->order_sn = $order_sn; 30 + const DONE = 204;
49 - $order_good->goods_id = $member_id;
50 - $order_good->goods_name = $membership->name;
51 - $order_good->goods_image = $membership->getSingleImg();
52 - $order_good->goods_price = $membership->price;
53 - $order_good->goods_number = 1;
54 31
55 - $order_good->save();
56 32
33 + public function order_goods()
34 + {
35 + return $this->hasOne('App\Models\OrderGood','order_sn');
57 } 36 }
58 37
59 /** 38 /**
...@@ -61,7 +40,7 @@ class Order extends Model ...@@ -61,7 +40,7 @@ class Order extends Model
61 * @param string $prefix 40 * @param string $prefix
62 * @return string 41 * @return string
63 */ 42 */
64 - public function get_sn($prefix = '') 43 + static public function get_sn($prefix = '')
65 { 44 {
66 $Sn = $prefix . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99)); 45 $Sn = $prefix . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
67 return $Sn; 46 return $Sn;
......
...@@ -10,5 +10,6 @@ class OrderGood extends Model ...@@ -10,5 +10,6 @@ class OrderGood extends Model
10 { 10 {
11 use HasDateTimeFormatter; 11 use HasDateTimeFormatter;
12 protected $table = 'order_goods'; 12 protected $table = 'order_goods';
13 - 13 +
14 + const MemberShip = 1001;
14 } 15 }
......
1 +<?php
2 +/**
3 + * Created by PhpStorm.
4 + * User: lishuai
5 + * Date: 2022/2/15
6 + * Time: 4:23 PM
7 + */
8 +
9 +namespace App\Payment;
10 +
11 +class AliPayment implements PaymentInterface
12 +{
13 + public function prepare()
14 + {
15 + // TODO: Implement prepare() method.
16 + }
17 +}
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +/**
3 + * Created by PhpStorm.
4 + * User: lishuai
5 + * Date: 2022/2/15
6 + * Time: 4:25 PM
7 + */
8 +
9 +namespace App\Payment;
10 +
11 +class PaymentFactory
12 +{
13 + public function init($pay_type)
14 + {
15 + switch ($pay_type){
16 + case 'alipay':
17 + return new AliPayment();
18 + case 'wechat':
19 + return new WechatPayment();
20 + default:
21 + throw new \Exception('未知的支付方式');
22 + }
23 + }
24 +}
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +/**
3 + * Created by PhpStorm.
4 + * User: lishuai
5 + * Date: 2022/2/15
6 + * Time: 4:00 PM
7 + */
8 +
9 +namespace App\Payment;
10 +
11 +use App\Models\Order;
12 +
13 +interface PaymentInterface
14 +{
15 + /**
16 + * @param $order
17 + * @return array
18 + */
19 + public function prepare(Order $order);
20 +
21 +}
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +/**
3 + * Created by PhpStorm.
4 + * User: lishuai
5 + * Date: 2022/2/15
6 + * Time: 4:23 PM
7 + */
8 +
9 +namespace App\Payment;
10 +
11 +use App\Models\Order;
12 +use GuzzleHttp\Client;
13 +use GuzzleHttp\Exception\GuzzleException;
14 +
15 +class WechatPayment implements PaymentInterface
16 +{
17 + /** 支付接口基础地址 */
18 + const MCH_BASE_URL = 'https://api.mch.weixin.qq.com';
19 +
20 + /** APP支付*/
21 + const APP_URL = '/v3/pay/transactions/app';
22 +
23 + /** H5支付*/
24 + const H5_URL = '/v3/pay/transactions/h5';
25 +
26 + /** App的应用id */
27 + public $appid;
28 +
29 + /** 商户身份ID */
30 + public $mchid;
31 +
32 + /** 商品描述*/
33 + public $description;
34 +
35 + /** 商户订单号*/
36 + public $out_trade_no;
37 +
38 + /** 支付回调*/
39 + public $notify_url;
40 +
41 + /** 订单金额*/
42 + public $amount;
43 +
44 + /** 证书序列号*/
45 + public $serial_no;
46 +
47 + /** 商户API私钥*/
48 + // todo 待添加 file:///path/to/merchant/apiclient_key.pem'
49 + public $mch_private_key = '';
50 +
51 +
52 + public function prepare(Order $order)
53 + {
54 + $body = [
55 + 'appid' => env('WECHAT_APPID'),
56 + 'mchid' => env('WECHAT_PAY_MCH_ID'),
57 + 'description' => $order->description,
58 + 'out_trade_no' => $order->order_sn,
59 + 'notify_url' => env('WECHAT_PAY_NOTIFY'),
60 + 'amount' => [
61 + 'total' => $order->pay_amount * 100,
62 + 'currency' => 'CNY'
63 + ],
64 + ];
65 +
66 + $timestamp = time();//时间戳
67 +
68 + $nonce = self::nonce(32);//随机串
69 +
70 + $sign = $this->sign('POST', self::APP_URL, $timestamp, $nonce, json_encode($body));
71 +
72 + //设置HTTP头
73 + $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
74 + env('WECHAT_PAY_MCH_ID'), $nonce, $timestamp, env('WECHAT_PAY_SERIAL_NO'), $sign);
75 + $headers = [
76 + 'Accept' => 'application/json, text/plain, application/x-gzip, application/pdf, image/png, image/*;q=0.5',
77 + 'User-Agent' => '*/*',
78 + 'Content-Type' => 'application/json; charset=utf-8',
79 + 'Authorization' => $token,
80 + ];
81 +
82 + $client = new Client([
83 + 'base_uri' => self::MCH_BASE_URL,
84 + 'timeout' => 0,
85 + 'allow_redirects' => false,
86 + 'headers' => $headers
87 + ]);
88 +
89 + try {
90 + $response = $client->request('POST', self::APP_URL, ['json' => $body]);
91 + dd($prepayid = $response->getBody()->getContents());
92 + } catch (GuzzleException $exception) {
93 + dd($exception->getMessage());
94 + }
95 +
96 + return [
97 + 'appid' => env('WECHAT_APPID'),
98 + 'partnerid' => env('WECHAT_PAY_MCH_ID'),
99 + 'prepayid' => $prepayid,
100 + 'package' => 'Sign=WXPay',
101 + 'noncestr' => $nonce,
102 + 'timestamp' => $timestamp,
103 + 'sign' => $sign
104 + ];
105 + }
106 +
107 +
108 + public function notify()
109 + {
110 +
111 + }
112 +
113 +
114 +
115 +
116 + /**
117 + * @param string $http_method "GET"/"POST"
118 + * @param string $url "/v3/certificates"
119 + * @param string $timestamp
120 + * @param string $nonce
121 + * @param string $body "json_encode($body)"
122 + * @return array
123 + */
124 + public function sign($http_method, $url, $timestamp, $nonce, $body = '')
125 + {
126 + $mch_private_key = openssl_get_privatekey(file_get_contents($this->mch_private_key));//私钥
127 +
128 + //构造签名串
129 + $message = implode("\n", [$http_method, $url, $timestamp, $nonce, $body]);
130 +
131 + //计算签名值
132 + openssl_sign($message, $signature, $mch_private_key, OPENSSL_ALGO_SHA256);
133 +
134 + return base64_encode($signature);
135 + }
136 +
137 + /**
138 + * 生成32位随机字符串
139 + * @param int $size - Nonce string length, default is 32.
140 + * @return string - base62 random string.
141 + * @throws
142 + */
143 + public static function nonce(int $size = 32): string
144 + {
145 + if ($size < 1) {
146 + throw new \InvalidArgumentException('Size must be a positive integer.');
147 + }
148 +
149 + return implode('', array_map(static function (string $c): string {
150 + return '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[ord($c) % 62];
151 + }, str_split(random_bytes($size))));
152 + }
153 +
154 +}
...@@ -21,7 +21,6 @@ class CreateMembershipTable extends Migration ...@@ -21,7 +21,6 @@ class CreateMembershipTable extends Migration
21 $table->unsignedTinyInteger('bg_type')->default(0)->comment('背景类型,1=单图,2=轮播图,3=视频'); 21 $table->unsignedTinyInteger('bg_type')->default(0)->comment('背景类型,1=单图,2=轮播图,3=视频');
22 22
23 $table->string('bg_images')->default('')->comment('介绍图'); 23 $table->string('bg_images')->default('')->comment('介绍图');
24 - $table->unsignedTinyInteger('is_video')->default(0)->comment('视频开关');
25 $table->string('video_url')->default('')->comment('视频地址'); 24 $table->string('video_url')->default('')->comment('视频地址');
26 $table->string('video_cover')->default('')->comment('视频封面'); 25 $table->string('video_cover')->default('')->comment('视频封面');
27 26
......
...@@ -17,6 +17,7 @@ class CreateOrderTable extends Migration ...@@ -17,6 +17,7 @@ class CreateOrderTable extends Migration
17 $table->increments('id'); 17 $table->increments('id');
18 $table->string('order_sn')->index()->default('')->comment('订单号'); 18 $table->string('order_sn')->index()->default('')->comment('订单号');
19 $table->unsignedBigInteger('user_id')->index()->comment('用户id'); 19 $table->unsignedBigInteger('user_id')->index()->comment('用户id');
20 + $table->string('description')->default('')->comment('订单描述');
20 $table->decimal('pay_amount')->comment('实付金额'); 21 $table->decimal('pay_amount')->comment('实付金额');
21 $table->decimal('goods_amount')->comment('商品金额'); 22 $table->decimal('goods_amount')->comment('商品金额');
22 $table->unsignedSmallInteger('status')->comment('订单状态:100待付款 101用户取消 102超时取消 103商户取消 201已付款 204已完成'); 23 $table->unsignedSmallInteger('status')->comment('订单状态:100待付款 101用户取消 102超时取消 103商户取消 201已付款 204已完成');
...@@ -24,6 +25,7 @@ class CreateOrderTable extends Migration ...@@ -24,6 +25,7 @@ class CreateOrderTable extends Migration
24 $table->string('source')->comment('来源'); 25 $table->string('source')->comment('来源');
25 $table->string('pay_number')->default('')->comment('支付交易号'); 26 $table->string('pay_number')->default('')->comment('支付交易号');
26 $table->string('pay_type')->default('')->comment('支付类型'); 27 $table->string('pay_type')->default('')->comment('支付类型');
28 + $table->string('callback')->default('')->comment('回调参数');
27 $table->timestamp('pay_time')->nullable()->comment('支付时间'); 29 $table->timestamp('pay_time')->nullable()->comment('支付时间');
28 $table->timestamps(); 30 $table->timestamps();
29 }); 31 });
......
...@@ -17,6 +17,7 @@ class CreateOrderGoodsTable extends Migration ...@@ -17,6 +17,7 @@ class CreateOrderGoodsTable extends Migration
17 $table->increments('id'); 17 $table->increments('id');
18 $table->string('order_sn')->comment('订单编号'); 18 $table->string('order_sn')->comment('订单编号');
19 $table->unsignedInteger('goods_id')->comment('商品id'); 19 $table->unsignedInteger('goods_id')->comment('商品id');
20 + $table->unsignedInteger('goods_type')->comment('商品类型');
20 $table->string('goods_name')->default('')->comment('商品名称'); 21 $table->string('goods_name')->default('')->comment('商品名称');
21 $table->string('goods_image')->default('')->comment('商品封面'); 22 $table->string('goods_image')->default('')->comment('商品封面');
22 $table->decimal('goods_price')->default('0.00')->comment('商品价格'); 23 $table->decimal('goods_price')->default('0.00')->comment('商品价格');
......
...@@ -16,17 +16,17 @@ class CreateMembershipGoodsTable extends Migration ...@@ -16,17 +16,17 @@ class CreateMembershipGoodsTable extends Migration
16 Schema::create('membership_goods', function (Blueprint $table) { 16 Schema::create('membership_goods', function (Blueprint $table) {
17 $table->increments('id'); 17 $table->increments('id');
18 $table->integer('membership_id')->comment('会员id'); 18 $table->integer('membership_id')->comment('会员id');
19 - $table->decimal('price')->comment('价格'); 19 + $table->decimal('price')->default('0.00')->comment('价格');
20 - $table->decimal('line_price')->comment('划线价格'); 20 + $table->decimal('line_price')->default('0.00')->comment('划线价格');
21 - $table->integer('limit_days')->comment('有效天数'); 21 + $table->integer('limit_days')->default(0)->comment('有效天数');
22 $table->string('limit_unit')->default('')->comment('有效期单位'); 22 $table->string('limit_unit')->default('')->comment('有效期单位');
23 $table->unsignedTinyInteger('terminal')->comment('1=Android,2=IOS'); 23 $table->unsignedTinyInteger('terminal')->comment('1=Android,2=IOS');
24 $table->unsignedTinyInteger('state')->comment('0=下架,1=售卖中'); 24 $table->unsignedTinyInteger('state')->comment('0=下架,1=售卖中');
25 $table->unsignedTinyInteger('sn')->comment('SN顺序'); 25 $table->unsignedTinyInteger('sn')->comment('SN顺序');
26 - $table->unsignedInteger('visits')->comment('访问量'); 26 + $table->unsignedInteger('visits')->default(0)->comment('访问量');
27 - $table->unsignedInteger('virtual_sales')->comment('虚拟销售量'); 27 + $table->unsignedInteger('virtual_sales')->default(0)->comment('虚拟销售量');
28 - $table->unsignedInteger('sales')->comment('销售量'); 28 + $table->unsignedInteger('sales')->default(0)->comment('销售量');
29 - $table->unsignedInteger('stocks')->comment('库存数量'); 29 + $table->unsignedInteger('stocks')->default(0)->comment('库存数量');
30 $table->timestamps(); 30 $table->timestamps();
31 }); 31 });
32 } 32 }
......
This diff is collapsed. Click to expand it.
1 +.extension-demo {
2 + font-size: 1.3rem;
3 + cursor: pointer;
4 +}
5 +
6 +.dengje-gallery-group {
7 + position: relative;
8 + margin: 5%;
9 +}
10 +
11 +.dengje-gallery-group.multiple .gallery-img-wrapper.bg-left {
12 + top: 5%;
13 + left: -5%;
14 + -webkit-transform: rotate(-5deg);
15 + -moz-transform: rotate(-5deg);
16 + -o-transform: rotate(-5deg);
17 + -ms-transform: rotate(-5deg);
18 + transform: rotate(-5deg);
19 +}
20 +
21 +.dengje-gallery-group.multiple .gallery-img-wrapper.bg-left:before {
22 + content: "";
23 + width: 100%;
24 + height: 100%;
25 + background: #eff4de;
26 + display: block;
27 +}
28 +
29 +.dengje-gallery-group.multiple .gallery-img-wrapper.bg-right {
30 + top: 5%;
31 + left: 5%;
32 + -webkit-transform: rotate(5deg);
33 + -moz-transform: rotate(5deg);
34 + -o-transform: rotate(5deg);
35 + -ms-transform: rotate(5deg);
36 + transform: rotate(5deg);
37 +}
38 +
39 +.dengje-gallery-group.multiple .gallery-img-wrapper.bg-right:before {
40 + content: "";
41 + width: 100%;
42 + height: 100%;
43 + background: #768590;
44 + display: block;
45 +}
46 +
47 +.dengje-gallery-group .gallery-img-wrapper {
48 + padding: 3%;
49 + background: #fff;
50 + height: 100%;
51 + width: 100%;
52 + z-index: 1;
53 + display: flex;
54 + justify-content: center;
55 + align-items: center;
56 + position: absolute;
57 + -webkit-box-shadow: .2em .2em .5em rgb(0 0 0 / 30%);
58 + -moz-box-shadow: .2em .2em .5em rgba(0, 0, 0, 0.3);
59 + box-shadow: .2em .2em .5em rgb(0 0 0 / 30%);
60 +}
61 +
62 +.dengje-gallery-group .gallery-img-wrapper > img {
63 + max-width: 100%;
64 + max-height: 100%;
65 +}
66 +
67 +.dengje-gallery-group .bg-multi {
68 + display: none;
69 +}
70 +
71 +.dengje-gallery-group.multiple .bg-multi {
72 + display: block;
73 +}
74 +
75 +.hide {
76 + display: none;
77 +}
1 +/*!
2 + * Viewer.js v1.5.0
3 + * https://fengyuanchen.github.io/viewerjs
4 + *
5 + * Copyright 2015-present Chen Fengyuan
6 + * Released under the MIT license
7 + *
8 + * Date: 2019-11-23T05:10:21.757Z
9 + */
10 +
11 +.viewer-zoom-in::before,
12 +.viewer-zoom-out::before,
13 +.viewer-one-to-one::before,
14 +.viewer-reset::before,
15 +.viewer-prev::before,
16 +.viewer-play::before,
17 +.viewer-next::before,
18 +.viewer-rotate-left::before,
19 +.viewer-rotate-right::before,
20 +.viewer-flip-horizontal::before,
21 +.viewer-flip-vertical::before,
22 +.viewer-fullscreen::before,
23 +.viewer-fullscreen-exit::before,
24 +.viewer-close::before {
25 + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC');
26 + background-repeat: no-repeat;
27 + background-size: 280px;
28 + color: transparent;
29 + display: block;
30 + font-size: 0;
31 + height: 20px;
32 + line-height: 0;
33 + width: 20px;
34 +}
35 +
36 +.viewer-zoom-in::before {
37 + background-position: 0 0;
38 + content: 'Zoom In';
39 +}
40 +
41 +.viewer-zoom-out::before {
42 + background-position: -20px 0;
43 + content: 'Zoom Out';
44 +}
45 +
46 +.viewer-one-to-one::before {
47 + background-position: -40px 0;
48 + content: 'One to One';
49 +}
50 +
51 +.viewer-reset::before {
52 + background-position: -60px 0;
53 + content: 'Reset';
54 +}
55 +
56 +.viewer-prev::before {
57 + background-position: -80px 0;
58 + content: 'Previous';
59 +}
60 +
61 +.viewer-play::before {
62 + background-position: -100px 0;
63 + content: 'Play';
64 +}
65 +
66 +.viewer-next::before {
67 + background-position: -120px 0;
68 + content: 'Next';
69 +}
70 +
71 +.viewer-rotate-left::before {
72 + background-position: -140px 0;
73 + content: 'Rotate Left';
74 +}
75 +
76 +.viewer-rotate-right::before {
77 + background-position: -160px 0;
78 + content: 'Rotate Right';
79 +}
80 +
81 +.viewer-flip-horizontal::before {
82 + background-position: -180px 0;
83 + content: 'Flip Horizontal';
84 +}
85 +
86 +.viewer-flip-vertical::before {
87 + background-position: -200px 0;
88 + content: 'Flip Vertical';
89 +}
90 +
91 +.viewer-fullscreen::before {
92 + background-position: -220px 0;
93 + content: 'Enter Full Screen';
94 +}
95 +
96 +.viewer-fullscreen-exit::before {
97 + background-position: -240px 0;
98 + content: 'Exit Full Screen';
99 +}
100 +
101 +.viewer-close::before {
102 + background-position: -260px 0;
103 + content: 'Close';
104 +}
105 +
106 +.viewer-container {
107 + bottom: 0;
108 + direction: ltr;
109 + font-size: 0;
110 + left: 0;
111 + line-height: 0;
112 + overflow: hidden;
113 + position: absolute;
114 + right: 0;
115 + -webkit-tap-highlight-color: transparent;
116 + top: 0;
117 + -ms-touch-action: none;
118 + touch-action: none;
119 + -webkit-touch-callout: none;
120 + -webkit-user-select: none;
121 + -moz-user-select: none;
122 + -ms-user-select: none;
123 + user-select: none;
124 +}
125 +
126 +.viewer-container::-moz-selection,
127 +.viewer-container *::-moz-selection {
128 + background-color: transparent;
129 +}
130 +
131 +.viewer-container::selection,
132 +.viewer-container *::selection {
133 + background-color: transparent;
134 +}
135 +
136 +.viewer-container img {
137 + display: block;
138 + height: auto;
139 + max-height: none !important;
140 + max-width: none !important;
141 + min-height: 0 !important;
142 + min-width: 0 !important;
143 + width: 100%;
144 +}
145 +
146 +.viewer-canvas {
147 + bottom: 0;
148 + left: 0;
149 + overflow: hidden;
150 + position: absolute;
151 + right: 0;
152 + top: 0;
153 +}
154 +
155 +.viewer-canvas > img {
156 + height: auto;
157 + margin: 15px auto;
158 + max-width: 90% !important;
159 + width: auto;
160 +}
161 +
162 +.viewer-footer {
163 + bottom: 0;
164 + left: 0;
165 + overflow: hidden;
166 + position: absolute;
167 + right: 0;
168 + text-align: center;
169 +}
170 +
171 +.viewer-navbar {
172 + background-color: rgba(0, 0, 0, 0.5);
173 + overflow: hidden;
174 +}
175 +
176 +.viewer-list {
177 + -webkit-box-sizing: content-box;
178 + box-sizing: content-box;
179 + height: 50px;
180 + margin: 0;
181 + overflow: hidden;
182 + padding: 1px 0;
183 +}
184 +
185 +.viewer-list > li {
186 + color: transparent;
187 + cursor: pointer;
188 + float: left;
189 + font-size: 0;
190 + height: 50px;
191 + line-height: 0;
192 + opacity: 0.5;
193 + overflow: hidden;
194 + -webkit-transition: opacity 0.15s;
195 + transition: opacity 0.15s;
196 + width: 30px;
197 +}
198 +
199 +.viewer-list > li:hover {
200 + opacity: 0.75;
201 +}
202 +
203 +.viewer-list > li + li {
204 + margin-left: 1px;
205 +}
206 +
207 +.viewer-list > .viewer-loading {
208 + position: relative;
209 +}
210 +
211 +.viewer-list > .viewer-loading::after {
212 + border-width: 2px;
213 + height: 20px;
214 + margin-left: -10px;
215 + margin-top: -10px;
216 + width: 20px;
217 +}
218 +
219 +.viewer-list > .viewer-active,
220 +.viewer-list > .viewer-active:hover {
221 + opacity: 1;
222 +}
223 +
224 +.viewer-player {
225 + background-color: #000;
226 + bottom: 0;
227 + cursor: none;
228 + display: none;
229 + left: 0;
230 + position: absolute;
231 + right: 0;
232 + top: 0;
233 +}
234 +
235 +.viewer-player > img {
236 + left: 0;
237 + position: absolute;
238 + top: 0;
239 +}
240 +
241 +.viewer-toolbar > ul {
242 + display: inline-block;
243 + margin: 0 auto 5px;
244 + overflow: hidden;
245 + padding: 3px 0;
246 +}
247 +
248 +.viewer-toolbar > ul > li {
249 + background-color: rgba(0, 0, 0, 0.5);
250 + border-radius: 50%;
251 + cursor: pointer;
252 + float: left;
253 + height: 24px;
254 + overflow: hidden;
255 + -webkit-transition: background-color 0.15s;
256 + transition: background-color 0.15s;
257 + width: 24px;
258 +}
259 +
260 +.viewer-toolbar > ul > li:hover {
261 + background-color: rgba(0, 0, 0, 0.8);
262 +}
263 +
264 +.viewer-toolbar > ul > li::before {
265 + margin: 2px;
266 +}
267 +
268 +.viewer-toolbar > ul > li + li {
269 + margin-left: 1px;
270 +}
271 +
272 +.viewer-toolbar > ul > .viewer-small {
273 + height: 18px;
274 + margin-bottom: 3px;
275 + margin-top: 3px;
276 + width: 18px;
277 +}
278 +
279 +.viewer-toolbar > ul > .viewer-small::before {
280 + margin: -1px;
281 +}
282 +
283 +.viewer-toolbar > ul > .viewer-large {
284 + height: 30px;
285 + margin-bottom: -3px;
286 + margin-top: -3px;
287 + width: 30px;
288 +}
289 +
290 +.viewer-toolbar > ul > .viewer-large::before {
291 + margin: 5px;
292 +}
293 +
294 +.viewer-tooltip {
295 + background-color: rgba(0, 0, 0, 0.8);
296 + border-radius: 10px;
297 + color: #fff;
298 + display: none;
299 + font-size: 12px;
300 + height: 20px;
301 + left: 50%;
302 + line-height: 20px;
303 + margin-left: -25px;
304 + margin-top: -10px;
305 + position: absolute;
306 + text-align: center;
307 + top: 50%;
308 + width: 50px;
309 +}
310 +
311 +.viewer-title {
312 + color: #ccc;
313 + display: inline-block;
314 + font-size: 12px;
315 + line-height: 1;
316 + margin: 0 5% 5px;
317 + max-width: 90%;
318 + opacity: 0.8;
319 + overflow: hidden;
320 + text-overflow: ellipsis;
321 + -webkit-transition: opacity 0.15s;
322 + transition: opacity 0.15s;
323 + white-space: nowrap;
324 +}
325 +
326 +.viewer-title:hover {
327 + opacity: 1;
328 +}
329 +
330 +.viewer-button {
331 + background-color: rgba(0, 0, 0, 0.5);
332 + border-radius: 50%;
333 + cursor: pointer;
334 + height: 80px;
335 + overflow: hidden;
336 + position: absolute;
337 + right: -40px;
338 + top: -40px;
339 + -webkit-transition: background-color 0.15s;
340 + transition: background-color 0.15s;
341 + width: 80px;
342 +}
343 +
344 +.viewer-button:focus,
345 +.viewer-button:hover {
346 + background-color: rgba(0, 0, 0, 0.8);
347 +}
348 +
349 +.viewer-button::before {
350 + bottom: 15px;
351 + left: 15px;
352 + position: absolute;
353 +}
354 +
355 +.viewer-fixed {
356 + position: fixed;
357 +}
358 +
359 +.viewer-open {
360 + overflow: hidden;
361 +}
362 +
363 +.viewer-show {
364 + display: block;
365 +}
366 +
367 +.viewer-hide {
368 + display: none;
369 +}
370 +
371 +.viewer-backdrop {
372 + background-color: rgba(0, 0, 0, 0.5);
373 +}
374 +
375 +.viewer-invisible {
376 + visibility: hidden;
377 +}
378 +
379 +.viewer-move {
380 + cursor: move;
381 + cursor: -webkit-grab;
382 + cursor: grab;
383 +}
384 +
385 +.viewer-fade {
386 + opacity: 0;
387 +}
388 +
389 +.viewer-in {
390 + opacity: 1;
391 +}
392 +
393 +.viewer-transition {
394 + -webkit-transition: all 0.3s;
395 + transition: all 0.3s;
396 +}
397 +
398 +@-webkit-keyframes viewer-spinner {
399 + 0% {
400 + -webkit-transform: rotate(0deg);
401 + transform: rotate(0deg);
402 + }
403 +
404 + 100% {
405 + -webkit-transform: rotate(360deg);
406 + transform: rotate(360deg);
407 + }
408 +}
409 +
410 +@keyframes viewer-spinner {
411 + 0% {
412 + -webkit-transform: rotate(0deg);
413 + transform: rotate(0deg);
414 + }
415 +
416 + 100% {
417 + -webkit-transform: rotate(360deg);
418 + transform: rotate(360deg);
419 + }
420 +}
421 +
422 +.viewer-loading::after {
423 + -webkit-animation: viewer-spinner 1s linear infinite;
424 + animation: viewer-spinner 1s linear infinite;
425 + border: 4px solid rgba(255, 255, 255, 0.1);
426 + border-left-color: rgba(255, 255, 255, 0.5);
427 + border-radius: 50%;
428 + content: '';
429 + display: inline-block;
430 + height: 40px;
431 + left: 50%;
432 + margin-left: -20px;
433 + margin-top: -20px;
434 + position: absolute;
435 + top: 50%;
436 + width: 40px;
437 + z-index: 1;
438 +}
439 +
440 +@media (max-width: 767px) {
441 + .viewer-hide-xs-down {
442 + display: none;
443 + }
444 +}
445 +
446 +@media (max-width: 991px) {
447 + .viewer-hide-sm-down {
448 + display: none;
449 + }
450 +}
451 +
452 +@media (max-width: 1199px) {
453 + .viewer-hide-md-down {
454 + display: none;
455 + }
456 +}
This diff could not be displayed because it is too large.
...@@ -7,7 +7,6 @@ return [ ...@@ -7,7 +7,6 @@ return [
7 'fields' => [ 7 'fields' => [
8 'name' => '会员名称', 8 'name' => '会员名称',
9 'origin_price' => '原价(分)', 9 'origin_price' => '原价(分)',
10 - 'limited_days' => '有效天数',
11 'intro' => '简介', 10 'intro' => '简介',
12 'state' => '状态', 11 'state' => '状态',
13 'membership_id' => '会员id', 12 'membership_id' => '会员id',
......
1 +<style>
2 + .box-card {
3 + width: 380px;
4 + border: 1px solid rgb(220, 223, 230);
5 + border-radius: 40px;
6 + margin-right: 24px;
7 + padding: 37px 20px;
8 + min-height: 779px;
9 + }
10 +
11 + .phone-content {
12 + border: 1px solid rgb(220, 223, 230);
13 + height: 705px;
14 + overflow: hidden;
15 + position: relative;
16 + background: rgb(245, 245, 245);
17 + }
18 +
19 + .text {
20 + font-size: 16px;
21 + font-weight: 700;
22 + color: rgb(38, 38, 38);
23 + text-align: center;
24 + position: absolute;
25 + width: 100%;
26 + top: 50px;
27 + box-sizing: border-box;
28 + }
29 +
30 + .poem-block {
31 + width: 315px;
32 + height: 330px;
33 + margin: 0;
34 + padding: 0;
35 + position: absolute;
36 + text-align: center;
37 + top: 200px;
38 + left: 10px;
39 + border-radius: 5px;
40 + background: rgba(87, 78, 78, 0.6);
41 + box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .3);
42 + overflow: hidden;
43 +
44 + display: flex;
45 + flex-direction: column;
46 + }
47 +
48 + .poem-title {
49 + color: #ffffff;
50 + font-size: 14px;
51 + font-weight: bold;
52 + margin: 10px;
53 + display: flex;
54 + flex-direction: row;
55 + justify-content: space-between;
56 + align-items: center;
57 + }
58 +
59 + .intro-title {
60 + color: #ffffff;
61 + font-size: 14px;
62 + font-weight: bold;
63 + margin: 10px;
64 + text-align: start;
65 + }
66 +
67 + .price{
68 + display: flex;
69 + flex-direction: row;
70 + justify-content: flex-start;
71 + }
72 +</style>
73 +<div class="box-card">
74 + <div class="phone-content">
75 + <div class="text">会员页</div>
76 + <img src="{{asset('storage/image/mobile-head.png')}}" alt="" width="338" height="80">
77 + <div style="min-height: 625px;">
78 + <img width="338" height="625" class="bg_img" src="{{asset('storage/images/a5fe2ba2bd71b543cbf4c6fb3968ab64.png')}}">
79 + </div>
80 + <div class="poem-block">
81 + <div class="intro-title">开发者249d42a097c1944e进行了广播推送,这是一条广播</div>
82 + <div class="poem-title">
83 + <div> 连续包月 </div>
84 + <div class="price">
85 + <span> 29 元 </span>
86 + <span> / 月</span>
87 + </div>
88 + <button type="button" class="btn btn-primary">开通</button>
89 + </div>
90 + <div class="poem-title">
91 + <div> 连续包年 </div>
92 + <div class="price">
93 + <span> 299 元 </span>
94 + <span> / 月</span>
95 + </div>
96 + <button type="button" class="btn btn-primary">开通</button>
97 + </div>
98 + <div class="intro-title" style="min-height: 100px">开发者249d42a097c1944e进行了广播推送,这是一条广播</div>
99 + <div class="intro-title" style="text-align: center">服务协议 | 隐私政策 | 恢复购买</div>
100 + </div>
101 + </div>
102 +</div>
103 +<hr>
104 +<button type="button" class="btn btn-primary sync"><i class="feather icon-repeat"></i> 同步基本设置</button>
105 +<script>
106 + Dcat.ready(function () {
107 + var asset = "{{asset('/storage/')}}";
108 +
109 + $(document).off('click', '.sync').on('click', '.sync', function () {
110 + let ori_top = 80;
111 + let top = parseInt($('.field_top').val()) + ori_top;
112 + let left = $('.field_left').val();
113 + let font = $('.field_font_size').val();
114 + let content_size = 12 + parseInt(font);
115 + let title_size = 14 + parseInt(font);
116 + let text_color = $('.text_color').val() || 'whitesmoke';
117 + let text_bg_color = $('.text_bg_color').val() || '#5c6bc6';
118 + let opacity = parseInt($('.opacity').val()) / 100;
119 +
120 + $('.poem-block').css('top', top + 'px').css('left', left + 'px')
121 + .css('background-color', text_bg_color).css('opacity', opacity);
122 + $('.poem-title').css('font-size', title_size + 'px').css('color', text_color);
123 + $('.poem-content').css('font-size', content_size + 'px').css('color', text_color);
124 +
125 +
126 + let bg_img_url = $('.bg_img_url').find("input[type='hidden'][name='bg_url']").val();
127 + if (bg_img_url !== '') {
128 + $('.bg_img').attr('src', asset + '/' + bg_img_url).css('display', 'block');
129 + }
130 +
131 + let bg_video_url = $('.bg_video_url').find("input[type='hidden'][name='bg_url']").val();
132 + if (bg_video_url !== ''){
133 + $('#bg_video').attr('src', asset + '/' + bg_video_url).css('display', 'block');
134 + let bg_video = document.getElementById('bg_video');
135 + bg_video.autoplay = true;
136 + bg_video.loop = true;
137 + }
138 +
139 + let bgm_url = $('.bgm_url').find("input[type='hidden'][name='bgm_url']").val();
140 + if (bgm_url !== ''){
141 + $('#bg_audio').attr('src', asset + '/' + bgm_url);
142 + let bg_audio = document.getElementById('bg_audio');
143 + bg_audio.autoplay = true;
144 + bg_audio.loop = true;
145 + }
146 + })
147 +
148 + })
149 +</script>
...\ No newline at end of file ...\ No newline at end of file
1 +<style>
2 + .dengje-gallery-group {
3 + height: {{ $height }}px;
4 + width: {{ $width }}px;
5 + font-size: {{ $width / 12 }};
6 + }
7 +</style>
8 +
9 +<div id="{{ $id }}" class="dengje-gallery-group {{ count($src) > 1 ? 'multiple' : '' }}">
10 + <div class="gallery-img-wrapper bg-multi bg-left"></div>
11 + <div class="gallery-img-wrapper bg-multi bg-right"></div>
12 + <div class="gallery-img-wrapper">
13 + @foreach ($src as $k => $v)
14 + <img src="{{ $v }}" class="{{ $k >= 1 ? 'hide' : '' }}" alt="">
15 + @endforeach
16 + </div>
17 +</div>
18 +
19 +<script >
20 + var image = new Viewer(document.getElementById('{{ $id }}'));
21 +</script>
...\ No newline at end of file ...\ No newline at end of file
...@@ -35,4 +35,10 @@ Route::prefix('v1')->namespace('App\Http\Controllers\V1')->group(function (Route ...@@ -35,4 +35,10 @@ Route::prefix('v1')->namespace('App\Http\Controllers\V1')->group(function (Route
35 35
36 /** 创建订单 */ 36 /** 创建订单 */
37 $api->apiResource('/order', 'OrderController'); 37 $api->apiResource('/order', 'OrderController');
38 +
39 + /** 调起支付 */
40 + $api->apiResource('/pay', 'PayController');
41 +
42 + /** 会员页 */
43 + $api->apiResource('/membership', 'MembershipController');
38 }); 44 });
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -16,3 +16,22 @@ use Illuminate\Support\Facades\Route; ...@@ -16,3 +16,22 @@ use Illuminate\Support\Facades\Route;
16 Route::get('/', function () { 16 Route::get('/', function () {
17 return view('welcome'); 17 return view('welcome');
18 }); 18 });
19 +
20 +
21 +Route::get('/create_overlay', function () {
22 + header ('Content-Type: image/png');
23 + $im = @imagecreatetruecolor(640, 1008) or die('Cannot Initialize new GD image stream');
24 + $white = imagecolorallocate($im, 255, 255, 255); //创建颜色
25 + imagefill($im,0,0,$white); //自定义画布的背景颜色
26 + $text_color = imagecolorallocate($im, 233, 14, 91); // 文字颜色
27 + $font = storage_path('app/public/ffmpeg/arialuni.ttf');
28 + $text = 'A Simple Text String';
29 + $box = imagettfbbox(40,0,$font,$text);
30 + $x = (640 - ($box[2] - $box[0])) / 2;
31 + $y = (1008 - ($box[7] - $box[1])) / 2;
32 + imagettftext($im,40,0,$x,$y,$text_color,$font,$text);
33 + imagepng($im);
34 + imagedestroy($im);
35 +
36 + dd($im);
37 +});
...\ No newline at end of file ...\ No newline at end of file
......