V1版本,商品入库、各类报表、商品预警

This commit is contained in:
adstat 2024-07-03 11:51:23 +08:00
parent 2484090058
commit bbc89a2355
77 changed files with 9134 additions and 42 deletions

View File

@ -0,0 +1,58 @@
<?php
namespace App\Console\Commands;
use App\Models\MessageStock;
use App\Models\Shop;
use App\Models\StockNotice;
use App\Services\Business\BusinessFactory;
use App\Utils\DateTimeUtils;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Pool\Core\Redis;
class ChkOrderServer extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:orderServer';
/**
* The console command description.
*
* @var string
*/
protected $description = '快团团根据更新时间获取增量售后单';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$shops = Shop::query()->where('plat_id', Shop::$PLAT_KTT)->where('status', Shop::$STATUS_AUTHORIZED)->get();
$endTime = DateTimeUtils::getMicroTime();
$beginTime = $endTime-(24*60*60*1000);
foreach ($shops as $shop) {
BusinessFactory::init()->make($shop->plat_id)->setShop($shop)->downloadOrdersAndSaveServer($beginTime, $endTime);
}
Log::info('任务完成:快团团根据更新时间获取增量售后单');
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Console\Commands;
use App\Models\GoodsSku;
use App\Models\MessageStock;
use App\Models\StockNotice;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Pool\Core\Redis;
class ChkPriceearly extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:priceearly';
/**
* The console command description.
*
* @var string
*/
protected $description = '检查价格异常';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$stock=StockNotice::where('id',2)->first();
$results = DB::table('business_order_items as a')
->select('c.title as cn_name', 'b.title as title', DB::raw('FORMAT(a.goods_price / 100, 2) AS goods_price'), 'b.cost','a.created_at','a.business_order_id')
->leftJoin('goods_skus as b', 'a.external_sku_id', '=', 'b.external_sku_id')
->leftJoin('goods as c', 'b.goods_id', '=', 'c.id')
->where('b.title', '!=', '')
->groupBy('a.id')
->havingRaw('goods_price < cost')
->get();
$redis=new Redis();
$arr=[];
foreach ($results as $k=>$v){
if (!$redis->exists($stock->cn_name.'::'.$v->title)){
$redis->set($stock->cn_name.'::'.$v->title,$v->business_order_id);
$arr[$k]['title']=$stock->cn_name;
$arr[$k]['user_id']=$stock->user_id;
$arr[$k]['stock_notice_id']=$stock->id;
$arr[$k]['content']=Carbon::now()->format('Y-m-d')." 订单号:{$v->business_order_id} 商品{$v->cn_name}规格{$v->title}价格有异常,当前售价{$v->goods_price}/支,当前成本价{$v->cost}/支";
$arr[$k]['created_at']=Carbon::now();
MessageStock::insert($arr);
}
}
Log::info('任务完成:check-ChkPriceearly');
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace App\Console\Commands;
use App\Models\GoodsSku;
use App\Models\MessageStock;
use App\Models\StockNotice;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Pool\Core\Redis;
class ChkReplenish extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:replenish';
/**
* The console command description.
*
* @var string
*/
protected $description = '检查库存缺货';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Log::info('任务完成:check-replenish');
$stock=StockNotice::where('id',1)->first();
$stocksQuery = DB::table('goods_skus AS a')
->select(
'a.id',
'a.title',
'a.num',
'a.stock','a.updated_at',
DB::raw('b.title AS good_name'),
DB::raw('SUM(a.num - a.stock) AS stocks')
)
->leftJoin('goods AS b', 'a.goods_id', '=', 'b.id')
->where('a.num', '!=', 0)
->where('a.stock', '!=', 0)
->groupBy('id')
->havingRaw("stocks < $stock->active_piece");
$stocks = $stocksQuery->get();
$currentDateTime = Carbon::now();
$endOfDay = Carbon::today()->endOfDay();
$secondsRemaining = $currentDateTime->diffInSeconds($endOfDay);
$redis=new Redis();
$arr=[];
foreach ($stocks as $k=>$v){
if (!$redis->exists($stock->cn_name.'::'.$v->title)){
$redis->setex($stock->cn_name.'::'.$v->title,$secondsRemaining,$v->stock);
$arr[$k]['title']=$stock->cn_name;
$arr[$k]['user_id']=$stock->user_id;
$arr[$k]['stock_notice_id']=$stock->id;
$arr[$k]['content']=Carbon::now()->format('Y-m-d')." 商品{$v->good_name}规格{$v->title}需要补货,当前库存{$v->num}/支,可售库存{$v->stock}/支";
$arr[$k]['created_at']=Carbon::now();
MessageStock::insert($arr);
}
}
Log::info('任务完成:check-replenish');
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace App\Console\Commands;
use App\Models\GoodsSku;
use App\Models\MessageStock;
use App\Models\StockNotice;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Pool\Core\Redis;
class ChkShelflife extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:shelflife';
/**
* The console command description.
*
* @var string
*/
protected $description = '检查商品即将过期';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$stock=StockNotice::where('id',3)->first();
$results = DB::table('warehouse_stock_item as a')
->leftJoin('goods_skus as b', 'a.good_sku_id', '=', 'b.id')
->leftJoin('goods as c', 'a.good_id', '=', 'c.id')
->select('c.title as cn_name', 'b.title as title','a.buyer_at as buyer_ats', DB::row('DATE(a.buyer_at) as buyer_at'),'a.stock')
->where('b.title', '!=', '')
->groupBy('a.id')
->get();
$today=Carbon::today()->toDateString();
$currentDateTime = Carbon::now();
$endOfDay = Carbon::today()->endOfDay();
$secondsRemaining = $currentDateTime->diffInSeconds($endOfDay);
$redis=new Redis();
foreach ($results as $k =>$v){
$ymd=explode('-',$v->buyer_at);
$date = Carbon::create($ymd[0], $ymd[1], $ymd[2]);
$date->addDays($stock->day_num);
if ($today >= $date->toDateString()){
//报警
if (!$redis->exists($stock->cn_name.'::'.$v->title)){
$redis->setex($stock->cn_name.'::'.$v->title,$secondsRemaining,$v->stock);
$arr[$k]['title']=$stock->cn_name;
$arr[$k]['user_id']=$stock->user_id;
$arr[$k]['stock_notice_id']=$stock->id;
$arr[$k]['content']=Carbon::now()->format('Y-m-d')." 商品{$v->cn_name}规格{$v->title}即将过期,入库库存{$v->stock}/支";
$arr[$k]['created_at']=Carbon::now();
MessageStock::insert($arr);
}
}
}
Log::info('任务完成:check-shelflife');
}
}

View File

@ -45,7 +45,7 @@ class KttOrderQuery extends Command
$endTime = DateTimeUtils::getMicroTime();
$beginTime = $endTime - 63000;
foreach ($shops as $shop) {
BusinessFactory::init()->make($shop->plat_id)->setShop($shop)->downloadOrdersAndSave($beginTime, $endTime + 3000);
BusinessFactory::init()->make($shop->plat_id)->setShop($shop)->downloadOrdersAndSave($beginTime, $endTime);
}
}
}

View File

@ -2,13 +2,20 @@
namespace App\Console;
use App\Console\Commands\ChkOrderServer;
use App\Console\Commands\ChkPriceearly;
use App\Console\Commands\ChkReplenish;
use App\Console\Commands\ChkShelflife;
use App\Console\Commands\DailySalesReport;
use App\Console\Commands\GoodsSkuDailyReport;
use App\Console\Commands\Inventory;
use App\Models\StockNotice;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\Console\Commands\KttOrderQuery;
use App\Console\Commands\DeleteKttQuery;
use Illuminate\Support\Facades\Log;
use function RingCentral\Psr7\str;
class Kernel extends ConsoleKernel
{
@ -19,6 +26,7 @@ class Kernel extends ConsoleKernel
*/
protected $commands = [
//
\App\Console\Commands\ChkReplenish::class,
];
/**
@ -29,22 +37,34 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
// 服务器/etc/crontab添加cron入口
// * * * * * cd /home/wwwroot/erp.chutang66.com && php artisan schedule:run >> /dev/null 2>&1
$schedule->command(KttOrderQuery::class)->everyMinute();
// $schedule->command(KttOrderQuery::class)->everyMinute();
//
// $schedule->command(GoodsSkuDailyReport::class)->dailyAt('06:00');
// $schedule->command(Inventory::class)->dailyAt('07:00');
//
// $schedule->command(DailySalesReport::class, ['S1'])->dailyAt('12:00');
// $schedule->command(DailySalesReport::class, ['S2'])->dailyAt('13:30');
// $schedule->command(DailySalesReport::class, ['S3'])->dailyAt('15:00');
// $schedule->command(DailySalesReport::class, ['S4'])->dailyAt('16:00');
// $schedule->command(DailySalesReport::class, ['S5'])->dailyAt('17:30');
// $schedule->command(DailySalesReport::class, ['S6'])->dailyAt('20:00');
// $schedule->command(DailySalesReport::class, ['S7'])->dailyAt('09:30');
//
// $schedule->command(DeleteKttQuery::class)->daily();
$schedule->command(GoodsSkuDailyReport::class)->dailyAt('06:00');
$schedule->command(Inventory::class)->dailyAt('07:00');
// $schedule->command('check:replenish')->everyMinute();
// $active_time=StockNotice::pluck('active_time');
$schedule->command(ChkReplenish::class);
$schedule->command(ChkOrderServer::class)->everyFifteenMinutes();
$schedule->command(ChkPriceearly::class)->everyFifteenMinutes();
$schedule->command(ChkShelflife::class)->everyFifteenMinutes();
// $schedule->command('check:priceearly')->dailyAt((string)$active_time[1]);
// $schedule->command('check:shelflife')->dailyAt((string)$active_time[2]);
// $schedule->command('check:orderServer')->everyFifteenMinutes();
$schedule->command(DailySalesReport::class, ['S1'])->dailyAt('12:00');
$schedule->command(DailySalesReport::class, ['S2'])->dailyAt('13:30');
$schedule->command(DailySalesReport::class, ['S3'])->dailyAt('15:00');
$schedule->command(DailySalesReport::class, ['S4'])->dailyAt('16:00');
$schedule->command(DailySalesReport::class, ['S5'])->dailyAt('17:30');
$schedule->command(DailySalesReport::class, ['S6'])->dailyAt('20:00');
$schedule->command(DailySalesReport::class, ['S7'])->dailyAt('09:30');
$schedule->command(DeleteKttQuery::class)->daily();
}
/**

View File

@ -48,7 +48,7 @@ class LoginController extends Controller
public function login(Request $request)
{
$credentials = $request->only('name', 'password');
// dd($credentials);
if (Auth::attempt($credentials)) {
// 通过认证..
return response()->json(['token' => $request->user()->api_token]);

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Http\Helpers\ApiResponse;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
@ -9,7 +10,8 @@ use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
use AuthorizesRequests, DispatchesJobs, ValidatesRequests,ApiResponse;
protected $res = [
'httpCode' => 200,

View File

@ -9,7 +9,6 @@ use App\Models\GoodsSku;
use App\Models\Shop;
use App\Http\Resources\ShopsResource;
use App\Models\ShopSender;
use App\Models\ShopShip;
use App\Services\Business\KuaiTuanTuan\FaceSheet;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@ -20,6 +19,10 @@ use App\Models\BusinessOrderItem;
class ShopsController extends Controller
{
public function test()
{
return ["msg"=>'ok','code'=>200];
}
public function index(Request $request)
{
$shops = Shop::query()->filter()->paginate($request->get('per_page'));
@ -228,18 +231,11 @@ class ShopsController extends Controller
public function pddPrintAuth(Request $request)
{
[$shopId, $platId] = explode('_', $request->get('state'));
[$shopId, $type] = explode('_', $request->get('state'));
$faceSheet = new FaceSheet();
$faceSheet->setCode($request->get('code'));
$faceSheet->setShopWithId($shopId);
$faceSheet->auth('ship');
$shopShip = ShopShip::query()
->where('shop_id', $shopId)
->first();
if (empty($shopShip)) {
exit();
}
$faceSheet->setShop($shopShip);
$shopShip = $faceSheet->auth('ship', $type);
$resp = $faceSheet->searchWayBill();
if (!isset($resp['pdd_waybill_search_response']['waybill_apply_subscription_cols'])) {
exit();

View File

@ -0,0 +1,141 @@
<?php
namespace App\Http\Helpers;
use Symfony\Component\HttpFoundation\Response as FoundationResponse;
trait ApiResponse
{
/**
* @var int
*/
protected $statusCode = FoundationResponse::HTTP_OK;
protected $token = '';
/**
* @return mixed
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* @param $statusCode
* @return $this
*/
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
/**
* @param $token
* @return $this
*/
public function setToken($token)
{
$this->token = $token;
return $this;
}
/**
* @param $data
* @return \Illuminate\Http\JsonResponse
*/
public function respond($data)
{
$response = response()->json($data, $this->getStatusCode());
if ($this->token) {
$response->headers->set('Authorization', 'Bearer ' . $this->token);
}
return $response;
}
/**
* @param $status
* @param array $data
* @param null $code
* @return mixed
*/
public function status($status, array $data, $code = null)
{
if ($code) {
$this->setStatusCode($code);
}
$status = [
'status' => $status,
'code' => $this->statusCode
];
$data = array_merge($status, $data);
return $this->respond($data);
}
/**
* @param $message
* @param int $code
* @param string $status
* @return mixed
*/
public function failed($message, $code = FoundationResponse::HTTP_BAD_REQUEST, $status = 'error')
{
return $this->setStatusCode($code)->message($message, $status);
}
/**
* @param $message
* @param string $status
* @return mixed
*/
public function message($message, $status = "success")
{
if(!is_array($message)) {
$message = [$message];
}
return $this->status($status, [
'message' => $message
]);
}
/**
* @param string $message
* @return mixed
*/
public function internalError($message = "Internal Error!")
{
return $this->failed($message, FoundationResponse::HTTP_INTERNAL_SERVER_ERROR);
}
/**
* @param string $message
* @return mixed
*/
public function created($message = "created")
{
return $this->setStatusCode(FoundationResponse::HTTP_CREATED)
->message($message);
}
/**
* @param $data
* @param string $status
* @return mixed
*/
public function success($data=[], $status = "success")
{
return $this->status($status, compact('data'));
}
/**
* @param string $message
* @return mixed
*/
public function notFond($message = 'Not Fond!')
{
return $this->failed($message, Foundationresponse::HTTP_NOT_FOUND);
}
}

View File

@ -0,0 +1,114 @@
<?php
namespace App\Http\Helpers;
use ErrorException;
use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class ExceptionReport
{
use ApiResponse;
/**
* @var Exception
*/
public $exception;
/**
* @var Request
*/
public $request;
/**
* @var
*/
protected $report;
/**
* ExceptionReport constructor.
* @param Request $request
* @param Exception $exception
*/
function __construct(Request $request, Exception $exception)
{
$this->request = $request;
$this->exception = $exception;
}
/**
* @var array
*/
//当抛出这些异常时可以使用我们定义的错误信息与HTTP状态码
//可以把常见异常放在这里
public $doReport = [
AuthenticationException::class => ['未登录或登录状态失效', 401],
ModelNotFoundException::class => ['该模型未找到', 404],
AuthorizationException::class => ['没有此权限', 403],
ValidationException::class => [],
UnauthorizedHttpException::class => ['未登录或登录状态失效', 401],
// TokenInvalidException::class => ['未登录或登录状态失效', 401],
NotFoundHttpException::class => ['没有找到该页面', 404],
MethodNotAllowedHttpException::class => ['访问方式不正确', 405],
ErrorException::class => ['服务器内部错误', 500],
QueryException::class => ['参数错误', 400],
];
public function register($className, callable $callback)
{
$this->doReport[$className] = $callback;
}
/**
* @return bool
*/
public function shouldReturn()
{
foreach (array_keys($this->doReport) as $report) {
if ($this->exception instanceof $report) {
$this->report = $report;
return true;
}
}
return false;
}
/**
* @param Exception $e
* @return static
*/
public static function make(Exception $e)
{
return new static(\request(), $e);
}
/**
* @return mixed
*/
public function report()
{
if ($this->exception instanceof ValidationException) {
return $this->failed(current($this->exception->errors()), $this->exception->status);
}
$message = $this->doReport[$this->report];
return $this->failed($message[0], $message[1]);
}
public function prodReport()
{
return $this->failed('服务器错误', '500');
}
}

View File

@ -0,0 +1,255 @@
<?php
namespace App\Http\Libraries\ExcelUtil;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Events\AfterSheet;
class export implements FromCollection,WithHeadings, WithEvents
{
protected $data;
protected $headings;
protected $sheetName; //设置sheet页名称
protected $columnWidth = [];//设置列宽 key列 value:宽
protected $rowHeight = []; //设置行高 key行 value:高
protected $mergeCells = []; //合并单元格 key第一个单元格 value:第二个单元格
protected $font = []; //设置字体 keyA1:K8 value:11
protected $bold = []; //设置粗体 keyA1:K8 value:true
protected $background = []; //设置背景颜色 keyA1:K8 value:#F0F0F0F
protected $vertical = []; //设置定位 keyA1:K8 value:center
protected $horizontal = []; //设置水平定位 keyA1:K8 value:center
protected $wrapText = []; //设置是否自动换行 keyA1:K8 value:bool
//设置页面属性时如果无效 更改excel格式尝试即可
//构造函数传值
public function __construct($data, $headings)
{
$this->data = $data;
$this->headings = $headings;
$this->createData();
}
public function headings(): array
{
return $this->headings;
}
//数组转集合
public function collection()
{
return new Collection($this->data);
}
//业务代码
public function createData()
{
$this->data = collect($this->data)->toArray();
}
public function registerEvents(): array
{
return [
AfterSheet::class => function(AfterSheet $event) {
//设置sheet页名称
$event->getSheet()->getDelegate()->setTitle($this->sheetName);
//设置自动换行
foreach ($this->wrapText as $column => $bool) {
$event->sheet->getDelegate()
->getStyle($column)
->getAlignment()
->setWrapText(true);
}
//设置列宽
foreach ($this->columnWidth as $column => $width) {
$event->sheet->getDelegate()
->getColumnDimension($column)
->setWidth($width);
}
//设置行高,$i为数据行数
foreach ($this->rowHeight as $row => $height) {
$event->sheet->getDelegate()
->getRowDimension($row)
->setRowHeight($height);
}
//设置区域单元格垂直居中
foreach ($this->vertical as $region => $position) {
$event->sheet->getDelegate()
->getStyle($region)
->getAlignment()
->setVertical($position);
}
//设置区域单元格水平定位
foreach ($this->horizontal as $region => $position) {
$event->sheet->getDelegate()
->getStyle($region)
->getAlignment()
->setHorizontal($position);
}
//设置区域单元格字体
foreach ($this->font as $region => $value) {
$event->sheet->getDelegate()
->getStyle($region)
->getFont()
->setSize($value);
}
//设置区域单元格字体粗体
foreach ($this->bold as $region => $bool) {
$event->sheet->getDelegate()
->getStyle($region)
->getFont()
->setBold($bool);
}
//设置区域单元格背景颜色
foreach ($this->background as $region => $item) {
$event->sheet->getDelegate()->getStyle($region)->applyFromArray([
'fill' => [
'fillType' => 'linear', //线性填充,类似渐变
'startColor' => [
'rgb' => $item //初始颜色
],
//结束颜色,如果需要单一背景色,请和初始颜色保持一致
'endColor' => [
'argb' => $item
]
]
]);
}
//合并单元格
foreach ($this->mergeCells as $start => $end) {
$event->sheet->getDelegate()->mergeCells($start.':'.$end);
}
}
];
}
/**
* @return array
* @2020/3/22 10:33
*/
public function setSheetName(string $sheetName)
{
$this->sheetName = trim($sheetName);
}
/**
* @return array
* @2020/3/22 10:33
* [
* A1:K7 => true
* ]
*/
public function setWrapText (array $wrapText)
{
$this->wrapText = array_change_key_case($this->symbol($wrapText),CASE_UPPER);
}
/**
* @return array
* @2020/3/22 10:33
* [
* A1:K7 => center
* ]
*/
public function setVertical (array $vertical)
{
$this->vertical = array_change_key_case($this->symbol($vertical),CASE_UPPER);
}
/**
* @return array
* @2020/3/22 10:33
* [
* A1:K7 => center
* ]
*/
public function setHorizontal (array $horizontal)
{
$this->horizontal = array_change_key_case($this->symbol($horizontal),CASE_UPPER);
}
/**
* @return array
* @2020/3/22 10:33
* [
* 'B' => 40,
* 'C' => 60
* ]
*/
public function setColumnWidth (array $columnwidth)
{
$this->columnWidth = array_change_key_case($columnwidth, CASE_UPPER);
}
/**
* @return array
* @2020/3/22 10:33
* [
* 1 => 40,
* 2 => 60
* ]
*/
public function setRowHeight (array $rowHeight)
{
$this->rowHeight = $rowHeight;
}
/**
* @return array
* @2020/3/22 10:33
* [
* A1:K7 => 12
* ]
*/
public function setFont (array $fount)
{
$this->font = array_change_key_case($fount, CASE_UPPER);
}
/**
* @return array
* @2020/3/22 10:33
* [
* A1:K7 => true
* ]
*/
public function setBold (array $bold)
{
$this->bold = array_change_key_case($bold, CASE_UPPER);
}
/**
* @return array
* @2020/3/22 10:33
* [
* A1:K7 => F0FF0F
* ]
*/
public function setBackground (array $background)
{
$this->background = array_change_key_case($background, CASE_UPPER);
}
public function symbol ($param) {
$record = [];
$count = count($this->data) + 1;//加上表头
foreach ($param as $key => $value) {
$str = str_replace("*", $count, $key);
$record[$str] = $value;
}
return $record;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Libraries\ExcelUtil;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
class import implements ToCollection
{
public $data;
protected $delTitle;
public function __construct($delTitle = 1)
{
$this->delTitle = $delTitle;
}
public function collection(Collection $rows)
{
$this->delTitle($rows);
//$rows 是数组格式
$this->data = $rows;
}
public function delTitle (&$rows) {
$rows = $rows->slice($this->delTitle)->values();
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
class BusinessOrderServer extends Model
{
protected $table='business_order_server';
public $timestamps = false;
protected $dates = ['lasted_at'];
public function save(array $options = [])
{
if(!$this->exists) {
} else {
$this->lasted_at = Carbon::now();
}
parent::save($options);
}
}

View File

@ -1,9 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class GoodsType extends Model
{
protected $table='goods_types';
/**
* 数组中的属性会被隐藏。
*

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MessageStock extends Model
{
protected $table='message_stock';
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class StockNotice extends Model
{
protected $table='stock_notice';
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class StockTackItem extends Model
{
protected $table='stock_tack_item';
}

11
app/Models/Supplier.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model
{
protected $table='supplier';
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class SupplierType extends Model
{
protected $table='supplier_type';
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class WarehouseStock extends Model
{
protected $table='warehouse_stock';
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class WarehouseStockItem extends Model
{
protected $table='warehouse_stock_item';
}

912
app/Pool/Core/Redis.php Normal file
View File

@ -0,0 +1,912 @@
<?php
namespace App\Pool\Core;
class Redis
{
protected $redis;
public function __construct()
{
$this->redis = app('redis.connection');
$this->redis->auth(env('REDIS_PASSWORD','123456'));
}
/*****************hash表操作函数*******************/
/**
* hGet 得到hash表中一个字段的值
* @param string $key 缓存key
* @param string $field 字段
* @return mixed string|false
*/
public function hGet($key, $field)
{
return $this->redis->hGet($key, $field);
}
/**
* 为hash表设定一个字段的值
* @param string $key 缓存key
* @param string $field 字段
* @param string $value 值。
* @return bool
*/
public function hSet($key, $field, $value)
{
return $this->redis->hSet($key,$field,$value);
}
/**
* hExists 判断hash表中指定field是不是存在
* @param string $key 缓存key
* @param string $field 字段
* @return bool
*/
public function hExists($key, $field)
{
return $this->redis->hExists($key, $field);
}
/**
* hDel 删除hash表中指定字段 ,支持批量删除
* @param string $key 缓存key
* @param string $field 字段
* @return int
*/
public function hDel($key, $field)
{
$fieldArr = explode(',', $field);
$delNum = 0;
foreach($fieldArr as $row)
{
$row = trim($row);
$delNum += $this->redis->hDel($key,$row);
}
return $delNum;
}
/**
* hLen 返回hash表元素个数
* @param string $key 缓存key
* @return int|bool
*/
public function hLen($key)
{
return $this->redis->hLen($key);
}
/**
* hSetNx 为hash表设定一个字段的值,如果字段存在返回false
* @param string $key 缓存key
* @param string $field 字段
* @param string $value 值。
* @return bool
*/
public function hSetNx($key, $field, $value)
{
return $this->redis->hSetNx($key, $field, $value);
}
/**
* hMset 为hash表多个字段设定值。
* @param string $key
* @param array $value
* @return array|bool
*/
public function hMset($key, $value)
{
if(!is_array($value))
return false;
return $this->redis->hMset($key, $value);
}
/**
* hMget 为hash表多个字段设定值。
* @param string $key
* @param array|string $value string以','号分隔字段
* @return array|bool
*/
public function hMget($key, $value)
{
if(!is_array($value))
$value = explode(',', $value);
return $this->redis->hMget($key, $value);
}
/**
* hIncrBy 为hash表设这累加可以负数
* @param string $key
* @param int $field
* @param string $value
* @return bool
*/
public function hIncrBy($key, $field, $value)
{
$value = intval($value);
return $this->redis->hIncrBy($key, $field, $value);
}
/**
* hKeys 返回所有hash表的所有字段
* @param string $key
* @return array|bool
*/
public function hKeys($key)
{
return $this->redis->hKeys($key);
}
/**
* hVals 返回所有hash表的字段值为一个索引数组
* @param string $key
* @return array|bool
*/
public function hVals($key)
{
return $this->redis->hVals($key);
}
/**
* hGetAll 返回所有hash表的字段值为一个关联数组
* @param string $key
* @return array|bool
*/
public function hGetAll($key)
{
return $this->redis->hGetAll($key);
}
/*********************有序集合操作*********************/
/**
* zAdd 给当前集合添加一个元素
* 如果value已经存在会更新order的值。
* @param string $key
* @param string $order 序号
* @param string $value
* @return bool
*/
public function zAdd($key, $order, $value)
{
return $this->redis->zAdd($key, $order, $value);
}
/**
* zincrby $value成员的order值增加$num,可以为负数
* @param string $key
* @param string $num 序号
* @param string $value
* @return mixed 返回新的order
*/
public function zincrby($key, $num, $value)
{
return $this->redis->zincrby($key, $num, $value);
}
public function incrby($key, $num=1)
{
return $this->redis->incrby($key,$num);
}
/**
* zRem 删除值为value的元素
* @param string $key
* @param string $value
* @return bool
*/
public function zRem($key, $value)
{
return $this->redis->zRem($key, $value);
}
/**
* zRange 集合以order递增排列后0表示第一个元素-1表示最后一个元素
* @param string $key
* @param int $start
* @param int $end
* @return mixed array|bool
*/
public function zRange($key, $start, $end)
{
return $this->redis->zRange($key, $start, $end);
}
/**
* zRevRange 集合以order递减排列后0表示第一个元素-1表示最后一个元素
* @param string $key
* @param int $start
* @param int $end
* @return array|bool
*/
public function zRevRange($key, $start, $end)
{
return $this->redis->zRevRange($key, $start, $end);
}
/**
* 集合以order递增排列后返回指定order之间的元素。
* min和max可以是-inf和+inf 表示最大值最小值
* @param string $key
* @param int $start '-inf'
* @param int $end "+inf"
* @param array $option 参数 package
* withscores=>true表示数组下标为Order值默认返回索引数组
* limit=>array(0,1) 表示从0开始取一条记录。
* @return array|bool
*/
public function zRangeByScore($key, $start = 0, $end = 0, $option = [])
{
return $this->redis->zRangeByScore($key, $start, $end, $option);
}
/**
* 集合以order递减排列后返回指定order之间的元素。
* min和max可以是-inf和+inf 表示最大值最小值
* @param string $key
* @param int $start '-inf'
* @param int $end "+inf"
* @param array $option 参数 package
* withscores=>true表示数组下标为Order值默认返回索引数组
* limit=>array(0,1) 表示从0开始取一条记录。
* @return array|bool
*/
public function zRevRangeByScore($key, $start = 0, $end = 0, $option = [])
{
return $this->redis->zRevRangeByScore($key,$start,$end,$option);
}
/**
* zCount 返回order值在start end之间的数量
* @param $key
* @param $start
* @param $end
* @return mixed
*/
public function zCount($key, $start, $end)
{
return $this->redis->zCount($key, $start, $end);
}
/**
* zScore 返回值为value的order值
* @param $key
* @param $value
* @return mixed
*/
public function zScore($key, $value)
{
return $this->redis->zScore($key, $value);
}
/**
* zRank 返回集合以score递增加排序后指定成员的排序号从0开始。
* @param $key
* @param $value
* @return mixed
*/
public function zRank($key,$value)
{
return $this->redis->zRank($key,$value);
}
/**
* zRevRank 返回集合以score递增加排序后指定成员的排序号从0开始。
* @param $key
* @param $value
* @return mixed
*/
public function zRevRank($key,$value)
{
return $this->redis->zRevRank($key,$value);
}
/**
* zRemRangeByScore 删除集合中score值在start end之间的元素 包括start end
* min和max可以是-inf和+inf 表示最大值最小值
* @param $key
* @param $start
* @param $end
* @return mixed 删除成员的数量
*/
public function zRemRangeByScore($key,$start,$end)
{
return $this->redis->zRemRangeByScore($key,$start,$end);
}
/**
* zCard 返回集合元素个数
* @param $key
* @return mixed
*/
public function zCard($key)
{
return $this->redis->zCard($key);
}
/*********************队列操作命令************************/
/**
* rPush 在队列尾部插入一个元素
* @param $key
* @param $value
* @return mixed 返回队列长度
*/
public function rPush($key,$value)
{
return $this->redis->rPush($key,$value);
}
/**
* rPushx 在队列尾部插入一个元素 如果key不存在什么也不做
* @param $key
* @param $value
* @return mixed 返回队列长度
*/
public function rPushx($key,$value)
{
return $this->redis->rPushx($key,$value);
}
/**
* lPush 在队列头部插入一个元素
* @param $key
* @param $value
* @return mixed 返回队列长度
*/
public function lPush($key,$value)
{
return $this->redis->lPush($key,$value);
}
/**
* lPushx 在队列头插入一个元素 如果key不存在什么也不做
* @param $key
* @param $value
* @return mixed 返回队列长度
*/
public function lPushx($key,$value)
{
return $this->redis->lPushx($key,$value);
}
/**
* lLen 返回队列长度
* @param $key
* @return mixed
*/
public function lLen($key)
{
return $this->redis->lLen($key);
}
/**
* lRange 返回队列指定区间的元素
* @param $key
* @param $start
* @param $end
* @return mixed
*/
public function lRange($key,$start,$end)
{
return $this->redis->lrange($key,$start,$end);
}
/**
* lIndex 返回队列中指定索引的元素
* @param $key
* @param $index
* @return mixed
*/
public function lIndex($key,$index)
{
return $this->redis->lIndex($key,$index);
}
/**
* lSet 设定队列中指定index的值。
* @param $key
* @param $index
* @param $value
* @return mixed
*/
public function lSet($key,$index,$value)
{
return $this->redis->lSet($key,$index,$value);
}
/**
* lRem 删除值为value的count个元素
* PHP-REDIS扩展的数据顺序与命令的顺序不太一样不知道是不是bug
* count>0 从尾部开始
* >0 从头部开始
* =0 删除全部
* @param $key
* @param $count
* @param $value
* @return mixed
*/
public function lRem($key,$count,$value)
{
return $this->redis->lRem($key,$value,$count);
}
/**
* lPop 删除并返回队列中的头元素
* Created by PhpStorm.
* User: w
* Date: 2018-12-09
* Time: 12:13
* @param $key
* @return mixed
*/
public function lPop($key)
{
return $this->redis->lPop($key);
}
/**
* rPop 删除并返回队列中的尾元素
* @param $key
* @return mixed
*/
public function rPop($key)
{
return $this->redis->rPop($key);
}
/*************redis字符串操作命令*****************/
/**
* set 设置一个key
* @param $key
* @param $value
* @return mixed
*/
public function set($key,$value)
{
return $this->redis->set($key,$value);
}
/**
* get 得到一个key
* @param $key
* @return mixed
*/
public function get($key)
{
return $this->redis->get($key);
}
/**
* setex 设置一个有过期时间的key
* @param $key
* @param $expire
* @param $value
* @return mixed
*/
public function setex($key,$expire,$value)
{
return $this->redis->setex($key,$expire,$value);
}
/**
* setnx 设置一个key,如果key存在,不做任何操作.
* @param $key
* @param $value
* @return mixed
*/
public function setnx($key,$value)
{
return $this->redis->setnx($key,$value);
}
/**
* mset 批量设置key
* @param $arr
* @return mixed
*/
public function mset($arr)
{
return $this->redis->mset($arr);
}
/*************redis 无序集合操作命令*****************/
/**
* sMembers 返回集合中所有元素
* @param $key
* @return mixed
*/
public function sMembers($key)
{
return $this->redis->sMembers($key);
}
/**
* sDiff 求2个集合的差集
* @param $key1
* @param $key2
* @return mixed
*/
public function sDiff($key1,$key2)
{
return $this->redis->sDiff($key1,$key2);
}
/**
* sAdd 添加集合。由于版本问题,扩展不支持批量添加。
* @param $key
* @param string|array $value
*/
public function sAdd($key,$value)
{
if(!is_array($value))
$arr = array($value);
else
$arr = $value;
foreach($arr as $row)
$this->redis->sAdd($key,$row);
}
/**
* scard 返回无序集合的元素个数
* Created by PhpStorm.
* @param $key
* @return mixed
*/
public function scard($key)
{
return $this->redis->scard($key);
}
/**
* srem 从集合中删除一个元素
* @param $key
* @param $value
* @return mixed
*/
public function srem($key,$value)
{
return $this->redis->srem($key,$value);
}
/*************redis管理操作命令*****************/
/**
* 选择数据库
* @param int $dbId 数据库ID号
* @return bool
*/
public function select($dbId)
{
$this->dbId = $dbId;
return $this->redis->select($dbId);
}
/**
* 清空当前数据库
* @return bool
*/
public function flushDB()
{
return $this->redis->flushDB();
}
/**
* info 返回当前库状态
* @return string
*/
public function info()
{
return $this->redis->info();
}
/**
* 同步保存数据到磁盘
*/
public function save()
{
return $this->redis->save();
}
/**
* 异步保存数据到磁盘
*/
public function bgSave()
{
return $this->redis->bgSave();
}
/**
* 返回最后保存到磁盘的时间
*/
public function lastSave()
{
return $this->redis->lastSave();
}
/**
* 返回key,支持*多个字符,?一个字符
* 只有* 表示全部
* @param string $key
* @return array
*/
public function keys($key)
{
return $this->redis->keys($key);
}
/**
* del 删除指定key
* @param $key
* @return mixed
*/
public function del($key)
{
return $this->redis->del($key);
}
/**
* exists 判断一个key值是不是存在
* @param $key
* @return mixed
*/
public function exists($key)
{
return $this->redis->exists($key);
}
/**
* expire 为一个key设定过期时间 单位为秒
* @param $key
* @param $expire
* @return mixed
*/
public function expire($key,$expire)
{
return $this->redis->expire($key,$expire);
}
/**
* ttl 返回一个key还有多久过期单位秒
* @param $key
* @return mixed
*/
public function ttl($key)
{
return $this->redis->ttl($key);
}
/**
* expireAt 设定一个key什么时候过期time为一个时间戳
* @param $key
* @param $time
* @return mixed
*/
public function expireAt($key,$time)
{
return $this->redis->expireAt($key,$time);
}
/**
* 关闭服务器链接
*/
public function close()
{
return $this->redis->close();
}
/**
* 关闭所有连接
*/
public static function closeAll()
{
foreach(static::$_instance as $o)
{
if($o instanceof self)
$o->close();
}
}
/** 这里不关闭连接因为session写入会在所有对象销毁之后。
public function __destruct()
{
return $this->redis->close();
}
**/
/**
* 返回当前数据库key数量
*/
public function dbSize()
{
return $this->redis->dbSize();
}
/**
* 返回一个随机key
*/
public function randomKey()
{
return $this->redis->randomKey();
}
/**
* 得到当前数据库ID
* @return int
*/
public function getDbId()
{
return $this->dbId;
}
/**
* 返回当前密码
*/
public function getAuth()
{
return $this->auth;
}
public function getHost()
{
return $this->host;
}
public function getPort()
{
return $this->port;
}
public function getConnInfo()
{
return [
'host' => $this->host,
'port' => $this->port,
'auth' => $this->auth
];
}
/*********************事务的相关方法************************/
/**
* watch 监控key,就是一个或多个key添加一个乐观锁
* 在此期间如果key的值如果发生的改变刚不能为key设定值
* 可以重新取得Key的值。
* @param $key
* @return mixed
*/
public function watch($key)
{
return $this->redis->watch($key);
}
/**
* 取消当前链接对所有key的watch
* EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH
*/
public function unwatch()
{
return $this->redis->unwatch();
}
/**
* multi 开启一个事务
* 事务的调用有两种模式Redis::MULTI和Redis::PIPELINE
* 默认是Redis::MULTI 模式,
* Redis::PIPELINE管道模式速度更快但没有任何保证原子性有可能造成数据的丢失
* @param $type
* @return mixed
*/
public function multi($type = \Redis::MULTI)
{
return $this->redis->multi($type);
}
/**
* 执行一个事务
* 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行
*/
public function exec()
{
return $this->redis->exec();
}
/**
* 回滚一个事务
*/
public function discard()
{
return $this->redis->discard();
}
/**
* 测试当前链接是不是已经失效
* 没有失效返回+PONG
* 失效返回false
*/
public function ping()
{
return $this->redis->ping();
}
public function auth($auth)
{
return $this->redis->auth($auth);
}
/*********************自定义的方法,用于简化操作************************/
/**
* hashAll 得到一组的ID号
* @param $prefix
* @param $ids
* @return array|bool
*/
public function hashAll($prefix,$ids)
{
if($ids == false)
return false;
if( is_string($ids) )
$ids = explode(',', $ids);
$arr = [];
foreach($ids as $id) {
$key = $prefix.'.'.$id;
$res = $this->hGetAll($key);
if($res != false)
$arr[] = $res;
}
return $arr;
}
/**
* pushMessage 生成一条消息放在redis数据库中。使用0号库。
* @param $lkey
* @param string|array $msg
* @return string
*/
public function pushMessage($lkey, $msg)
{
if(is_array($msg))
$msg = json_encode($msg);
$key = md5($msg);
//如果消息已经存在,删除旧消息,已当前消息为准
//echo $n=$this->lRem($lkey, 0, $key)."\n";
//重新设置新消息
$this->lPush($lkey, $key);
$this->setex($key, 3600, $msg);
return $key;
}
/**
* delKeys 得到条批量删除key的命令
* @param $keys
* @param $dbId
* @return string
*/
public function delKeys($keys,$dbId)
{
$redisInfo = $this->getConnInfo();
$cmdArr = [
'redis-cli',
'-a',
$redisInfo['auth'],
'-h',
$redisInfo['host'],
'-p',
$redisInfo['port'],
'-n',
$dbId,
];
$redisStr = implode(' ', $cmdArr);
$cmd = "{$redisStr} KEYS \"{$keys}\" | xargs {$redisStr} del";
return $cmd;
}
}

View File

@ -7,12 +7,15 @@ use App\Events\BusinessOrdersUpdate;
use App\Models\BusinessGoodsSku;
use App\Models\BusinessOrder;
use App\Models\BusinessOrderItem;
use App\Models\BusinessOrderServer;
use App\Models\GoodsSku;
use App\Models\Log;
use App\Models\Shop;
use App\Pool\Core\Redis;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Auth;
use GuzzleHttp\Promise;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log as LogFile;
abstract class BusinessClient
@ -37,6 +40,7 @@ abstract class BusinessClient
abstract public function incrQuantity($businessGoodsSku, $num, $incremental);
abstract public function downloadOrdersAndSave($beginTime, $endTime, $downloadType = 'default', $page = 1);
abstract public function downloadOrdersAndSaveServer($beginTime, $endTime, $page = 1);
public function saveOrders($orders)
{
@ -101,6 +105,41 @@ abstract class BusinessClient
$orderRecord->save();
}
}
public function saveOrdersServer($orders)
{
$shopId = $this->getShop()->id;
$model=new BusinessOrderServer();
$redis=new Redis();
foreach ($orders as $k=>$v){
$key='orderReturn::'.$v['order_sn'];
if (!$redis->exists($key)){
$redis->set($key,md5(json_encode($v)));
$model->shop_id=$shopId;
// $model->refund_amount=number_format($v['apply_extension']['refund_amount']/100,2);
$model->refund_amount=$v['apply_extension']['refund_amount'];
$model->refund_shipping_amount=$v['apply_extension']['refund_shipping_amount'];
$model->description=$v['apply_extension']['description'];
$model->reason=$v['apply_extension']['reason'];
$model->sub_extensions=isset($v['apply_extension']['sub_extensions'])?json_encode($v['apply_extension']['sub_extensions']):'';
$model->order_sn=$v['order_sn'];
$model->after_sales_status=$v['after_sales_status'];
$model->apply_type=$v['apply_type'];
$model->created_at=$v['created_at'];
$model->updated_at=$v['updated_at'];
$model->save();
if (isset($v['apply_extension']['sub_extensions'])){
$arr=$v['apply_extension']['sub_extensions'];
foreach ($arr as $k1=>$v1){
$v1[$k1]['shop_id']=$shopId;
$v1[$k1]['order_sn']=$v['order_sn'];
$v1[$k1]['business_order_server_id']=$model->id;
}
DB::table('business_order_server_item')->insert($arr);
}
}
}
}
public function authCallback($code, $shop)
{
@ -156,12 +195,12 @@ abstract class BusinessClient
if (strlen($paramsJson) > 1024) {
$paramsJson = '';
}
if (!in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list'], true)) {
if (!in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list','pdd.ktt.after.sales.increment.list'], true)) {
$log = new Log();
$log->module = 'plat';
$log->action = $method;
$log->target_type = $this->getShop()->plat_id . '--' . $this->getShop()->name;
$log->target_id = $this->getShop()->id;
$log->target_type = 2 . '--' . '花甜悦事';
$log->target_id = 2;
$log->target_field = $params['type'];
$log->user_id = Auth::id() ?? 999;
if ($size < 48000) {
@ -171,7 +210,7 @@ abstract class BusinessClient
}
$log->save();
}
if (in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list'], true)) {
if (in_array($params['type'], ['pdd.ktt.increment.order.query', 'pdd.ktt.order.list','pdd.ktt.after.sales.increment.list'], true)) {
LogFile::info('快团团请求: ' . $paramsJson);
LogFile::info('快团团返回: ' . json_encode($res, 256));
}

View File

@ -122,6 +122,23 @@ class KuaiTuanTuan extends BusinessClient
$this->downloadOrdersAndSave($beginTime, $endTime, $downloadType, $page + 1);
}
}
public function downloadOrdersAndSaveServer($beginTime, $endTime, $page = 1)
{
[$type, $appendParams] = OrderServer::getIncrementOrdersServer($beginTime, $endTime, $page);
$responseName = 'ktt_after_sales_incermet_list_response';
$res = $this->doRequest($type, $appendParams);
if (!isset($res[$responseName])) {
return;
}
$this->saveOrdersServer($res[$responseName]['list']);
if ($res[$responseName]['has_next']==false){
return;
}
$pageNum = ceil($res[$responseName]['total_count'] / $appendParams['page_size']);
if ($pageNum > $page && 30 >= $page) {
$this->downloadOrdersAndSaveServer($beginTime, $endTime, $page + 1);
}
}
public function getOrderInfo($orderSn)
{

View File

@ -92,5 +92,6 @@ class Order
return [$type, $appendParams];
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Services\Business\KuaiTuanTuan;
class OrderServer
{
public static function getIncrementOrdersServer($beginTime, $endTime, $page = 1)
{
$type = 'pdd.ktt.after.sales.increment.list';
$appendParams = [
'start_updated_at' => $beginTime, // 更新起始时间
'end_updated_at' => $endTime, // 更新结束时间
'page_number' => $page, // 页码
'page_size' => 100, // 数量
];
return [$type, $appendParams];
}
}

View File

@ -2,11 +2,15 @@
namespace App\Utils;
use App\Imports\CombinationGoodsImport;
use Illuminate\Http\UploadedFile;
use Intervention\Image\Facades\Image;
use Maatwebsite\Excel\Events\ImportFailed;
use Maatwebsite\Excel\Facades\Excel;
use OSS\Core\OssException;
use OSS\OssClient;
use Illuminate\Support\Str;
use App\Http\Libraries\ExcelUtil\import;
class UploadUtils
{
@ -119,5 +123,19 @@ class UploadUtils
{
return config('filesystems.disks.aliyun.url') . $filePath;
}
public static function uploadExcel ($file){
if($file->isValid()){
$vailExtension = ['xlsx','xls'];
if(! in_array($file->getClientOriginalExtension() ,$vailExtension) ){
throw new \Exception("文件格式错误");
}
// return Excel::toArray(new Import, $file)[0];
return Excel::toArray($file)[0];
}else{
throw new \Exception("文件无效");
}
}
}

View File

@ -17,6 +17,7 @@
"laravel/framework": "^6.20.26",
"laravel/tinker": "^2.5",
"maatwebsite/excel": "^3.1",
"predis/predis": "^2.2",
"spatie/laravel-permission": "*"
},
"require-dev": {

69
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "037b06c1b26399725a1d9c0687402942",
"content-hash": "964631bbee47f895975146a783331c50",
"packages": [
{
"name": "aliyuncs/oss-sdk-php",
@ -2587,6 +2587,73 @@
],
"time": "2022-07-30T15:51:26+00:00"
},
{
"name": "predis/predis",
"version": "v2.2.2",
"source": {
"type": "git",
"url": "https://github.com/predis/predis.git",
"reference": "b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/predis/predis/zipball/b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1",
"reference": "b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.3",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^8.0 || ~9.4.4"
},
"suggest": {
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
},
"type": "library",
"autoload": {
"psr-4": {
"Predis\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Till Krüss",
"homepage": "https://till.im",
"role": "Maintainer"
}
],
"description": "A flexible and feature-complete Redis client for PHP.",
"homepage": "http://github.com/predis/predis",
"keywords": [
"nosql",
"predis",
"redis"
],
"support": {
"issues": "https://github.com/predis/predis/issues",
"source": "https://github.com/predis/predis/tree/v2.2.2"
},
"funding": [
{
"url": "https://github.com/sponsors/tillkruss",
"type": "github"
}
],
"time": "2023-09-13T16:42:03+00:00"
},
{
"name": "psr/container",
"version": "1.1.1",

View File

@ -58,8 +58,16 @@ return [
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'modes' => [
'STRICT_ALL_TABLES',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_ZERO_DATE',
'NO_ZERO_IN_DATE',
'NO_AUTO_CREATE_USER',
],
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode=(SELECT REPLACE(@@sql_mode, "ONLY_FULL_GROUP_BY", ""))'
]) : [],
],

5
resources/frontend/.env Normal file
View File

@ -0,0 +1,5 @@
# VUE_APP_BASE_URL=http://cf-erp.com
VUE_APP_BASE_URL=https://erp.lookthere.cn
VUE_APP_BASE_URL_UPLOAD=http://cf-erp.com/upload/
VUE_APP_API_PROXY_PREFIX=/api
VUE_APP_API_NODE=development

View File

@ -10,9 +10,13 @@
"dependencies": {
"axios": "^0.21.1",
"core-js": "^3.6.5",
"echarts": "^5.5.0",
"element-china-area-data": "^6.1.0",
"element-ui": "^2.15.6",
"lodash": "^4.17.21",
"luxon": "^3.4.4",
"nprogress": "^0.2.0",
"qs": "^6.12.1",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vue-socket.io": "^3.0.10",

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,490 @@
*,
*:before,
*:after {
box-sizing: border-box;
}
::-webkit-scrollbar{
width:7px;
height:7px;
}
::-webkit-scrollbar-track-piece{
background:#EFEFEF;
}
::-webkit-scrollbar-thumb{
background: #C7D1DA;
border-radius: 3.5px;
}
html,
body,
ul,
li,
ol,
dl,
dd,
dt,
p,
h1,
h2,
h3,
h4,
h5,
h6,
form,
img,
textarea,
::before,
::after {
margin: 0;
padding: 0;
-webkit-tap-highlight-color: transparent;
-webkit-box-sizing: border-box;
}
body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px;
line-height: 1.15;
color: #303133;
background-color: #fff;
}
.page{
height: 100%;
width: 100%;
}
a {
color: mix(#fff, $--color-primary, 20%);
text-decoration: none;
&:focus,
&:hover {
color: $--color-primary;
text-decoration: underline;
}
}
img {
border: none;
vertical-align: middle;
max-width: 100%;
}
ul,
ol,
li {
list-style: none;
}
input,
textarea {
border: none;
resize: none;
outline: none;
-webkit-appearance: none;
background: transparent;
}
input::-webkit-input-placeholder {
color: #bbb;
}
button {
border: none;
outline: none;
background: transparent;
padding: 0;
}
cite,
i,
em,
s {
font-style: normal;
text-decoration: none;
}
/* Utils
------------------------------ */
.clearfix:before,
.clearfix:after {
content: " ";
display: table;
}
.clearfix:after {
clear: both;
}
/* Animation
------------------------------ */
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
/* Reset element-ui
------------------------------ */
.site-wrapper {
.el-pagination {
margin-top: 15px;
text-align: right;
}
}
/* Layout
------------------------------ */
.site-wrapper {
position: relative;
min-width: 1180px;
}
/* Sidebar fold
------------------------------ */
.site-sidebar--fold {
.site-navbar__header,
.site-navbar__brand,
.site-sidebar,
.site-sidebar__inner,
.el-menu.site-sidebar__menu {
width: 64px;
}
.site-navbar__body,
.site-content__wrapper {
margin-left: 64px;
}
.site-navbar__brand {
&-lg {
display: none;
}
&-mini {
display: inline-block;
}
}
.site-sidebar,
.site-sidebar__inner {
overflow: initial;
}
.site-sidebar__menu-icon {
margin-right: 0;
font-size: 20px;
}
.site-content--tabs>.el-tabs>.el-tabs__header {
left: 64px;
}
}
// animation
.site-navbar__header,
.site-navbar__brand,
.site-navbar__body,
.site-sidebar,
.site-sidebar__inner,
.site-sidebar__menu.el-menu,
.site-sidebar__menu-icon,
.site-content__wrapper,
.site-content--tabs>.el-tabs .el-tabs__header {
transition: inline-block .3s, left .3s, width .3s, margin-left .3s, font-size .3s;
}
/* Navbar
------------------------------ */
.site-navbar {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 1030;
height: 50px;
box-shadow: 0 2px 4px rgba(0, 0, 0, .08);
background-color: $navbar--background-color;
&--inverse {
.site-navbar__body {
background-color: transparent;
}
.el-menu {
>.el-menu-item,
>.el-submenu>.el-submenu__title {
color: #fff;
&:focus,
&:hover {
color: #fff;
background-color: mix(#000, $navbar--background-color, 15%);
}
}
>.el-menu-item.is-active,
>.el-submenu.is-active>.el-submenu__title {
border-bottom-color: mix(#fff, $navbar--background-color, 85%);
}
.el-menu-item i,
.el-submenu__title i,
.el-dropdown {
color: #fff;
}
}
.el-menu--popup-bottom-start {
background-color: $navbar--background-color;
}
}
&__header {
position: relative;
float: left;
width: 230px;
height: 50px;
overflow: hidden;
}
&__brand {
display: table-cell;
vertical-align: middle;
width: 230px;
height: 50px;
margin: 0;
line-height: 50px;
font-size: 20px;
text-align: center;
text-transform: uppercase;
white-space: nowrap;
color: #fff;
&-lg,
&-mini {
margin: 0 5px;
color: #fff;
&:focus,
&:hover {
color: #fff;
text-decoration: none;
}
}
&-mini {
display: none;
}
}
&__switch {
font-size: 18px;
border-bottom: none !important;
}
&__avatar {
border-bottom: none !important;
* {
vertical-align: inherit;
}
.el-dropdown-link {
>img {
width: 36px;
height: auto;
margin-right: 5px;
border-radius: 100%;
vertical-align: middle;
}
}
}
&__body {
position: relative;
margin-left: 230px;
padding-right: 15px;
background-color: #fff;
}
&__menu {
float: left;
background-color: transparent;
border-bottom: 0;
&--right {
float: right;
}
a:focus,
a:hover {
text-decoration: none;
}
.el-menu-item,
.el-submenu>.el-submenu__title {
height: 50px;
line-height: 50px;
}
.el-submenu>.el-menu {
top: 55px;
}
.el-badge {
display: inline;
z-index: 2;
&__content {
line-height: 16px;
}
}
}
}
/* Sidebar
------------------------------ */
.site-sidebar {
position: fixed;
top: 50px;
left: 0;
bottom: 0;
z-index: 1020;
width: 230px;
overflow: hidden;
&--dark,
&--dark-popper {
background-color: $sidebar--background-color-dark;
.site-sidebar__menu.el-menu,
>.el-menu--popup {
background-color: $sidebar--background-color-dark;
.el-menu-item,
.el-submenu>.el-submenu__title {
color: $sidebar--color-text-dark;
&:focus,
&:hover {
color: mix(#fff, $sidebar--color-text-dark, 50%);
background-color: mix(#fff, $sidebar--background-color-dark, 2.5%);
}
}
.el-menu,
.el-submenu.is-opened {
background-color: mix(#000, $sidebar--background-color-dark, 15%);
}
.el-menu-item.is-active,
.el-submenu.is-active>.el-submenu__title {
color: mix(#fff, $sidebar--color-text-dark, 80%);
}
}
}
&__inner {
position: relative;
z-index: 1;
width: 250px;
height: 100%;
padding-bottom: 15px;
overflow-y: scroll;
}
&__menu.el-menu {
width: 230px;
border-right: 0;
}
&__menu-icon {
width: 24px;
margin-right: 5px;
text-align: center;
font-size: 16px;
color: inherit !important;
}
}
/* Content
------------------------------ */
.site-content {
position: relative;
padding: 15px;
&__wrapper {
position: relative;
padding-top: 50px;
margin-left: 230px;
min-height: 100%;
background: $content--background-color;
}
&--tabs {
padding: 55px 0 0;
}
>.el-tabs {
>.el-tabs__header {
position: fixed;
top: 50px;
left: 230px;
right: 0;
z-index: 930;
padding: 0 55px 0 15px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .12), 0 0 6px 0 rgba(0, 0, 0, .04);
background-color: #fff;
>.el-tabs__nav-wrap {
margin-bottom: 0;
&:after {
display: none;
}
}
}
>.el-tabs__content {
padding: 0 15px 15px;
>.site-tabs__tools {
position: fixed;
top: 50px;
right: 0;
z-index: 931;
height: 40px;
padding: 0 12px;
font-size: 16px;
line-height: 40px;
background-color: $content--background-color;
cursor: pointer;
.el-icon--right {
margin-left: 0;
}
}
}
}
}
.el-table__expand-icon {
display: inline-block;
width: 14px;
vertical-align: middle;
margin-right: 5px;
}

View File

@ -0,0 +1,117 @@
.flex{
display: flex;
}
.page{
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.flex-cc{
height: 100vh;
width: 100%;
display: flex;
flex: 1;
flex-direction: column;
align-items:center;
justify-content:space-around;
}
.flex-cc-t{
height: 100vh;
width: 100%;
display: flex;
flex: 1;
flex-direction: column;
align-items:center;
}
.flex-col-center {
display: flex;
flex-direction: column;
align-items:center;
margin:10px 0px;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-row-wrap {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.column{
flex-direction: column !important;
}
.flex_a{
display: flex;
flex-direction: row;
align-items: center;
}
.flex_j{
display: flex;
justify-content: center;
}
.flex_aj{
@extend .flex_a;
justify-content: center;
}
.flex_column{
display: flex;
flex-direction: column;
}
.flex-col{
display: flex;
flex-direction: column;
}
.flex_sb{
@extend .flex_a;
justify-content: space-between;
}
.sb{
justify-content: space-between;
}
.jce{
justify-content: flex-end;
}
.flex_sa{
display: flex;
justify-content: space-around;
}
@for $i from 1 through 5 {
.flex#{$i}{
flex: $i;
}
}
.flexWrap {
flex-wrap: wrap;
}
.font-red {
color: #e54d42 !important;
}
.font-orange {
color: #f37b1d !important;
}
.font-yellow {
color: #fbbd08 !important;
}
.font-olive {
color: #8dc63f !important;
}
.font-green {
color: #39b54a !important;
}

View File

@ -0,0 +1,13 @@
@for $i from 12 through 100 {
.ft#{$i}{
font-size: $i *1px;
}
}
.ft0{
font-size:0;
}
.fw{
font-weight: bold;
}

View File

@ -0,0 +1,447 @@
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/
html {
line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
*/
body {
margin: 0;
}
/**
* Add the correct display in IE 9-.
*/
article,
aside,
footer,
header,
nav,
section {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* Add the correct display in IE 9-.
* 1. Add the correct display in IE.
*/
figcaption,
figure,
main { /* 1 */
display: block;
}
/**
* Add the correct margin in IE 8.
*/
figure {
margin: 1em 40px;
}
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* 1. Remove the gray background on active links in IE 10.
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/
a {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
}
/**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/
b,
strong {
font-weight: inherit;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font style in Android 4.3-.
*/
dfn {
font-style: italic;
}
/**
* Add the correct background and color in IE 9-.
*/
mark {
background-color: #ff0;
color: #000;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
audio,
video {
display: inline-block;
}
/**
* Add the correct display in iOS 4-7.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Remove the border on images inside links in IE 10-.
*/
img {
border-style: none;
}
/**
* Hide the overflow in IE.
*/
svg:not(:root) {
overflow: hidden;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers (opinionated).
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: sans-serif; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
button,
html [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; /* 2 */
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* 1. Add the correct display in IE 9-.
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Remove the default vertical scrollbar in IE.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in IE 9-.
* 1. Add the correct display in Edge, IE, and Firefox.
*/
details, /* 1 */
menu {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Scripting
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
canvas {
display: inline-block;
}
/**
* Add the correct display in IE.
*/
template {
display: none;
}
/* Hidden
========================================================================== */
/**
* Add the correct display in IE 10-.
*/
[hidden] {
display: none;
}

View File

@ -0,0 +1,115 @@
.mt1 {
margin-top: 1px;
}
.mt24 {
margin-top: 24px;
}
.mr24 {
margin-right: 24px;
}
.ml24 {
margin-left: 24px;
}
.pt24 {
padding-top: 24px;
}
.pr24 {
padding-right: 24px;
}
.pb24 {
padding-bottom: 24px;
}
.pl24 {
padding-left: 24px;
}
.pt88 {
padding-top: 88px;
}
.mlr24 {
margin-left: 24px;
margin-right: 24px;
}
.ptb24 {
padding-top: 24px;
padding-bottom: 24px;
}
.plr24 {
padding-left: 24px;
padding-right: 24px;
}
.p20 {
padding: 20upx;
}
.p24 {
padding: 24upx;
}
.p30 {
padding: 30upx;
}
.p40 {
padding: 40upx;
}
@for $i from 1 through 30 {
.mt#{$i*10}{
margin-top: $i * 10px;
}
.pt#{$i*10}{
padding-top: $i * 10px;
}
.pb#{$i*10}{
padding-bottom: $i * 10px;
}
}
@for $i from 1 through 20 {
.pl#{$i*10}{
padding-left: $i * 10px;
}
.pr#{$i*10}{
padding-right: $i * 10px;
}
.mb#{$i*10}{
margin-bottom: $i * 10px;
}
.ml#{$i*10}{
margin-left: $i * 10px;
}
.mr#{$i*10}{
margin-right: $i * 10px;
}
.mlr#{$i*10}{
margin-left: $i * 10px;
margin-right: $i * 10px;
}
.mtb#{$i*10}{
margin-top: $i * 10px;
margin-bottom: $i * 10px;
}
.ptb#{$i*10}{
padding-top: $i * 10px;
padding-bottom: $i * 10px;
}
.plr#{$i*10}{
padding-left: $i * 10px;
padding-right: $i * 10px;
}
}

View File

@ -0,0 +1,13 @@
// 站点主色
// tips: 要达到整站主题修改效果, 请确保[$--color-primary]站点主色与[/src/element-ui-theme/index.js]文件中[import './element-[#17B3A3]/index.css']当前主题色一致
$--color-primary: #3E8EF7;
// Navbar
$navbar--background-color: $--color-primary;
// Sidebar
$sidebar--background-color-dark: #263238;
$sidebar--color-text-dark: #8a979e;
// Content
$content--background-color: #f1f4f5;

View File

@ -0,0 +1,9 @@
@import "normalize"; // api: https://github.com/necolas/normalize.css/
@import "variables"; // 站点变量
@import "base";
@import "space";
@import "font";
@import "flex";

View File

@ -0,0 +1,60 @@
<!-- 搜索表单 -->
<template>
<div>
<el-card>
<div class="flex-row">
<template v-for="(top,index) in boardData">
<div class="flex_column flex_aj flex1" style="min-width:150px;padding:10px;" >
<span class="ft20 fw">{{top.label}}</span>
<span class="ft18" style="margin-top: 5px;">{{top.value}}</span>
</div>
<div :class="getStyle(boardData,index)"></div>
</template>
</div>
</el-card>
</div>
</template>
<script>
export default {
name: "Board",
props: {
boardData: {
type: Array,
default: () => [],
},
},
data() {
return {
loading: false,
};
},
computed: {
getStyle(){
return (lst,index)=>{
let len= Object.keys(lst).length;
if (len>0) {
if (len==index+1) {
return ''
}
return 'divi';
}
}
}
},
methods: {
},
};
</script>
<style scoped>
.divi{
display: inline-block;
width: 1px;
margin: 0 8px;
vertical-align: middle;
position: relative;
background-color:#DCDFE6;
}
</style>

View File

@ -0,0 +1,575 @@
<!-- 搜索表单 -->
<template>
<div class="ces-form">
<el-dialog
:title="dialogFormTitle"
:visible.sync="dialogFormVisible"
@close="reset()"
:close-on-click-modal="false"
:width="dialogWidth"
v-if="dialogFormVisible"
>
<el-form
:size="size"
:inline="inline"
:label-width="labelWidth"
:rules="formRules"
:model="formData"
ref="ruleForm"
>
<el-form-item
v-for="item in formCols"
:label="item.label"
:key="item.label"
:prop="item.prop"
>
<!-- 输入框 -->
<el-input
v-if="item.type==='input'"
:type="item.mold||'text'"
v-model="formData[item.prop]"
:size="size || item.size"
:style="{width:(item.width?item.width+'px':'300px')}"
@change="item.change && item.change(formData[item.prop])"
:placeholder="item.placeholder || '请输入'+item.label"
:disabled="item.isDisabled && item.isDisabled(formData)"
></el-input>
<!-- 文本域 -->
<el-input
v-if="item.type==='textarea'"
type="textarea"
:rows="4"
:size="size || item.size"
:style="{width:(item.width?item.width+'px':'300px')}"
:disabled="item.isDisabled && item.isDisabled(formData)"
@change="item.change && item.change(formData[item.prop])"
v-model="formData[item.prop]"
></el-input>
<!-- 下拉框 -->
<el-select
v-if="item.type==='select'"
v-model="formData[item.prop]"
filterable
:multiple="item.multiple"
:size="size || item.size"
:style="{width:(item.width?item.width+'px':'300px')}"
@change="item.change && item.change(formData[item.prop])"
@visible-change="item.focus && item.focus($event,formData[item.prop])"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
>
<el-option
v-for="op in item.options"
:label="op.label"
:value="op.value"
:key="op.value"
></el-option>
</el-select>
<!-- 级联省市区 -->
<!-- <el-cascader
v-if="item.type==='cascader'"
:style="{width:item.width+'px'}"
:placeholder="item.placeholder || '请选择'"
:options="cascaderOptions"
v-model="formData[item.prop]"
@focus="item.focus && item.focus(formData[item.prop])"
filterable
clearable
:show-all-levels="item.showAll && item.showAll(formData[item.prop])"
:props="{ expandTrigger: 'hover',checkStrictly: item.checkStrictly && item.checkStrictly(formData[item.prop]) }"
@change="item.change && item.change(formData[item.prop])"
></el-cascader> -->
<el-cascader
v-if="item.type==='cascader'"
:style="{width:(item.width?item.width+'px':'300px')}"
:placeholder="item.placeholder || `请选择${item.label}` "
:options="item.options?item.options:options"
v-model="formData[item.prop]"
@focus="item.focus && item.focus(formData[item.prop])"
filterable
clearable
:show-all-levels="item.showAll && item.showAll(formData[item.prop])"
:props="{ expandTrigger: 'hover',checkStrictly: item.checkStrictly && item.checkStrictly(formData[item.prop]) }"
@change="item.change && item.change(formData[item.prop])"
></el-cascader>
<!-- 单选 -->
<el-radio-group
v-if="item.type==='radio'"
v-model="formData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
>
<el-radio v-for="rav in item.radios" :label="rav.value" :key="rav.value">{{rav.label}}</el-radio>
</el-radio-group>
<!-- 单选按钮 -->
<el-radio-group
v-if="item.type==='radioButton'"
v-model="formData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
>
<el-radio-button v-for="ra in item.radios" :label="ra.value" :key="ra.value">{{ra.label}}</el-radio-button>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-if="item.type==='checkbox'"
v-model="formData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
>
<el-checkbox v-for="ch in item.checkboxs" :label="ch.value" :key="ch.value">{{ch.label}}</el-checkbox>
</el-checkbox-group>
<!-- 日期 -->
<el-date-picker
v-if="item.type==='date'"
v-model="formData[item.prop]"
:style="{width:item.width+'px'}"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
:picker-options="expireTimeOption"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
></el-date-picker>
<!-- 开始-结束日期 -->
<el-date-picker
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
v-if="item.type==='daterange'"
type="daterange"
v-model="formData[item.prop]"
:style="{width:item.width+'px'}"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="expireTimeOption"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
></el-date-picker>
<!-- 时间 -->
<el-time-select
v-if="item.type==='time'"
v-model="formData[item.prop]"
format="HH:mm:ss"
value-format="HH:mm:ss"
:style="{width:item.width+'px'}"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
></el-time-select>
<!-- 开始-结束时间 -->
<el-time-picker
v-if="item.type==='timePicker'"
v-model="formData[item.prop]"
is-range
format="HH:mm"
value-format="HH:mm"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择时间范围"
:style="{width:item.width+'px'}"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
:picker-options="{
selectableRange:['00:00:00 - 23:59:59']
}"
></el-time-picker>
<!-- 日期时间 -->
<el-date-picker
v-if="item.type==='dateTime'"
type="datetime"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
:style="{width:item.width+'px'}"
v-model="formData[item.prop]"
@change="item.change && item.change(formData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
></el-date-picker>
<!-- 计数器 -->
<el-input-number
v-if="item.type==='number'"
v-model="formData[item.prop]"
controls-position="right"
:style="{width:item.width+'px'}"
@change="item.change && item.change(formData[item.prop])"
:min="1"
/>
<!-- 滑块 -->
<!-- <el-slider v-if="item.type==='Slider'" v-model="formData[item.prop]"></el-slider> -->
<!-- 开关 -->
<el-switch
v-if="item.type==='switch'"
v-model="formData[item.prop]"
:size="size || item.size"
:active-value="item.values&&item.values[0]"
:inactive-value="item.values&&item.values[1]"
@change="item.change && item.change(formData[item.prop])"
:style="{width:item.width+'px'}"
:disabled="item.isDisabled && item.isDisabled(formData[item.prop])"
></el-switch>
<!-- 树形菜单弹出框 -->
<el-popover
v-if="item.type==='popover'"
placement="bottom-start"
width="200"
ref="deptListPopover"
trigger="click">
<el-tree
:data="item.options"
accordion
node-key="id"
ref="popoverTree"
:props="defaultProps"
:highlight-current="true"
:expand-on-click-node="false"
@node-click="item.handleNodeClick"/>
<el-input
slot="reference"
v-model="formData[item.prop]"
:readonly="true"
:style="{width:item.width+'px'}"
:placeholder="item.placeholder || '请选择'"
></el-input>
</el-popover>
<!-- 树形控件 -->
<el-tree
v-if="item.type==='tree'"
:data="item.options"
show-checkbox
node-key="id"
ref="tree"
:props="defaultProps"
@check-change="item.check"/>
<!-- 图片上传 -->
<section v-if="item.type==='upload'" :style="{width:item.width+'px'}">
<el-upload
v-loading="loading"
class="avatar-uploader"
:action="upload_url+'/common/upload'"
:show-file-list="false"
:on-success="item.success"
:on-error="handleAvatarError"
:on-progress="handleAvatarProgress"
:before-upload="beforeAvatarUpload"
name="file"
>
<img v-if="formData[item.prop]" :src="formData[item.prop]|formatImg" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</section>
<!-- 多图片上传 -->
<section v-if="item.type==='uploadList'" :style="{width:item.width+'px'}">
<el-upload
v-loading="loading"
:action="upload_url+'/common/upload'"
list-type="picture-card"
:multiple="true"
:limit="9"
:fileList="fileList"
:on-success="handleAvatarSuccess"
:on-error="handleAvatarError"
:on-progress="handleAvatarProgress"
:before-upload="beforeAvatarUpload"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:on-preview="handlePictureCardPreview"
:headers="{'Authorization':$cookie.get('token')}"
name="file"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible" append-to-body title="预览">
<img width="100%" :src="dialogImageUrl" alt />
</el-dialog>
</section>
<!-- 地图 -->
<div v-if="item.type==='map'" id="container" :style="{width:(item.width||'900px'),height:(item.height||'300px')}"></div>
<MapSearch v-if="item.type==='mapSearch'" @getLocation="item.address":style="{width:(item.width||'900px'),height:(item.height||'500px'),border: 'solid 1px #efefef'}"></MapSearch>
<!-- 默认 -->
<span
v-if="!item.type"
:style="{width:item.width+'px','display': 'inline-block'}"
:size="size"
:class="item.itemClass && item.itemClass(formData[item.prop])"
>{{(item.formatter && item.formatter(formData[item.prop])) || formData[item.prop]}}</span>
<!-- 图片显示 -->
<el-image
v-if="item.type==='img'"
:src="formData[item.prop]|formatImg"
:style="{width:item.width+'px',height:item.width+'px'}"
></el-image>
<!-- 富文本 -->
<r-tinymce
v-if="item.type==='tinymce'"
v-model="formData[item.prop]"
:height="300"
:width="600"
></r-tinymce>
</el-form-item>
<slot></slot>
</el-form>
<el-form
inline
v-if="isHandle"
:style="btnStyle || 'justify-content: flex-end;display: flex;margin-top:20px;'"
>
<el-form-item v-for="item in formHandle" :key="item.label" style="margin-bottom: 0;">
<el-button
:type="item.type"
:size="item.size || size"
:icon="item.icon"
:disabled="item.disabled"
@click="item.handle"
>{{item.label}}</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import area from "@/util/area2";
import MapSearch from "../../local";
import * as plugin from "@/util/Tmap";
import { regionData, CodeToText } from "element-china-area-data";
export default {
props: {
isHandle: {
type: Boolean,
default: true,
},
labelWidth: {
type: String,
default: "100px",
},
dialogWidth: {
type: String,
default: "1000px",
},
inline: {
type: Boolean,
default: true,
},
// mini,medium,small
size: {
type: String,
default: "small",
},
formCols: {
type: Array,
default: () => [],
},
formHandle: {
type: Array,
default: () => [],
},
formData: {
type: Object,
default: () => {},
},
formRules: {
type: Object,
default: () => {},
},
btnStyle: {
type: String,
default: "",
},
fileList: {
type: Array,
default: () => [],
},
defaultProps: {
type: Object,
default: () => ({children: "sysMenuList",label: "interfaceName"}),
},
destroy: {
type: Boolean,
default: true,
},
},
name: "Form",
data() {
return {
options:regionData,
form:{test:'2'},
numbers:0,
dialogFormVisible: false,
dialogFormTitle: "新增",
loading: false,
imageUrl: "",
upload_url: process.env.NODE_ENV !== 'production'?'/api':process.env.VUE_APP_URL,
imgurl: process.env.VUE_APP_IMG,
cascaderOptions: area,
map: null,
expireTimeOption:{
disabledDate(date) {
return date.getTime() < Date.now() - 8.64e7;
}
},
dialogVisible: false,
dialogImageUrl:''
};
},
computed:{
getType() {
return (type)=>{
// console.log('select',typeof type);
let istype=/^\d+$/.test(type)
if (istype) {
console.log('select类型',typeof type.toString());
return type.toString();
}
// console.log('select',typeof type);
return type
}
}
},
components:{MapSearch},
created() {
console.log('created表单form');
},
methods: {
async plugin(point={lat:'',lng:''}) {
var qq = await plugin.Tmap();
if(point.lat==''|| point.lat==0){
point = await this.getLocation()
}
this.map = await plugin.center(point.lat, point.lng);
qq.maps.event.addListener(this.map.marker, "dragend", (e) => {
this.map.map.setCenter(e.latLng);
this.$emit("position", {
lat: e.latLng.lat,
lng: e.latLng.lng,
});
});
},
getLocation() {//api
return new Promise((resolve, reject) => {
this.$jsonp("https://apis.map.qq.com/ws/location/v1/ip", {
key: "N6RBZ-AJN35-AACI2-Q2ICF-HYV6O-JRBBZ",
output: "jsonp"
}).then((res) => {
resolve({
lat: res.result.location.lat,
lng: res.result.location.lng,
});
});
});
},
handleAvatarSuccess(res, file) {
this.loading = false;
this.imageUrl = URL.createObjectURL(file.raw);
if(res.code==200){
this.$message({
message: "上传成功",
type: "success",
});
this.$emit("imgs", res.data.value);
}else{
this.$message({
message: res.msg,
type: "error",
});
}
},
reset() {
this.$refs.ruleForm.resetFields()
this.$parent.$data.formData = this.$parent.$options.data.call(this).formData;
if(this.$parent.$data.formTable){
this.$parent.$data.formTable.data = this.$parent.$options.data.call(this).formTable.data
}
},
handleAvatarError() {
this.loading = false;
this.$message.error("上传失败");
},
beforeAvatarUpload(file) {
const isJPG = file.type === "image/jpeg" || "image/png";
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isJPG) {
this.$message.error("上传格式只能是图片格式!");
}
if (!isLt5M) {
this.$message.error("上传头像图片大小不能超过 5MB!");
}
return isJPG && isLt5M;
},
handleAvatarProgress(res, file) {
this.loading = true;
},
handleRemove(file, fileList) {
let list = fileList.map((item) => {
console.log(item)
if(item.url.indexOf('http')!=-1){
return item.url.split(process.env.VUE_APP_IMG)[1]
}else{
return item.url
}
});
this.$emit("imgs", {
type: "pictureCard",
url: list,
});
},
handleExceed() {
this.$message({
message: "最多上传9张图片",
type: "warning",
});
},
handlePictureCardPreview(file){
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
},
};
</script>
<style scoped lang="scss">
.avatar-uploader,
.el-upload {
border: 1px dashed #ccc;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 150px;
height: 150px;
display: block;
}
.avatar-uploader:hover,
.el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 150px;
height: 150px;
line-height: 150px;
text-align: center;
}
.avatar {
width: 150px;
height: 150px;
display: block;
}
</style>

View File

@ -0,0 +1,543 @@
<!-- 搜索表单 -->
<template>
<div class="ces-search">
<el-form :size="size" :inline="inline" :label-position="labelPosition" :label-width="labelWidth" ref="ruleForm" :rules="formRule" :model="itemData">
<el-form-item v-for="item in formItem" :label="item.label" :key="item.label" :prop="item.prop" :label-width="item.labelWidth||labelWidth">
<!-- 输入框 -->
<el-input
v-if="item.type==='input'"
:type="item.mold||'text'"
v-model="itemData[item.prop]"
:size="size || item.size"
:style="{width:(item.width?item.width+'px':'300px')}"
@change="item.change && item.change(itemData[item.prop])"
:placeholder="`请输入${item.label}`"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
@blur="item.mold==='number'?BlurText($event):''"
>
<template v-if="item.slot" :slot="item.slot.direction||'append'">{{item.slot.value}}</template>
</el-input>
<!-- 文本域 -->
<el-input
v-if="item.type==='textarea'"
type="textarea"
:rows="4"
:size="size || item.size"
:style="{width:item.width+'px'}"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
@change="item.change && item.change(itemData[item.prop])"
v-model="itemData[item.prop]"
></el-input>
<!-- 下拉框 -->
<el-select
v-if="item.type==='select'"
v-model="itemData[item.prop]"
filterable
:size="size || item.size"
:style="{width:(item.width?item.width+'px':'300px')}"
:multiple="item.multiple"
:placeholder="`请选择${item.placeholder||item.label}`"
@change="item.change && item.change(itemData[item.prop])"
@visible-change="item.focus && item.focus($event,itemData[item.prop])"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
>
<el-option v-for="op in item.options" :label="op.label" :value="op.value" :key="op.value"></el-option>
</el-select>
<!-- 级联省市区 -->
<el-cascader
v-if="item.type==='cascader'"
:style="{width:(item.width?item.width+'px':'300px')}"
:placeholder="item.placeholder || `请选择${item.label}` "
:options="options"
v-model="itemData[item.prop]"
@focus="item.focus && item.focus(itemData[item.prop])"
filterable
clearable
:show-all-levels="item.showAll && item.showAll(itemData[item.prop])"
:props="{ expandTrigger: 'hover',checkStrictly: item.checkStrictly && item.checkStrictly(itemData[item.prop]) }"
@change="item.change && item.change(itemData[item.prop])"
></el-cascader>
<!-- 单选 -->
<el-radio-group
v-if="item.type==='radio'"
v-model="itemData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(itemData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
>
<el-radio v-for="ra in item.radios" :label="ra.value" :key="ra.value">{{ra.label}}</el-radio>
</el-radio-group>
<!-- 单选按钮 -->
<el-radio-group
v-if="item.type==='radioButton'"
v-model="itemData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(itemData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
>
<el-radio-button v-for="ra in item.radios" :label="ra.value" :key="ra.value">{{ra.label}}</el-radio-button>
</el-radio-group>
<!-- 复选框 -->
<!-- <el-checkbox-group
v-if="item.type==='checkbox'"
v-model="itemData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(itemData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
>
<el-checkbox v-for="ch in item.checkboxs" :label="ch.value" :key="ch.value">{{ch.label}}</el-checkbox>
</el-checkbox-group> -->
<el-checkbox-group v-model="itemData[item.prop]" v-if="item.type==='checkbox'">
<el-checkbox v-for="ch in item.checkboxs" :label="ch.label" :key="ch.value" :name="item.prop"></el-checkbox>
</el-checkbox-group>
<!-- 树形组件 -->
<el-tree
v-if="item.type==='tree'"
:data="item.treeArr"
show-checkbox
:node-key="treeKey"
:default-expanded-keys="treeExpandArr"
:default-checked-keys="treeCheckArr"
:props="defaultProps"
@check-change="handleCheckChange"
@check="getChecked"
>
</el-tree>
<!-- 日期 -->
<el-date-picker
v-if="item.type==='date'"
v-model="itemData[item.prop]"
:style="{width:item.width+'px'}"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
:placeholder="`请选择${item.placeholder||''}`"
@change="item.change && item.change(itemData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
></el-date-picker>
<!-- 开始-结束日期 -->
<el-date-picker
v-if="item.type==='daterange'"
type="daterange"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
v-model="itemData[item.prop]"
:style="{width:item.width+'px'}"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="item.change && item.change(itemData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
></el-date-picker>
<!-- 时间 -->
<el-time-select
v-if="item.type==='time'"
v-model="itemData[item.prop]"
format="HH:mm:ss"
value-format="HH:mm:ss"
:picker-options="item.options||{
start: '00:00',
step: '00:30',
end: '23:30'
}"
:style="{width:item.width+'px'}"
@change="item.change && item.change(itemData[item.prop])"
:size="size || item.size"
:placeholder="`请选择${item.placeholder||''}`"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
></el-time-select>
<!-- 日期时间 -->
<el-date-picker
v-if="item.type==='dateTime'"
:type="item.timeType||'datetime'"
:style="{width:item.width+'px'}"
v-model="itemData[item.prop]"
@change="item.change && item.change(itemData[item.prop])"
:size="size || item.size"
:placeholder="`请选择${item.placeholder||''}`"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
></el-date-picker>
<!-- 滑块 -->
<!-- <el-slider v-if="item.type==='Slider'" v-model="itemData[item.prop]"></el-slider> -->
<!-- 开关 -->
<el-switch
v-if="item.type==='switch'"
v-model="itemData[item.prop]"
:size="size || item.size"
:active-value="item.values&&item.values[0]"
:inactive-value="item.values&&item.values[1]"
@change="item.change && item.change(itemData[item.prop])"
:style="{width:item.width+'px'}"
:disabled="item.isDisabled && item.isDisabled(itemData[item.prop])"
></el-switch>
<!-- 图片上传 -->
<section v-if="item.type==='upload'" :style="{width:item.width+'px'}">
<el-upload
v-loading="loading"
class="avatar-uploader"
:action="upload_url+'/common/upload'"
:show-file-list="false"
:on-success="item.success"
:on-error="handleAvatarError"
:on-progress="handleAvatarProgress"
:before-upload="beforeAvatarUpload"
name="file"
>
<img v-if="itemData[item.prop]" :src="itemData[item.prop]|formatImg" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</section>
<!-- 文件上传 -->
<section v-if="item.type==='uploadfill'" :style="{width:item.width+'px'}">
<el-upload
v-loading="loading"
class="avatar-uploader"
:action="file_url+'common//uploadNotRename'"
:show-file-list="false"
:on-success="item.success"
:on-error="handleAvatarError"
:on-progress="handleAvatarProgress"
name="file"
>
<img v-if="itemData[item.prop]" :src="itemData[item.prop]|formatImg" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</section>
<!-- 多图片上传 -->
<section v-if="item.type==='uploadList'" :style="{width:item.width+'px'}">
<el-upload
ref="pictureCard"
v-loading="loading"
:action="upload_url+'/common/upload'"
list-type="picture-card"
:multiple="true"
:limit="item.limit||9"
:fileList="fileList"
:on-success="handleAvatarSuccess"
:on-error="handleAvatarError"
:on-progress="handleAvatarProgress"
:before-upload="beforeAvatarUpload"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:on-preview="handlePictureCardPreview"
:headers="{'Authorization':$cookie.get('token')}"
name="file"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible" title="预览" width="600px">
<img width="100%" :src="dialogImageUrl" alt />
</el-dialog>
</section>
<!-- 富文本 -->
<r-tinymce
v-if="item.type==='tinymce'"
v-model="itemData[item.prop]"
:height="300"
:width="600"
></r-tinymce>
</el-form-item>
<el-form-item :style="btnRight?`float:right`:''">
<el-button
v-for="(btnl,btni) in btnList" :key="btni"
:type="btnl.type"
:size="btnl.size || size"
:icon="btnl.icon"
:disabled="btnl.disabled"
:style="{display:btnl.visible}"
@click="getHandle(btnl.handle,btnl.deed)"
>{{btnl.label}}</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { regionData, CodeToText } from "element-china-area-data";
export default {
name: "FormItem",
props: {
labelPosition:{
type: String,
default: "none"
},
isState:{
type: Boolean,
default: true,
},
btnRight:{
type: Boolean,
default: false,
},
isHandle: {
type: Boolean,
default: true,
},
inline: {
type: Boolean,
default: false,
},
labelWidth: {
type: String,
default: "100px",
},
// mini,medium,small
size: {
type: String,
default: "medium",
},
btnList: {
type: Array,
default: () => [],
},
formItem: {
type: Array,
default: () => [],
},
itemData: {
type: Object,
default: () => {},
},
formRule: {
type: Object,
default: () => {},
},
fileList: {
type: Array,
default: () => [],
},
treeArr: {
type: Array,
default: () => [],
},
treeExpandArr: {
type: Array,
default: () => [],
},
treeCheckArr: {
type: Array,
default: () => [],
},
treeKey:{
type: String,
default: "id"
},
defaultProps: {
children: 'children',
label: 'label'
},
},
data() {
return {
// treeExpandArr:[],
// treeCheckArr:[],
options:regionData,
loading: false,
imageUrl: "",
upload_url: process.env.NODE_ENV,
imgurl: process.env.VUE_APP_IMG,
dialogVisible: false,
dialogImageUrl:'',
isMouse:false,
isLoading:false,
good:[],
file_url:''
};
},
created(){
this.file_url=process.env.VUE_APP_IMG
},
methods: {
getChecked(checkedNodes,checkedKeys){
this.$parent.$emit('treeKeyArr',checkedKeys.checkedKeys);
},
handleCheckChange(data, checked) {
if (data.parent_id==0 && checked) {
data.chk=1
}else if (data.parent_id==0 && !checked) {
data.chk=0
}else if (data.parent_id !==0) {
data.chk=checked?1:0
}
},
getHandle(func,deed){
if (deed=='reset') {
this.$refs.ruleForm.resetFields();
}else{
func.call(func)
}
},
mouseenter(icon){
if (icon==='el-icon-refresh') {
this.isMouse=true;
}
},
mouseleave(icon){
if (icon==='el-icon-refresh') {
this.isMouse=false;
}
},
BlurText(e) {
let boolean = new RegExp("^[1-9][0-9]*$").test(e.target.value)
if(!boolean) {
this.$message.warning('请输入正整数')
e.target.value = ' '
}
},
handleAvatarSuccess(res, file) {
this.loading = false;
this.imageUrl = URL.createObjectURL(file.raw);
this.$message({
message: "上传成功",
type: "success",
});
this.$emit("imgs", {
type: "text",
url: `${res.data}`,
});
},
handleUpload() {
this.$refs['excel-upload-input'][0].click()
},
handleClick(e){
const files = e.target.files
const rawFile = files[0] // only use files[0]
if (!rawFile) return
//this.upload(rawFile)
this.$emit("uploadEX", rawFile);
},
upload(rawFile) {
this.$refs['excel-upload-input'][0].value = null // fix can't select the same excel
const before = this.beforeUpload(rawFile)
if (before) {
let formData = new FormData();
formData.append('file',rawFile);
this.$http.upload('/file/upload',formData).then(res=>{
if(res.code == 200){
this.$message({
type: "success",
message: "上传成功",
});
}
})
}
},
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: '小于1兆',
type: 'warning'
})
return false
},
handleAvatarError() {
this.loading = false;
this.$message.error("上传失败");
},
beforeAvatarUpload(file) {
const isJPG = file.type === "image/jpeg" || "image/png";
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isJPG) {
this.$message.error("上传格式只能是图片格式!");
}
if (!isLt5M) {
this.$message.error("上传头像图片大小不能超过 5MB!");
}
return isJPG && isLt5M;
},
handleAvatarProgress(res, file) {
this.loading = true;
},
handleRemove(file, fileList) {
let list = fileList.map((item) => {
console.log(item)
if(item.response){
return item.response.data
}else{
if(item.url.indexOf('http')!=-1){
return item.url.split(process.env.VUE_APP_IMG)[1]
}else{
return item.url
}
}
});
this.$emit("imgs", {
type: "pictureCard",
url: list,
});
},
handleExceed() {
this.$message({
message: `最多上传${this.$refs.pictureCard[0].limit||9}张图片`,
type: "warning",
});
},
handlePictureCardPreview(file){
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
},
};
</script>
<style scoped lang="scss">
@keyframes circles{
0%{transform:rotateZ(0deg);}
100%{transform:rotateZ(360deg);}
}
.good{
animation-name:circles;
animation-duration:3s;
animation-iteration-count:1;
}
.avatar-uploader,
.el-upload {
border: 1px dashed #ccc;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 150px;
height: 150px;
display: block;
}
.avatar-uploader:hover,
.el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 150px;
height: 150px;
line-height: 150px;
text-align: center;
}
.avatar {
width: 150px;
height: 150px;
display: block;
}
.excel-upload-input{
display: none;
z-index: -9999;
}
</style>

View File

@ -0,0 +1,170 @@
<!-- 搜索表单 -->
<template>
<div class="ces-form">
<section class="header" v-if="Handle">
<el-button type="primary" size="mini" @click="addTable">新增</el-button>
<el-button type="danger" size="mini" @click="del">删除</el-button>
</section>
<el-form :rules="formTable.rules" :model="formTable" ref="ruleForm">
<el-table
:data="formTable.data"
border
@select="select"
@select-all="selectAll"
:header-cell-class-name="headRuleStyle"
:header-cell-style="{backgroundColor: '#f5f7fa',fontWeight: 'bold',color: '#909399'}">
<el-table-column v-if="isSelection" type="selection" align="center"></el-table-column>
<el-table-column
v-for="item in formTable.cols"
:size="size"
:key="item.id"
:prop="item.prop"
:label="item.label"
:width="item.width"
:align="item.align||'center'"
:fixed="item.fixed"
:show-overflow-tooltip="item.overflow">
<template slot-scope="scope">
<el-form-item :prop="`data.${scope.$index}.${[item.prop]}`" :rules="formTable.rules[item.prop]" >
<!-- 默认 -->
<span
v-if="!item.type"
:style="item.itemStyle && item.itemStyle(scope.row)"
:class="item.itemClass && item.item.itemClass(scope.row)"
>{{(item.formatter && item.formatter(scope.row)) || scope.row[item.prop] || '-'}}</span>
<!-- 输入框 -->
<el-input v-if="item.type==='input'" v-model="scope.row[item.prop]" :size="item.size || size"></el-input>
<!-- 下拉框 -->
<el-select
v-if="item.type==='select'"
v-model="scope.row[item.prop]"
:size="item.size || size"
filterable
:multiple="item.multiple"
:placeholder="item.placeholder || '请选择'"
@change="item.change && item.change(scope.row[item.prop])"
@focus="item.focus && item.focus(scope.row[item.prop])"
:disabled="item.isDisabled && item.isDisabled(scope.row[item.prop])"
@remove-tag="item.remove && item.remove(scope.row[item.prop])">
<el-option v-for="op in item.options" :label="op.label" :value="op.value" :key="op.value"></el-option>
</el-select>
<!-- 级联省市区 -->
<el-cascader
v-if="item.type==='cascader'"
:placeholder="item.placeholder || '请选择'"
:options="item.options || []"
v-model="scope.row[item.prop]"
:size="item.size || size"
@focus="item.focus && item.focus(scope.row[item.prop])"
filterable
clearable
:show-all-levels="item.showAll && item.showAll(scope.row[item.prop])"
:props="{ expandTrigger: 'hover',checkStrictly: item.checkStrictly && item.checkStrictly(scope.row[item.prop]),value:item.value?item.value:'vaule' }"
@change="item.change && item.change(scope.row[item.prop])"
ref="cascaderAddr">
</el-cascader>
<span v-if="item.type==='button'">
<el-button
v-for="(btn,idx) in item.btnList"
:key="idx"
:size="item.size || size"
:disabled="btn.disabled && btn.disabled(scope.row)"
:type="btn.type"
:icon="btn.icon"
@click="btn.handle(scope.row,scope.$index)"
>{{btn.label}}</el-button>
</span>
</el-form-item>
</template>
</el-table-column>
</el-table>
</el-form>
</div>
</template>
<script>
export default {
props: {
// mini,medium,small
size: {
type: String,
default: "mini",
},
formTable: {
type: Object,
default: () => {}
},
isSelection: {
type: Boolean,
default: true,
},
Handle: {
type: Boolean,
default: true,
},
addTable:Function
},
name: 'FormTable',
data() {
return {
checkBox:[]
};
},
created() {
},
methods: {
select(rows, row) {
this.$emit("select", rows, row);
this.checkBox = rows
},
//
selectAll(rows) {
this.$emit("select", rows);
this.checkBox = rows
},
del(){
if(this.checkBox&&this.checkBox.length>0){
let arr = this.formTable.data.filter(item => !this.checkBox.some(ele => ele === item))
this.checkBox = []
this.formTable.data = arr
}else{
this.$message({
message: '请先选择好需要删除的表格',
type: 'warning'
});
}
},
headRuleStyle(row, rowIndex){
if(row.column.property){
let idx = row.row.findIndex(item=>{
return item.property == Object.keys(this.formTable.rules)[0]
})
// console.log(row.column.property)
// console.log(Object.keys(this.formTable.rules)[row.columnIndex-idx])
if(Object.keys(this.formTable.rules)[row.columnIndex-idx] == row.column.property && row.column.property && Object.keys(this.formTable.rules).length!=1){
return 'rules';
}else if(Object.keys(this.formTable.rules).toString() == row.column.property){
return 'rules';
}
}
}
}
}
</script>
<style scoped>
.header{
background-color: #f5f7fa;
font-weight:bold ;
padding: 10px 10px;
border: 1px solid #EBEEF5;
border-bottom: none;
}
.el-form >>> .rules .cell:before{
content: '*';
color: #f00;
margin-right: 5px;
display: inline-block;
}
/* .el-form >>>.el-form-item {margin-bottom: 0;} */
</style>

View File

@ -0,0 +1,538 @@
<!-- 搜索表单 -->
<template>
<div class="ces-search">
<el-form :size="size" :inline="inline" :label-position="labelPosition" :label-width="labelWidth" ref="ruleForm" :rules="searchRules" :model="searchData">
<el-form-item v-for="item in searchForm" :label="item.label" :key="item.label" :prop="item.prop" :label-width="item.labelWidth||labelWidth">
<!-- 输入框 -->
<el-input
v-if="item.type==='input'"
:type="item.mold||'text'"
v-model="searchData[item.prop]"
:size="size || item.size"
:style="getInputStyle(item.width)"
@change="item.change && item.change(searchData[item.prop])"
:placeholder="`请输入${item.placeholder||item.label}`"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
@blur="item.mold==='number'?BlurText($event):''"
>
<template v-if="item.slot" :slot="item.slot.direction||'append'">{{item.slot.value}}</template>
</el-input>
<!-- 文本域 -->
<el-input
v-if="item.type==='textarea'"
type="textarea"
:rows="4"
:size="size || item.size"
:style="{width:item.width+'px'}"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
@change="item.change && item.change(searchData[item.prop])"
v-model="searchData[item.prop]"
></el-input>
<!-- 下拉框 -->
<el-select
v-if="item.type==='select'"
v-model="searchData[item.prop]"
filterable
:size="size || item.size"
:style="{width:item.width+'px'}"
:multiple="item.multiple"
:placeholder="`请选择${item.placeholder||''}`"
@change="item.change && item.change(searchData[item.prop])"
@visible-change="item.focus && item.focus($event,searchData[item.prop])"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
>
<el-option v-for="op in item.options" :label="op.label" :value="op.value" :key="op.value"></el-option>
</el-select>
<!-- 级联省市区 -->
<el-cascader
v-if="item.type==='cascader'"
:style="{width:item.width+'px'}"
:placeholder="item.placeholder || '请选择'"
:options="item.options || []"
v-model="searchData[item.prop]"
@focus="item.focus && item.focus(searchData[item.prop])"
filterable
clearable
:show-all-levels="item.showAll && item.showAll(searchData[item.prop])"
:props="{ expandTrigger: 'hover',checkStrictly: item.checkStrictly && item.checkStrictly(searchData[item.prop]) }"
@change="item.change && item.change(searchData[item.prop])"
></el-cascader>
<!-- 单选 -->
<el-radio-group
v-if="item.type==='radio'"
v-model="searchData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(searchData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
>
<el-radio v-for="ra in item.radios" :label="ra.value" :key="ra.value">{{ra.label}}</el-radio>
</el-radio-group>
<!-- 单选按钮 -->
<el-radio-group
v-if="item.type==='radioButton'"
v-model="searchData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(searchData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
>
<el-radio-button v-for="ra in item.radios" :label="ra.value" :key="ra.value">{{ra.label}}</el-radio-button>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-if="item.type==='checkbox'"
v-model="searchData[item.prop]"
:style="{width:item.width+'px'}"
@change="item.change && item.change(searchData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
>
<el-checkbox v-for="ch in item.checkboxs" :label="ch.value" :key="ch.value">{{ch.label}}</el-checkbox>
</el-checkbox-group>
<!-- 日期 -->
<el-date-picker
v-if="item.type==='date'"
v-model="searchData[item.prop]"
:style="{width:item.width+'px'}"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
:placeholder="`请选择${item.placeholder||''}`"
@change="item.change && item.change(searchData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
></el-date-picker>
<!-- 开始-结束日期 -->
<el-date-picker
v-if="item.type==='daterange'"
type="daterange"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
v-model="searchData[item.prop]"
:style="{width:item.width+'px'}"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
@change="item.change && item.change(searchData[item.prop])"
:size="size || item.size"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
></el-date-picker>
<!-- 时间 -->
<el-time-select
v-if="item.type==='time'"
v-model="searchData[item.prop]"
format="HH:mm:ss"
value-format="HH:mm:ss"
:picker-options="item.options||{
start: '00:00',
step: '00:30',
end: '23:30'
}"
:style="{width:item.width+'px'}"
@change="item.change && item.change(searchData[item.prop])"
:size="size || item.size"
:placeholder="`请选择${item.placeholder||''}`"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
></el-time-select>
<!-- 日期时间 -->
<el-date-picker
v-if="item.type==='dateTime'"
:type="item.timeType||'datetime'"
:style="{width:item.width+'px'}"
v-model="searchData[item.prop]"
@change="item.change && item.change(searchData[item.prop])"
:size="size || item.size"
:placeholder="`请选择${item.placeholder||''}`"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
></el-date-picker>
<!-- 滑块 -->
<!-- <el-slider v-if="item.type==='Slider'" v-model="searchData[item.prop]"></el-slider> -->
<!-- 开关 -->
<el-switch
v-if="item.type==='switch'"
v-model="searchData[item.prop]"
:size="size || item.size"
:active-value="item.values&&item.values[0]"
:inactive-value="item.values&&item.values[1]"
@change="item.change && item.change(searchData[item.prop])"
:style="{width:item.width+'px'}"
:disabled="item.isDisabled && item.isDisabled(searchData[item.prop])"
></el-switch>
<!-- 图片上传 -->
<section v-if="item.type==='upload'" :style="{width:item.width+'px'}">
<el-upload
v-loading="loading"
class="avatar-uploader"
:action="upload_url+'/common/upload'"
:show-file-list="false"
:on-success="item.success"
:on-error="handleAvatarError"
:on-progress="handleAvatarProgress"
:before-upload="beforeAvatarUpload"
name="file"
>
<img v-if="searchData[item.prop]" :src="searchData[item.prop]|formatImg" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</section>
<!-- 多图片上传 -->
<section v-if="item.type==='uploadList'" :style="{width:item.width+'px'}">
<el-upload
ref="pictureCard"
v-loading="loading"
:action="upload_url+'/common/upload'"
list-type="picture-card"
:multiple="true"
:limit="item.limit||9"
:fileList="fileList"
:on-success="handleAvatarSuccess"
:on-error="handleAvatarError"
:on-progress="handleAvatarProgress"
:before-upload="beforeAvatarUpload"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:on-preview="handlePictureCardPreview"
:headers="{'Authorization':$cookie.get('token')}"
name="file"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible" title="预览" width="600px">
<img width="100%" :src="dialogImageUrl" alt />
</el-dialog>
</section>
<!-- 富文本 -->
<r-tinymce
v-if="item.type==='tinymce'"
v-model="searchData[item.prop]"
:height="300"
:width="600"
></r-tinymce>
</el-form-item>
<el-form-item v-for="(item,i) in searchHandle" :key="'searchBtn'+i">
<!-- <el-button type="primary" @click="onSubmit">{{item.label}}</el-button> -->
<el-button
v-if="(item.isShow===undefined && item.theme===undefined)?true:item.isShow&& item.isShow()"
:disabled="item.isDisabled && item.isDisabled(item)"
class="el-button el-button--medium"
            :class="getBtnClass(item)"
:style="{width:item.width+'px'}"
@click="getHandle(item.handle)"
@mouseleave="mouseleave(item.icon)"
            @mouseenter="mouseenter(item.icon)"
            >
<i :class="upCir(item.icon)"></i>
{{item.label}}</el-button>
<div v-if="item.theme==='upload'">
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
<el-button :loading="item.loading" :size="item.size || size" type="success" icon="el-icon-upload2" @click="handleUpload">
{{item.label}}
</el-button>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "Search",
props: {
labelPosition:{
type: String,
default: "none"
},
isState:{
type: Boolean,
default: true,
},
isHandle: {
type: Boolean,
default: true,
},
inline: {
type: Boolean,
default: true,
},
labelWidth: {
type: String,
default: "100px",
},
// mini,medium,small
size: {
type: String,
default: "medium",
},
searchForm: {
type: Array,
default: () => [],
},
searchHandle: {
type: Array,
default: () => [],
},
searchData: {
type: Object,
default: () => {},
},
searchRules: {
type: Object,
default: () => {},
},
fileList: {
type: Array,
default: () => [],
},
},
data() {
return {
loading: false,
imageUrl: "",
upload_url: process.env.NODE_ENV !== 'production'?'/proxy':process.env.VUE_APP_URL,
imgurl: process.env.VUE_APP_IMG,
dialogVisible: false,
dialogImageUrl:'',
isMouse:false,
isLoading:false,
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
};
},
computed: {
getInputStyle(){
return (width)=>{
if (width) {
return `${width}+px`;
}
return '180px'
}
},
upCir(){
return (icon)=>{
if (this.loading&&icon==='el-icon-refresh') {
if (this.loading) {
return `el-icon-loading`
}
return `el-icon-refresh`;
}
else if (this.isMouse&&icon==='el-icon-refresh') {
return `good el-icon-refresh-left`
}
else {
return icon;
}
}
},
getBtnClass(){
return (row)=>{
let str=``,bbs=row.icon=='el-icon-refresh'?true:false;
str+=`el-button--`+row.type+` `
// str+=row.size?`el-button--`+row.size+` `:``
if (this.loading&&bbs) {
str+=`is-disabled`
}
return str;
}
},
},
methods: {
getHandle(func){
func.call(func)
},
mouseenter(icon){
if (icon==='el-icon-refresh') {
this.isMouse=true;
}
},
mouseleave(icon){
if (icon==='el-icon-refresh') {
this.isMouse=false;
}
},
BlurText(e) {
let boolean = new RegExp("^[1-9][0-9]*$").test(e.target.value)
if(!boolean) {
this.$message.warning('请输入正整数')
e.target.value = ' '
}
},
handleAvatarSuccess(res, file) {
this.loading = false;
this.imageUrl = URL.createObjectURL(file.raw);
this.$message({
message: "上传成功",
type: "success",
});
this.$emit("imgs", {
type: "text",
url: `${res.data}`,
});
},
handleUpload() {
this.$refs['excel-upload-input'][0].click()
},
handleClick(e){
const files = e.target.files
const rawFile = files[0] // only use files[0]
if (!rawFile) return
//this.upload(rawFile)
this.$emit("uploadEX", rawFile);
},
upload(rawFile) {
this.$refs['excel-upload-input'][0].value = null // fix can't select the same excel
const before = this.beforeUpload(rawFile)
if (before) {
let formData = new FormData();
formData.append('file',rawFile);
this.$http.upload('/file/upload',formData).then(res=>{
if(res.code == 200){
this.$message({
type: "success",
message: "上传成功",
});
}
})
}
},
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: '小于1兆',
type: 'warning'
})
return false
},
handleAvatarError() {
this.loading = false;
this.$message.error("上传失败");
},
beforeAvatarUpload(file) {
const isJPG = file.type === "image/jpeg" || "image/png";
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isJPG) {
this.$message.error("上传格式只能是图片格式!");
}
if (!isLt5M) {
this.$message.error("上传头像图片大小不能超过 5MB!");
}
return isJPG && isLt5M;
},
handleAvatarProgress(res, file) {
this.loading = true;
},
handleRemove(file, fileList) {
let list = fileList.map((item) => {
console.log(item)
if(item.response){
return item.response.data
}else{
if(item.url.indexOf('http')!=-1){
return item.url.split(process.env.VUE_APP_IMG)[1]
}else{
return item.url
}
}
});
this.$emit("imgs", {
type: "pictureCard",
url: list,
});
},
handleExceed() {
this.$message({
message: `最多上传${this.$refs.pictureCard[0].limit||9}张图片`,
type: "warning",
});
},
handlePictureCardPreview(file){
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
},
};
</script>
<style scoped lang="scss">
.ces-search {
display:flex;
flex-direction:column;
flex-wrap: wrap;
align-items:center;
}
@keyframes circles{
0%{transform:rotateZ(0deg);}
100%{transform:rotateZ(360deg);}
}
.good{
animation-name:circles;
animation-duration:3s;
animation-iteration-count:1;
}
.avatar-uploader,
.el-upload {
border: 1px dashed #ccc;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 150px;
height: 150px;
display: block;
}
.avatar-uploader:hover,
.el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 150px;
height: 150px;
line-height: 150px;
text-align: center;
}
.avatar {
width: 150px;
height: 150px;
display: block;
}
.excel-upload-input{
display: none;
z-index: -9999;
}
</style>

View File

@ -0,0 +1,485 @@
<!--表格组件 -->
<template>
<section class="ces-table-pagee flex_column flex1">
<slot></slot>
<!-- 表格操作按钮 -->
<section class="ces-handle" v-if="isHandle" style="margin-bottom: 20px;">
<!-- <section class="ces-handle" v-if="isHandle"> -->
<el-button
v-for="item in tableHandles"
v-if="item.isShow ? item.isShow():true"
:disabled="item.isDisabled && item.isDisabled()"
:key="item.label"
:size="size || item.size"
:type="item.type"
:icon="item.icon"
@click="item.handle"
>{{item.label}}</el-button>
<slot name="tip"></slot>
</section>
<!-- 数据表格 -->
<section class="ces-table flex1" ref="tableBody">
<el-table
:data="tableData"
:size="size"
:border="isBorder"
@select="select"
@select-all="selectAll"
@current-change="CurrentChange"
:highlightCurrentRow="highlightCurrentRow"
:row-class-name="tableRowClassName"
v-loading="loading"
:defaultSelections="defaultSelections"
ref="cesTable"
:row-key="rowKey"
:height="height"
:header-cell-style="{backgroundColor: '#f5f7fa',fontWeight: 'bold',color: '#909399'}"
:tree-props="treeProps"
>
<el-table-column v-if="isSelection" type="selection" align="center" sortable></el-table-column>
<el-table-column v-if="isIndex" type="index" sortable :label="indexLabel" align="center" width="80" :index="indexMethod"></el-table-column>
<!-- 数据栏 -->
<el-table-column
v-for="(item,idx) in tableCols"
:key="idx"
:prop="item.prop"
:label="item.btnLabel||item.label"
:width="item.width"
:min-width="item.minWidth"
:align="item.align || 'center'"
:fixed="item.fixed"
:render-header="item.require?renderHeader:null"
:show-overflow-tooltip="isOverflow"
>
<template slot-scope="scope">
<!-- html -->
<span v-if="item.type==='html'" v-html="item.html(scope.row)"></span>
<!-- tag -->
<el-tag
v-if="item.type==='tag'"
:type="item.theme && item.theme(scope.row)"
>{{(item.formatter && item.formatter(scope.row)) || scope.row[item.prop]}}</el-tag>
<!-- 按钮 -->
<span v-if="item.type==='button'">
<el-button
v-for="(btn,idx) in item.btnList"
:key="idx"
:disabled="btn.isDisabled&& btn.isDisabled(scope.row)"
v-if="btn.isShow?btn.isShow(scope.row):true"
:type="btn.type"
:size="btn.size || size"
:icon="btn.icon"
:circle="btn.circle||false"
@click="btn.handle(scope.row,scope.$index)"
>{{btn.label}}</el-button>
<el-dropdown v-if="item.dropList" @command="item.handleCommand" trigger="click">
<el-button icon="el-icon-setting" :size="item.dropSize || size" type="info" />
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="(drop,idx) in item.dropList"
:key="idx"
:command="{btn:drop.label,row:scope.row}"
:disabled="drop.isDisabled && drop.isDisabled(scope.row)"
>{{drop.label}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
<!-- 输入框 -->
<el-input
v-if="item.type==='input'"
v-model="scope.row[item.prop]"
:size="size || btn.size"
:disabled="item.isDisabled && item.isDisabled(scope.row)"
:placeholder="item.placeholder || '请输入'"
@focus="item.focus && item.focus(scope.row)"
@input="item.input && item.input(scope.row,scope.$index)"
@change="item.change && item.change(scope.row,scope.$index)"
></el-input>
<!-- 下拉框 -->
<el-select
v-if="item.type==='select'"
v-model="scope.row[item.prop]"
:size="size || btn.size"
:disabled="item.isDisabled && item.isDisabled(scope.row)"
@change="item.change && item.change(scope.row)"
>
<el-option
v-for="(op,idx) in item.options"
:label="op.label"
:value="op.value"
:key="idx"
></el-option>
</el-select>
<!-- 级联省市区 -->
<el-cascader
v-if="item.type==='cascader'"
:style="{width:item.width+'px'}"
:placeholder="item.placeholder || '请选择'"
:options="item.options || []"
v-model="scope.row[item.prop]"
@focus="item.focus && item.focus(scope.row)"
filterable
clearable
:props="{ expandTrigger: 'hover',checkStrictly: true }"
@change="item.change && item.change(scope.row)"
></el-cascader>
<!-- 单选 -->
<el-radio-group
v-if="item.type==='radio'"
v-model="scope.row[item.prop]"
:disabled="item.isDisabled && item.isDisabled(scope.row)"
:size="size || btn.size"
@change="item.change && item.change(scope.row)"
>
<el-radio v-for="(ra,idx) in item.radios" :label="ra.value" :key="idx">{{ra.label}}</el-radio>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-if="item.type==='checkbox'"
v-model="scope.row[item.prop]"
:disabled="item.isDisabled && item.isDisabled(scope.row)"
:size="size || btn.size"
@change="item.change && item.change(scope.row)"
>
<el-checkbox
v-for="(ra,idx) in item.checkboxs"
:label="ra.value"
:key="idx"
>{{ra.label}}</el-checkbox>
</el-checkbox-group>
<!-- 评价 -->
<el-rate
v-if="item.type==='rate'"
:value ="scope.row[item.prop]*1"
:allow-half="true"
:disabled="item.isDisabled && item.isDisabled(scope.row)"
:size="size || btn.size"
@change="item.change && item.change(scope.row)"
></el-rate>
<!-- 开关 -->
<el-switch
v-if="item.type==='switch'"
v-model="scope.row[item.prop]"
:size="size || btn.size"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="item.values&&item.values[0]"
:inactive-value="item.values&&item.values[1]"
:disabled="item.isDisabled && item.isDisabled(scope.row)"
@change="item.change && item.change(scope.row)"
></el-switch>
<!-- 二维码 -->
<el-popover
v-if="item.type==='qrCode'"
:placement="item.placement || 'left'"
trigger="click"
id="qrcode"
>
<div>
<vue-qr
:logo-src="item.logoSrc"
:size="300"
:margin="10"
:auto-color="true"
:dot-scale="1"
:text="(item.formatter && item.formatter(scope.row)) || (scope.row[item.prop]===0?0:scope.row[item.prop]?scope.row[item.prop]:'-')" />
</div>
<vue-qr
slot="reference"
:logo-src="item.logoSrc"
:size="100"
:margin="10"
:auto-color="true"
:dot-scale="1"
:text="(item.formatter && item.formatter(scope.row)) || (scope.row[item.prop]===0?0:scope.row[item.prop]?scope.row[item.prop]:'-')" />
</el-popover>
<!-- 单图像 -->
<!-- <img v-if="item.type==='image'" :src="scope.row[item.prop]" @click="item.handle && item.handle(scope.row)"/> -->
<el-popover
v-if="item.type==='image'"
:placement="item.placement || 'left'"
trigger="click"
>
<div>
<el-image
style="width: 300px;height: auto;"
:src="scope.row[item.prop]|formatImg"
fit="fill"
></el-image>
</div>
<el-image
:style="item.style || 'width: 80px;height: 80px;'"
:src="scope.row[item.prop]|formatImg"
fit="contain"
slot="reference"
></el-image>
</el-popover>
<!-- 多图像 -->
<el-popover
v-if="item.type==='images'"
:placement="item.placement || 'left'"
trigger="click"
>
<div>
<el-carousel
trigger="click"
style="width: 300px;height: auto;"
arrow="always"
indicator-position="none"
:autoplay="false"
>
<el-carousel-item v-for="(url,idx) in imgList(scope.row[item.prop])" :key="idx">
<img :src="url|formatImg" alt style="width: 300px;height: auto;" />
</el-carousel-item>
</el-carousel>
</div>
<el-button type="text" slot="reference">查看</el-button>
</el-popover>
<!-- 滑块 -->
<el-slider
v-if="item.type==='slider'"
v-model="scope.row[item.prop]"
:disabled="item.isDisabled && item.isDisabled(scope.row)"
:size="size || btn.size"
@change="item.change && item.change(scope.row)"
></el-slider>
<!-- 默认 -->
<span
v-if="!item.type"
@click="item.handle && item.handle(scope.row)"
:style="item.itemStyle && item.itemStyle(scope.row)"
:class="item.itemClass && item.item.itemClass(scope.row)"
>{{(item.formatter && item.formatter(scope.row)) || (scope.row[item.prop]===0?0:scope.row[item.prop]?scope.row[item.prop]:'-')}}</span>
</template>
</el-table-column>
<div slot="empty">
<p>
<img src="~@/assets/img/original.png" width="163" height="100" />
</p>
<p>
<span>当前暂无数据请添加数据</span>
</p>
</div>
</el-table>
</section>
<!-- 分页 -->
<section class="ces-pagination" v-if="isPagination">
<el-pagination
style="display: flex;justify-content: star;align-items: center;"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
:page-sizes="[10, 20, 50, 100]"
layout="total,sizes ,prev, pager, next,jumper"
:page-size="tablePage[pagination[0]] || tablePage.size"
:current-page="tablePage[pagination[1]] || tablePage.offset"
:total="tablePage[pagination[2]] || tablePage.total"
></el-pagination>
</section>
</section>
</template>
<script>
// import VueQr from 'vue-qr';
export default {
name: "Table",
// components: {VueQr},
props: {
// mini,medium,small
size: {
type: String,
default: "medium",
},
rowKey:{
type: String,
default: "id",
},
isOverflow: {
type: Boolean,
default: false,
},
isBorder: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
//
isHandle: {
type: Boolean,
default: false,
},
tableHandles: {
type: Array,
default: () => [],
},
//
tableData: {
type: Array,
default: () => [],
},
//
tableCols: {
type: Array,
default: () => [],
},
//
isSelection: {
type: Boolean,
default: false,
},
defaultSelections: {
type: [Array, Object],
default: () => null,
},
//
isIndex: {
type: Boolean,
default: false,
},
indexLabel: {
type: String,
default: "序号",
},
//
isPagination: {
type: Boolean,
default: false,
},
//
tablePage: {
type: Object,
default: () => ({
size: 10,
page: 1,
total: 0,
}),
},
highlightCurrentRow: {
type: Boolean,
default: false,
},
treeProps:{
type: Object,
default: () => ({children: 'children', hasChildren: 'hasChildren'}),
},
},
data() {
return {
height: 0,
imgurl: process.env.VUE_APP_IMG,
pagination:[]
//imgurl:'http://s302t98620.zicp.vip:44353/car/img/'
};
},
watch: {
defaultSelections(val) {
this.$nextTick(function () {
if (Array.isArray(val)) {
val.forEach((row) => {
this.$refs.cesTable.toggleRowSelection(row);
});
} else {
this.$refs.cesTable.toggleRowSelection(val);
}
});
},
},
mounted() {
if(this.pagination && this.pagination.length==0){
Object.keys(this.tablePage).forEach(key=>{
this.pagination.push(key)
})
}
this.$nextTick((_) => {
this.$parent.$el.className = "flex_column";
this.$parent.$el.style.height = "100%";
this.height = this.$refs.tableBody.clientHeight;
console.log('mounted:this.height'+this.height);
// this.height = 500;
});
},
computed: {
getSort(){
return (index)=>{
return !Boolean(index)
}
}
},
methods: {
indexMethod(index){
return (this.tablePage[this.pagination[1]]-1)*this.tablePage[this.pagination[0]]+index+1
},
//
select(rows, row) {
this.$emit("select", rows, row);
},
//
selectAll(rows) {
this.$emit("select", rows);
},
//
handleCurrentChange(val) {
this.tablePage[this.pagination[1]] = val;
this.$emit("refresh", {
[this.pagination[0]]: this.tablePage[this.pagination[0]],
[this.pagination[1]]: this.tablePage[this.pagination[1]],
});
},
handleSizeChange(val) {
this.tablePage[this.pagination[0]] = val;
this.$emit("refresh", {
[this.pagination[0]]: this.tablePage[this.pagination[0]],
[this.pagination[1]]: this.tablePage[this.pagination[1]],
});
},
CurrentChange(val) {
this.$emit("radio", val);
},
imgList(img) {
if (img) {
let arr = img.split(",").map(item=>{
return item.startsWith('http') ? item : process.env.VUE_APP_IMG + item;
})
return arr;
}
return [];
},
tableRowClassName({ row, rowIndex }) {
//
row.index = rowIndex;
},
renderHeader(h, obj) {
return h(
"span",
{
class: "ces-table-require",
},
obj.column.label
);
},
},
};
</script>
<style scoped>
.ces-table-require::before {
content: "*";
color: red;
}
.el-button + .el-dropdown {
margin-left: 10px;
}
.ces-handle{
display: flex;
flex-direction: row;
}
/* .ces-table-page{
margin-top: 20px;
padding: 0 30px;
} */
</style>

View File

@ -0,0 +1,9 @@
import Vue from 'vue'
const componentsContext = require.context('./', true, /index\.vue$/);
componentsContext.keys().forEach(component => {
const componentConfig = componentsContext(component);
const ctrl = componentConfig.default || componentConfig;
Vue.component(`r-${ctrl.name.substr(0,1).toLocaleLowerCase()+ctrl.name.substr(1)}`, ctrl)
});

View File

@ -3,13 +3,20 @@ import App from "./App.vue";
import router from "./router";
import store from "./store";
import ElementUI from "element-ui";
import httpRequest from '@/util/https'
import msg from '@/util/msg'
import "element-ui/lib/theme-chalk/index.css";
import "@/css/style.css";
import '@/assets/scss/index.scss'
import "./router/main";
import '@/components/global'
import echarts from '@/util/echartUi.js'
Vue.use(ElementUI);
Vue.config.productionTip = false;
Vue.prototype.$http = httpRequest
Vue.prototype.$msg = msg
Vue.prototype.$chart = echarts
new Vue({
router,
store,

View File

@ -115,6 +115,53 @@ const list = [
name: "供应商列表",
component: () => import("../views/supplier/supplierList.vue"),
},
{
path: "putWarehouse",
name: "商品入库",
component: () => import("../views/stock/putWarehouse.vue"),
},
{
path: "stockNotice",
name: "库存预警",
component: () => import("../views/stock/stockNotice.vue"),
},
{
path: "stockTacks",
name: "盘点报损",
component: () => import("../views/stock/stockTacks.vue"),
},
{
path: "message",
name: "消息列表",
component: () => import("../views/message/message.vue"),
},
{
path: "salesChart",
name: "销售图表",
component: () => import("../views/dataCenter/salesChart.vue"),
},
{
path: "goodChart",
name: "商品图表",
component: () => import("../views/dataCenter/goodChart.vue"),
},
{
path: "flagChart",
name: "交易趋势图表",
component: () => import("../views/dataCenter/flagChart.vue"),
},
{
path: "damageChart",
name: "报损图表",
component: () => import("../views/dataCenter/damageChart.vue"),
},
{
path: "costChart",
name: "交易趋势图表",
component: () => import("../views/dataCenter/costChart.vue"),
},
],
},
];

1
resources/frontend/src/util/area2.js vendored Normal file

File diff suppressed because one or more lines are too long

36
resources/frontend/src/util/echartUi.js vendored Normal file
View File

@ -0,0 +1,36 @@
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts';
// 引入柱状图图表,图表后缀都为 Chart
import { BarChart,LineChart,PieChart } from 'echarts/charts';
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LegendComponent,
ToolboxComponent,
} from 'echarts/components';
// 标签自动布局,全局过渡动画等特性
import { LabelLayout, UniversalTransition } from 'echarts/features';
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from 'echarts/renderers';
// 注册必须的组件
echarts.use([
ToolboxComponent,
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
BarChart,
LabelLayout,
UniversalTransition,
CanvasRenderer,
LineChart,
PieChart
]);
export default echarts

View File

@ -2,9 +2,15 @@ import axios from "axios";
import { getToken } from "@/util/auth";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import qs from 'qs'
import merge from 'lodash/merge'
import { Message } from "element-ui";
var instance = axios.create({
timeout: 360000,
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*',
}
});
instance.interceptors.request.use(
@ -54,5 +60,43 @@ instance.interceptors.response.use(
return Promise.reject(error);
}
);
instance.adornParams = (params = {}, openDefultParams = false) => {
var defaults = {
't': new Date().getTime()
}
return openDefultParams ? merge(defaults, params) : params
}
instance.adornData = (data = {}, openDefultdata = false, contentType = 'json') => {
var defaults = {
't': new Date().getTime()
}
data = openDefultdata ? merge(defaults, data) : data
return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data)
}
instance.adornUrl = (actionName) => {
//return (process.env.NODE_ENV !== 'production' && process.env.OPEN_PROXY ? 'proxyApi/' : process.env.VUE_APP_URL) + actionName
if (process.env.VUE_APP_API_NODE !== 'production' && process.env.VUE_APP_API_PROXY_PREFIX) {
console.log(111111111);
return `${actionName}`
} else {
console.log(111111111);
env.VUE_APP_BASE_URL + actionName
}
}
instance.get = (url, query) => {
return instance({
url: instance.adornUrl(url),
method: 'get',
params: query
})
}
instance.post = (url, data = {}) => {
return instance({
url: instance.adornUrl(url),
method: 'post',
data: data
})
}
export default instance;

106
resources/frontend/src/util/https.js vendored Normal file
View File

@ -0,0 +1,106 @@
import axios from "axios";
import { getToken } from "@/util/auth";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import qs from 'qs'
import merge from 'lodash/merge'
import { Message } from "element-ui";
const http = axios.create({
timeout: 1000 * 50,
withCredentials: true,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
})
axios.defaults.withCredentials = true;
axios.defaults.changeOrigin = true;
http.interceptors.request.use(
(config) => {
// config.headers['content-type'] = 'application/json'
// config.headers['Shop-Id'] = localStorage.getItem('shopId') || 1
// 在发送请求之前做些什么
// config.headers['content-type'] = 'application/json'
config.headers.Authorization = "Bearer " + getToken(); // 请求头
NProgress.start();
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
http.interceptors.response.use(
(response) => {
NProgress.done();
console.log('添加响应拦截器'+response);
const res = response.status;
// 对响应数据做点什么
if (res === 200 || res === 201) {
return response.data
}
},
(error) => {
console.log(error.response);
NProgress.done();
// 对响应错误做点什么
if (error.response.status === 400) {
Message({
message: error.response.data.errorMessage,
type: "error",
});
}
if (error.response.status === 500) {
Message({
message: error.response.data.message,
type: "error",
});
}
return Promise.reject(error);
}
);
http.adornParams = (params = {}, openDefultParams = false) => {
var defaults = {
't': new Date().getTime()
}
return openDefultParams ? merge(defaults, params) : params
}
http.adornData = (data = {}, openDefultdata = false, contentType = 'json') => {
var defaults = {
't': new Date().getTime()
}
data = openDefultdata ? merge(defaults, data) : data
return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data)
}
http.adornUrl = (actionName) => {
if (process.env.VUE_APP_API_NODE !== 'production' && process.env.VUE_APP_API_PROXY_PREFIX) {
return process.env.VUE_APP_API_PROXY_PREFIX+`${actionName}`
} else {
process.env.VUE_APP_BASE_URL + actionName
}
}
http.get = (url, query) => {
return http({
url: http.adornUrl(url),
method: 'get',
params: query
})
}
http.post = (url, data = {}) => {
return http({
url: http.adornUrl(url),
method: 'post',
data: data
})
}
export default http;

58
resources/frontend/src/util/msg.js vendored Normal file
View File

@ -0,0 +1,58 @@
import {MessageBox, Message} from 'element-ui'
const message = (msg, type) => {
Message({
showClose: true,
message: msg,
type: type
});
}
//成功
const success = (msg) => {
message(msg, 'success');
}
//消息
const info = (msg) => {
message(msg, 'info');
}
//错误
const error = (msg) => {
message(msg, 'error');
}
//警告
const warning = (msg) => {
message(msg, 'warning');
}
//ElMessageBox
const alert = ({title, msg, okBtnText, func}) => {
if (!title) {
title = '提示';
}
if (!msg) {
msg = '错误';
}
if (!okBtnText) {
okBtnText = '确定';
}
MessageBox.alert(msg, title, {
confirmButtonText: okBtnText,
}).then(func ? func : () => {})
}
const confirm = ({title, msg, func, cFunc}) => {
MessageBox.confirm(msg, title ? title:'提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
}).then(func ? func : () => {}).catch(cFunc ? cFunc : () => {});
}
export default {
success,
info,
error,
warning,
alert,
confirm,
}

View File

@ -49,6 +49,7 @@ export default {
type: "error",
});
} else {
console.log(11111111);
axios.post("/api/auth/login", this.form).then((res) => {
let data = res.data;
if (data.error) {

View File

@ -0,0 +1,202 @@
<template>
<div>
<el-card class="boxcard">
<div slot="header" class="clearfix">
<span style="padding-right: 5px;">时间维度</span>
<el-radio-group @input="change" v-model="param.time">
<el-radio-button v-for="time in list.times" :label="time.value">{{time.label}}</el-radio-button>
</el-radio-group>
<el-date-picker
@change="change"
v-model="param.date"
value-format="yyyy-MM-dd"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions">
</el-date-picker>
</div>
<div id="canvas" style="height: 300px;width:100%;"></div>
</el-card>
<!-- 内容 -->
<div :style="{height:`${this.contentHeight}px`}">
<r-table class="page" :isIndex="false" :tableData="tableData" :tableCols="tableCols" @refresh="init()" >
</r-table>
</div>
</div>
</template>
<script>
export default {
data() {
return {
param:{keyWord:'week',time:'week'},
contentHeight:500,
myChart:{},
list:{times:[{label:'今日',value:'day'},{label:'昨日',value:'yesterday'},{label:'本周',value:'week'},{label:'本月',value:'month'},{label:'本季',value:'quarter'},{label:'本年',value:'year'}],},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
tableData: [],
tableCols:[
{ label: "商品类型", prop: "type_name",width:150},
{ label: "品牌", prop: "brand_name",width:150},
{ label: "商品名称", prop: "good_name",width:150},
{ label: "商品sku", prop: "title",width:200},
{ label: "SKU销量", prop: "goods_number",width:200},
{ label: "实际⼊库", prop: "stock",width:200},
{ label: "总量库存", prop: "num",width:200},
{ label: "可售库存", prop: "shopStock",width:200},
{ label: "采购人员", prop: "buyer_user",width:150},
{ label: "操作人员", prop: "operator_user",width:150},
{ label: "创建时间", prop: "created_by",width:200},
],
}
},
mounted() {
let element = document.querySelector('.box-card');
let topEl = document.querySelector('.boxcard');
this.contentHeight=element.offsetHeight-topEl.offsetHeight-70
this.$nextTick(()=>{
setTimeout(() => {
this.init(this.param)
}, 1500);
})
},
methods:{
change(e){
this.param.keyWord=e
this.init(this.param)
},
init(param={}){
this.$http.post('/costChart',Object.assign(param)).then(res=>{
if (res.code==200) {
this.getCanvas(res.data.field,res.data.data,res.data.sumDate)
this.tableData=res.data.list
} else {
this.$msg.error('获取数据失败,请重新获取~')
}
})
},
getCanvas(field=[],data=[],sumCount=[]) {
console.log(data);
let series=[]
this.myChart = this.$chart.init(document.getElementById("canvas"));
const option = {
color: [
'#CCFFFF','#FFCCCC','#99CCCC','#FFCC99','#FF9999','#996699','#CC9999','#FFFFCC','#CCCC99','#FFFF99','#CCCCFF','#0099CC','#CCCCCC','#FF6666','#FF9966','#FFCCCC','#CC9966','#666666','#FF6666','#FFFF66','#99CC66','#CC3333','#CCCCCC','#003366','#663366','#CCCC99','#666666','#FF6666','#FFFF00','#0066CC','#333333','#336633','#990033','#66CC00','#CC99CC','#339933','#993399','#006633',
'#CC9966','#003300','#FF0033','#333399','#CCCC00','#003399','#99CC00','#CC0033','#999933','#993333','#333300','#CCFF99','#99CCFF','#99CC99','#336699','#CC9933','#FFCC33','#336666','#99CC33','#FFCC00','#CC6699','#FFFF00','#3366CC','#FF6600','#009966','#990033','#CCFF66','#FF9900','#FF9966','#996600','#CC6600','#999999','#CCCC33','#000000','#999966','#663300','#FF9933','#6666CC'
],
dataZoom: [
{
type: 'slider',
realtime: true,
start: 0,
end: 5,
height: 5,
left: 5,
right: 5,
bottom: 10,
show: true,
fillerColor: "rgba(17, 100, 210, 0.42)",
borderColor: "rgba(17, 100, 210, 0.12)",
handleSize: 0,
showDetail: false,
zoomLock: true,
moveOnMouseMove: false,
startValue: 0,
endValue: 6,
minValueSpan: 6,
maxValueSpan: 6,
},
{
type: "inside",
start: 0,
end: 5,
zoomOnMouseWheel: false,
moveOnMouseWheel: true,
moveOnMouseMove: true
}
],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
width: 500,
type: 'scroll',
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: field
}
],
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
mark: { show: true },
dataView: { show: true, sreadOnly: false },
magicType: { show: true, type: ['stack'] },
restore: { show: true },
saveAsImage: { show: true }
}
},
yAxis: [
{
type: 'value'
}
],
series: []
};
Object.keys(data).forEach(key => {
console.log(data[key]);
series.push({name: key,type: 'bar',stack: 'Ad',emphasis: {focus: 'series'},data:data[key]})
});
series.push({name: '合计',type: 'bar',data: sumCount,emphasis: {focus: 'series'},markLine: {lineStyle: {type: 'dashed'},data: [[{ type: 'min' }, { type: 'max' }]]}})
option.series=series
this.myChart.setOption(option);
},
}
};
</script>

View File

@ -0,0 +1,202 @@
<template>
<div>
<el-card class="boxcard">
<div slot="header" class="clearfix">
<span style="padding-right: 5px;">时间维度</span>
<el-radio-group @input="change" v-model="param.time">
<el-radio-button v-for="time in list.times" :label="time.value">{{time.label}}</el-radio-button>
</el-radio-group>
<el-date-picker
@change="change"
v-model="param.date"
value-format="yyyy-MM-dd"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions">
</el-date-picker>
</div>
<div id="canvas" style="height: 300px;width:100%;"></div>
</el-card>
<!-- 内容 -->
<div :style="{height:`${this.contentHeight}px`}">
<r-table class="page" :isIndex="false" :tableData="tableData" :tableCols="tableCols" @refresh="init()" >
</r-table>
</div>
</div>
</template>
<script>
export default {
data() {
return {
param:{keyWord:'week',time:'week'},
contentHeight:500,
myChart:{},
list:{times:[{label:'今日',value:'day'},{label:'昨日',value:'yesterday'},{label:'本周',value:'week'},{label:'本月',value:'month'},{label:'本季',value:'quarter'},{label:'本年',value:'year'}],},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
tableData: [],
tableCols:[
{ label: "商品类型", prop: "type_name",width:150},
{ label: "品牌", prop: "brand_name",width:150},
{ label: "商品名称", prop: "good_name",width:150},
{ label: "商品sku", prop: "title",width:200},
{ label: "报损库存", prop: "stock",width:200},
{ label: "总量库存", prop: "num",width:200},
{ label: "可售库存", prop: "shopStock",width:200},
{ label: "采购人员", prop: "buyer_user",width:150},
{ label: "操作人员", prop: "operator_user",width:150},
{ label: "创建时间", prop: "created_by",width:200},
],
}
},
mounted() {
let element = document.querySelector('.box-card');
let topEl = document.querySelector('.boxcard');
this.contentHeight=element.offsetHeight-topEl.offsetHeight-70
this.$nextTick(()=>{
setTimeout(() => {
this.init(this.param)
}, 1500);
})
},
methods:{
change(e){
this.param.keyWord=e
this.init(this.param)
},
init(param={}){
this.$http.post('/damageChart',Object.assign(param)).then(res=>{
if (res.code==200) {
this.getCanvas(res.data.field,res.data.data,res.data.sumDate)
this.tableData=res.data.list
} else {
this.$msg.error('获取数据失败,请重新获取~')
}
})
},
getCanvas(field=[],data=[],sumCount=[]) {
console.log(data);
let series=[]
this.myChart = this.$chart.init(document.getElementById("canvas"));
console.log(this.myChart);
const option = {
color: [
'#CCFFFF','#FFCCCC','#99CCCC','#FFCC99','#FF9999','#996699','#CC9999','#FFFFCC','#CCCC99','#FFFF99','#CCCCFF','#0099CC','#CCCCCC','#FF6666','#FF9966','#FFCCCC','#CC9966','#666666','#FF6666','#FFFF66','#99CC66','#CC3333','#CCCCCC','#003366','#663366','#CCCC99','#666666','#FF6666','#FFFF00','#0066CC','#333333','#336633','#990033','#66CC00','#CC99CC','#339933','#993399','#006633',
'#CC9966','#003300','#FF0033','#333399','#CCCC00','#003399','#99CC00','#CC0033','#999933','#993333','#333300','#CCFF99','#99CCFF','#99CC99','#336699','#CC9933','#FFCC33','#336666','#99CC33','#FFCC00','#CC6699','#FFFF00','#3366CC','#FF6600','#009966','#990033','#CCFF66','#FF9900','#FF9966','#996600','#CC6600','#999999','#CCCC33','#000000','#999966','#663300','#FF9933','#6666CC'
],
dataZoom: [
{
type: 'slider',
realtime: true,
start: 0,
end: 5,
height: 5,
left: 5,
right: 5,
bottom: 10,
show: true,
fillerColor: "rgba(17, 100, 210, 0.42)",
borderColor: "rgba(17, 100, 210, 0.12)",
handleSize: 0,
showDetail: false,
zoomLock: true,
moveOnMouseMove: false,
startValue: 0,
endValue: 6,
minValueSpan: 6,
maxValueSpan: 6,
},
{
type: "inside",
start: 0,
end: 5,
zoomOnMouseWheel: false,
moveOnMouseWheel: true,
moveOnMouseMove: true
}
],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
width: 500,
type: 'scroll',
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: field
}
],
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
mark: { show: true },
dataView: { show: true, sreadOnly: false },
magicType: { show: true, type: ['stack'] },
restore: { show: true },
saveAsImage: { show: true }
}
},
yAxis: [
{
type: 'value'
}
],
series: []
};
Object.keys(data).forEach(key => {
console.log(data[key]);
series.push({name: key,type: 'bar',stack: 'Ad',emphasis: {focus: 'series'},data:data[key]})
});
series.push({name: '合计',type: 'bar',data: sumCount,emphasis: {focus: 'series'},markLine: {lineStyle: {type: 'dashed'},data: [[{ type: 'min' }, { type: 'max' }]]}})
option.series=series
this.myChart.setOption(option);
},
}
};
</script>

View File

@ -0,0 +1,146 @@
<template>
<div>
<el-card class="boxcard" style="height: 100%;">
<div slot="header" class="clearfix" >
<span style="padding-right: 5px;">时间维度</span>
<el-radio-group @input="change" v-model="param.time">
<el-radio-button v-for="time in list.times" :label="time.value">{{time.label}}</el-radio-button>
</el-radio-group>
<el-date-picker
@change="change"
v-model="param.date"
value-format="yyyy-MM-dd"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions">
</el-date-picker>
</div>
<div id="canvas" style="min-height: 400px;width:100%;"></div>
</el-card>
<!-- <div :style="{height:`${this.contentHeight}px`}">
<r-table class="page" :isIndex="false" :tableData="tableData" :tableCols="tableCols" @refresh="init()" >
</r-table>
</div> -->
</div>
</template>
<script>
export default {
data() {
return {
param:{keyWord:'month',time:'month'},
contentHeight:500,
myChart:{},
list:{times:[{label:'今日',value:'day'},{label:'昨日',value:'yesterday'},{label:'本周',value:'week'},{label:'本月',value:'month'},{label:'本季',value:'quarter'},{label:'本年',value:'year'}],},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
tableData: [],
tableCols:[
{ label: "商品类型", prop: "type_name",width:150},
{ label: "品牌", prop: "brand_name",width:150},
{ label: "商品名称", prop: "good_name",width:150},
{ label: "商品sku", prop: "title",width:200},
{ label: "报损库存", prop: "stock",width:200},
{ label: "总量库存", prop: "num",width:200},
{ label: "可售库存", prop: "shopStock",width:200},
{ label: "采购人员", prop: "buyer_user",width:150},
{ label: "操作人员", prop: "operator_user",width:150},
{ label: "创建时间", prop: "created_by",width:200},
],
}
},
mounted() {
let element = document.querySelector('.box-card');
let topEl = document.querySelector('.boxcard');
this.contentHeight=element.offsetHeight-topEl.offsetHeight-70
this.$nextTick(()=>{
setTimeout(() => {
this.init(this.param)
}, 1500);
})
},
methods:{
change(e){
this.param.keyWord=e
this.init(this.param)
},
init(param={}){
this.$http.post('/flagChart',Object.assign(param)).then(res=>{
this.getCanvas(res.data.field,res.data.data)
})
},
getCanvas(field=[],data=[]) {
console.log(data);
let series=[]
this.myChart = this.$chart.init(document.getElementById("canvas"));
let option = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['订单金额', '退款金额']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: field
},
yAxis: {
type: 'value'
},
series: []
};
Object.keys(data).forEach(key => {
console.log(key);
});
series.push({name: '订单金额',type: 'line',stack: 'Total',data:data['goods_cost_price']})
series.push({name: '退款金额',type: 'line',stack: 'Total',data:data['refund_amount']})
option.series=series
this.myChart.setOption(option);
},
}
};
</script>

View File

@ -0,0 +1,90 @@
<template>
<div>
<r-board :boardData="tops" class="boxcard" />
<div :style="{height:`${this.contentHeight}px`}" style="background-color: #ffff;margin-top: 10px;padding-top: 20px;">
<r-table class="page" :isIndex="false" :tableData="tableData" :tableCols="tableCols" :tablePage="pagination" @refresh="init()" >
<div class="flex-col-center" style="width:100%;">
<r-search ref="searchalt" :searchData="searchData" :searchForm="searchForm" :searchHandle="searchHandle" style="" />
</div>
</r-table>
</div>
</div>
</template>
<script>
export default {
data() {
return {
pagination: { size: 10, page: 1, total: 0 },
searchData: {
keyWord:"",
status:'-1'
},
searchForm: [
// { type: "radioButton",label: "", prop: "keyWord",radios:[{label:'',value:'day'},{label:'',value:'yesterday'},{label:'',value:'week'},{label:'',value:'month'},{label:'',value:'quarter'},{label:'',value:'year'}]},
{ type: "input",label: "关键词", prop: "keyWord",placeholder:'商品名、规格'},
{ type: "select",label: "SKU状态", prop: "status",options:[{label:'全部',value:'-1'},{label:'在售',value:'1'},{label:'下架',value:'0'},{label:'预警',value:'2'}]},
],
searchHandle: [
{type: "info",icon: "el-icon-refresh", handle: e => this.init(true) },
{label: "搜索",type: "primary",icon: "el-icon-search",handle: e => this.seach()},
],
contentHeight:500,
tableData: [],
tableCols:[
{ label: "商品类型", prop: "type_name",width:150},
{ label: "品牌", prop: "brand_name",width:150},
{ label: "商品名称", prop: "good_name",width:150},
{ label: "商品sku", prop: "title",width:200},
{ label: "总量库存", prop: "num",width:200},
{ label: "可售库存", prop: "stock",width:200},
{ label: "采购成本", prop: "buyer_price",width:150},
{ label: "参考售价", prop: "reference_price",width:150},
{ label: "成本", prop: "cost",width:200},
{ label: "SKU状态", prop: "status",width:200},
{ label: "更新时间", prop: "updated_at",width:200},
],
tops:[{label:'商品总数',value:0},{label:'实际库存量',value:0},{label:'可售库存量',value:0},{label:'在售SKU',value:0},{label:'下架SKU',value:0},{label:'预警SKU',value:0}],
}
},
computed:{
},
mounted() {
let element = document.querySelector('.box-card');
let topEl = document.querySelector('.boxcard');
console.log(element.offsetHeight,topEl.offsetHeight);
this.contentHeight=element.offsetHeight-50-10-10
this.init()
},
methods:{
init(param={}){
this.$http.post('/goodChart',Object.assign(this.pagination,param)).then(res=>{
this.tableData=res.data.list
let skan=res.data.skan
this.tops.forEach((lst,index)=>{
lst.value=skan[index]
})
})
},
seach(){
this.init(this.searchData)
}
}
};
</script>
<style scoped>
.divi{
display: inline-block;
width: 1px;
margin: 0 8px;
vertical-align: middle;
position: relative;
background-color:#DCDFE6;
}
</style>

View File

@ -0,0 +1,201 @@
<template>
<div>
<el-card class="boxcard">
<div slot="header" class="clearfix">
<span style="padding-right: 5px;">时间维度</span>
<el-radio-group @input="change" v-model="param.time">
<el-radio-button v-for="time in list.times" :label="time.value">{{time.label}}</el-radio-button>
</el-radio-group>
<el-date-picker
@change="change"
v-model="param.date"
value-format="yyyy-MM-dd"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions">
</el-date-picker>
</div>
<div id="salescanvas" style="height: 400px;width:100%;"></div>
</el-card>
<!-- 内容 -->
<div :style="{height:`${this.contentHeight}px`}">
<r-table class="page" :isIndex="false" :tableData="tableData" :tableCols="tableCols" @refresh="init()" >
</r-table>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts';
export default {
data() {
return {
param:{keyWord:'week',time:'week'},
contentHeight:500,
myChart:{},
list:{times:[{label:'今日',value:'day'},{label:'昨日',value:'yesterday'},{label:'本周',value:'week'},{label:'本月',value:'month'},{label:'本季',value:'quarter'},{label:'本年',value:'year'}],},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
tableData: [],
tableCols:[
{ label: "商品类型", prop: "type_name",width:150},
{ label: "品牌", prop: "brand_name",width:150},
{ label: "商品名称", prop: "good_name",width:150},
{ label: "商品sku", prop: "title",width:200},
{ label: "销量", prop: "goods_number",width:200},
{ label: "实际库存", prop: "num",width:200},
{ label: "可售库存", prop: "stock",width:200},
{ label: "缺货量", prop: "lastNum",width:150},
{ label: "状态", prop: "status",width:150},
],
}
},
mounted() {
let element = document.querySelector('.box-card');
let topEl = document.querySelector('.boxcard');
this.contentHeight=element.offsetHeight-topEl.offsetHeight-70
this.$nextTick(()=>{
setTimeout(() => {
this.init(this.param)
}, 1500);
})
},
methods:{
change(e){
this.param.keyWord=e
this.init(this.param)
},
init(param={}){
this.$http.post('/salesChart',Object.assign(param)).then(res=>{
if (res.code==200) {
this.getCanvas(res.data.field,res.data.data,res.data.sumDate)
this.tableData=res.data.list
} else {
this.$msg.error('获取数据失败,请重新获取~')
}
})
},
getCanvas(field=[],data=[],sumCount=[]) {
console.log('-------getCanvas data------2023-09-25--');
var series=[]
this.myChart=this.$chart.init(document.getElementById("salescanvas"));
const option = {
color: [
'#CCFFFF','#FFCCCC','#99CCCC','#FFCC99','#FF9999','#996699','#CC9999','#FFFFCC','#CCCC99','#FFFF99','#CCCCFF','#0099CC','#CCCCCC','#FF6666','#FF9966','#FFCCCC','#CC9966','#666666','#FF6666','#FFFF66','#99CC66','#CC3333','#CCCCCC','#003366','#663366','#CCCC99','#666666','#FF6666','#FFFF00','#0066CC','#333333','#336633','#990033','#66CC00','#CC99CC','#339933','#993399','#006633',
'#CC9966','#003300','#FF0033','#333399','#CCCC00','#003399','#99CC00','#CC0033','#999933','#993333','#333300','#CCFF99','#99CCFF','#99CC99','#336699','#CC9933','#FFCC33','#336666','#99CC33','#FFCC00','#CC6699','#FFFF00','#3366CC','#FF6600','#009966','#990033','#CCFF66','#FF9900','#FF9966','#996600','#CC6600','#999999','#CCCC33','#000000','#999966','#663300','#FF9933','#6666CC'
],
dataZoom: [
{
type: 'slider',
realtime: true,
start: 0,
end: 5,
height: 5,
left: 5,
right: 5,
bottom: 10,
show: true,
fillerColor: "rgba(17, 100, 210, 0.42)",
borderColor: "rgba(17, 100, 210, 0.12)",
handleSize: 0,
showDetail: false,
zoomLock: true,
moveOnMouseMove: false,
startValue: 0,
endValue: 6,
minValueSpan: 6,
maxValueSpan: 6,
},
{
type: "inside",
start: 0,
end: 5,
zoomOnMouseWheel: false,
moveOnMouseWheel: true,
moveOnMouseMove: true
}
],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
width: 500,
type: 'scroll',
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: field
}
],
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
mark: { show: true },
dataView: { show: true, sreadOnly: false },
magicType: { show: true, type: ['stack'] },
restore: { show: true },
saveAsImage: { show: true }
}
},
yAxis: [
{
type: 'value'
}
],
series: []
};
Object.keys(data).forEach(key => {
series.push({name:key,type: 'bar',stack: 'Ad',emphasis: {focus: 'series'},data:data[key]})
});
series.push({name: '合计',type: 'bar',data: sumCount,emphasis: {focus: 'series'},markLine: {lineStyle: {type: 'dashed'},data: [[{ type: 'min' }, { type: 'max' }]]}})
option.series=series
this.myChart.setOption(option);
},
}
};
</script>

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,7 @@
</ul>
</div>
<div class="box-card">
<div class="box-card" ref="boxCard">
<router-view></router-view>
</div>
</el-main>

View File

@ -0,0 +1,76 @@
<template>
<div>
<el-card>
<!-- <el-button @click="init()">测试</el-button> -->
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane v-for="(data,index) in list" :key="index" :label="getState(index)" :name="index">
<el-alert
style="margin: 10px;height:50px;"
v-for="(item,itemi) in data" :key="itemi"
:title="item.content"
type="info"
close-text="知道了"
@close="close(item)">
</el-alert>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script>
export default {
data() {
return {
list:[],
activeName: '1'
};
},
mounted(){
this.init()
},
computed:{
getState(){
return function(state){
var str=''
console.log(state);
switch (parseInt(state)) {
case 1:
str='补货预警'
break;
case 2:
str='价格预警'
break;
default:
str='保质期预警'
break;
}
return str
}
}
},
methods: {
handleClick(tab, event) {
console.log(tab, event);
},
init(){
this.$http.post('/getNoticeList').then(res=>{
if (res.code == 200) {
console.log(res.data);
this.list=res.data
}else{
this.$msg.error('失败!')
}
})
},
close(e) {
this.$http.post('/getSaveRead',e).then(res=>{
if (res.code == 200) {
this.$msg.success('变更成功!')
}else{
this.$msg.error('变更失败!')
}
})
}
}
};
</script>

View File

@ -0,0 +1,238 @@
<template>
<div class="goods">
<el-card class="boxcard" shadow="never">
<r-search ref="searchalt" :searchData="searchData" :searchForm="searchForm" :searchHandle="searchHandle" />
</el-card>
<div :style="{height:`${this.contentHeight}px`}" style="padding-top:30px;">
<r-table class="page" :isIndex="false" isHandle :tableData="tableData" :tableCols="tableCols" :tableHandles="tableHandles" :isPagination="true" :tablePage="pagination" @refresh="init()">
<el-upload slot="tip" ref="newset" action="/api/uploadPutWarehouse" :multiple="false" name="file"
:show-file-list="false" :on-success="inventorySuccess" :before-upload="beforeInventory"
:on-error="inventoryError" style="display:inline-block;margin: 0 10px 0 10px;" :headers="headers">
<el-button type="primary" plain>入库导入</el-button>
</el-upload>
</r-table>
</div>
<r-form
:isHandle="true"
:formRules="formRules"
:formCols="formCols"
:formHandle="formHandle"
:formData="formData"
labelWidth="100px"
dialogWidth="550px"
:inline="false"
ref="elForm"
/>
</div>
</template>
<script>
export default {
data() {
return {
load:false,
flag:false,
title:'',
param:{},
supplierType:[],
headers: {
'Authorization': 'Bearer ' + localStorage.getItem("token")
},
pagination: { size: 10, page: 1, total: 0 },
searchData: {
keyWord:"",
// status:'',
},
searchForm: [
{ type: "input",label: "关键词", prop: "keyWord",placeholder:'商品、SKU、批次',width:400},
],
searchHandle: [
{type: "info",icon: "el-icon-refresh", handle: e => this.init(true) },
{label: "搜索",type: "primary",icon: "el-icon-search",handle: e => this.seach()},
],
tableData: [],
tableCols:[
{ label: "供应商名称", prop: "supplier_name",width:200},
{ label: "商品名称", prop: "good_name",width:150},
{ label: "商品sku", prop: "sku_name",width:200},
{ label: "采购批次", prop: "batch",width:150},
{ label: "采购数量", prop: "stock",width:200},
{ label: "采购单价", prop: "buyer_price",width:150},
{ label: "销售价格", prop: "price",width:150},
{ label: "保质期/天", prop: "shelf_life",width:150},
{ label: "采购日期", prop: "buyer_at",width:150},
{ label: "采购员", prop: "username",width:200},
{ label: "备注", prop: "remark",width:250},
{ label: "创建时间", prop: "created_at",width:200},
{ label: "操作",type: "button",width: 300,
btnList: [
// {label: "",type: "primary",size: "mini",handle: (row) => this.elFormChange(row)},
{label: "删除",type: "danger",size: "mini",handle: (row) => this.elFormDelete(row)},
]
},
],
tableHandles: [
{
label: "新增入库",
type: "primary",
handle: e => {this.elFormVisible(e)} ,
}
],
formData: {
supplier_id: "",
good_type:[],
stock:'',
buyer_price:'',
price:'',
shelf_life:"",
buyer_at:"",
buyer_by:"",
remark:"",
},
formCols: [
{ label: "供应商名称", prop: "supplier_id",width:350,type:'select',options:[]},
{ label: "商品/规格", prop: "good_type",width:350,type:'cascader',options:[]},
{ label: "入库数量", prop: "stock",width:350,type:'number'},
{ label: "采购单价", prop: "buyer_price",width:350,type:'number'},
{ label: "销售价格", prop: "price",width:350,type:'number'},
{ label: "保质期/天", prop: "shelf_life",width:350,type:'number'},
{ label: "入库日期", prop: "buyer_at",width:350,type:'dateTime'},
{ label: "采购员", prop: "buyer_by",width:350,type:'select',options:[]},
{ label: "备注", prop: "remark",width:350,type: "textarea"},
],
formRules: {
supplier_id: [{ required: true, message: "请选择供应商名称", trigger: "blur" }],
good_type: [{ required: true, message: "请选择商品/规格", trigger: "blur" }],
buyer_by: [{ required: true, message: "请选择采购员", trigger: "blur" }],
},
formHandle: [
{
label: "确认",
type: "primary",
icon: "el-icon-circle-plus-outline",
handle: e => this.elFormSubmit(),
},
{
label: "取消",
icon: "el-icon-circle-close",
handle: e => this.elFormVisible(),
},
],
width:500,
height:500,
contentHeight:500,
}
},
mounted() {
let element = document.querySelector('.box-card');
this.width=element.offsetWidth
this.height=element.offsetHeight
let topEl = document.querySelector('.boxcard');
this.contentHeight=element.offsetHeight-topEl.offsetHeight
this.$http.post('/getProSku').then(res=>{
this.formCols[0].options=res.data.supplier;
this.formCols[1].options=res.data.product;
this.formCols[7].options=res.data.user;
})
this.init()
},
methods: {
beforeInventory() {
this.loadingModule = this.$loading({
lock: true,
text: '导入中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
},
inventorySuccess(response) {
this.$message({
message: '导入成功!',
type: "success",
});
this.loadingModule.close();
},
inventoryError(err) {
this.$message({
message: err.errorMessage,
type: "error",
});
this.loadingModule.close();
},
init(param={}) {
let data=Object.assign(this.pagination,param)
this.$http.get('/listPutWarehouse',data).then(res=>{
this.pagination.page=res.data.page
this.pagination.size=res.data.size
this.pagination.total=res.data.total
this.tableData=res.data.list;
console.log(this.tableData);
})
},
seach(){
this.init(this.searchData)
},
elFormUpload(e){
console.log('导入',e);
},
elFormVisible(title){
if (title==undefined) {
this.title='新增'
this.formData = { ...{} };
}
this.$refs.elForm.dialogFormTitle=this.title
this.$refs.elForm.dialogFormVisible =!this.$refs.elForm.dialogFormVisible
},
elFormChange(e){
this.title='修改'
this.formData={...e}
console.log(this.formData);
this.elFormVisible("修改");
},
elFormDelete(e){
this.$confirm('您确定要删除吗?','提示').then(()=>{
this.$http.post('/delPutWarehouse',{id:e.id}).then(res=>{
if (res.code == 200) {
this.$msg.success('删除成功!')
this.tableData=this.tableData.filter((obj)=>{
return obj.id!=e.id
})
}
})
}).catch(()=>{})
},
elFormSubmit(e) {
this.$refs.elForm.$refs.ruleForm.validate((valid) => {
if (valid) {
// console.log(this.formData);return;
this.$http.post('/savePutWarehouse', this.formData).then((res) => {
console.log(res);
if (res.code==200) {
this.$message({
type: "success",
message: `${this.title}成功!`,
duration: 1300,
onClose: (res) => {
this.elFormVisible();
this.init();
},
});
}
});
// this.treeUpload()
}
});
},
},
};
</script>

View File

@ -0,0 +1,129 @@
<template>
<div class="goods">
<div >
<el-card class="boxcard" shadow="never" >
<div slot="header" class="clearfix">
<span>{{list[0].cn_name}}</span>
</div>
<r-formItem ref="itemData0" btnRight :itemData="list[0]" :btnList="btnList0" :formRule="formRule" :formItem="formItem0"></r-formItem>
</el-card>
<el-card class="boxcard" shadow="never" >
<div slot="header" class="clearfix">
<span>{{list[1].cn_name}}</span>
</div>
<r-formItem ref="itemData1" btnRight :itemData="list[1]" :btnList="btnList1" :formRule="formRule" :formItem="formItem1"></r-formItem>
</el-card>
<el-card class="boxcard" shadow="never" >
<div slot="header" class="clearfix">
<span>{{list[2].cn_name}}</span>
</div>
<r-formItem ref="itemData2" btnRight :itemData="list[2]" :btnList="btnList2" :formRule="formRule" :formItem="formItem2"></r-formItem>
</el-card>
</div>
</div>
</template>
<script>
import index from '../../components/global/FormItem/index.vue';
export default {
data() {
return {
itemData0: {
day_num: 3,
active_time:'13:10',
active_piece: 10,
active_price: 0,
user_id:1,
},
itemData1: {
day_num: 3,
active_time:'13:10',
active_piece: 10,
active_price: 0,
user_id:1,
},
itemData2: {
day_num: 3,
active_time:'13:10',
active_piece: 10,
active_price: 0,
user_id:1,
},
formItem0: [
{labelWidth:'120',type: "time", label: "激活检查时间", prop: "active_time"},
{labelWidth:'120',type: "select", label: "管理员推送", prop: "user_id",options:[]},
],
formItem1: [
{labelWidth:'120',type: "time", label: "激活检查时间", prop: "active_time"},
{labelWidth:'120',type: "select", label: "管理员推送", prop: "user_id",options:[]},
],
formItem2: [
{labelWidth:'120',type: "time", label: "激活检查时间", prop: "active_time"},
{labelWidth:'120',type: "select", label: "管理员推送", prop: "user_id",options:[]},
],
formRule: {
minutes: [{ required: true, message: "请输入时间区间/分钟", trigger: "blur" }],
active_time: [{ required: true, message: "请选择激活检查时间", trigger: "blur" }],
user_id: [{ required: true, message: "请选择管理员推送", trigger: "blur" }],
},
btnList0: [{label: "确定",type: "primary",icon: "el-icon-setting",handle: e => this.elFormSubmit0()}],
btnList1: [{label: "确定",type: "primary",icon: "el-icon-setting",handle: e => this.elFormSubmit1()}],
btnList2: [{label: "确定",type: "primary",icon: "el-icon-setting",handle: e => this.elFormSubmit2()}],
width:500,
height:500,
contentHeight:500,
list:[],
data:[],
}
},
mounted() {
this.$http.post('/stockNoticeList').then(res=>{
this.list=res.data.list
// this.data=res.data.user
this.formItem0[1].options=res.data.user
this.formItem1[1].options=res.data.user
this.formItem2[1].options=res.data.user
})
},
methods: {
getSavestockNotice(index=0){
this.$http.post('/saveStockNotice',this.list[index]).then(res=>{
if (res.code == 200) {
this.$msg.success('修改成功!')
}else{
this.$msg.error('修改失败!')
}
})
},
elFormSubmit0(e) {
console.log(e);
this.$refs.itemData0.$refs.ruleForm.validate((valid) => {
if (valid) {
this.getSavestockNotice(0)
}
});
},
elFormSubmit1(e) {
this.$refs.itemData1.$refs.ruleForm.validate((valid) => {
if (valid) {
this.getSavestockNotice(1)
}
});
},
elFormSubmit2(e) {
this.$refs.itemData2.$refs.ruleForm.validate((valid) => {
if (valid) {
this.getSavestockNotice(2)
}
});
},
},
};
</script>

View File

@ -0,0 +1,232 @@
<template>
<div class="goods">
<el-card class="boxcard" shadow="never">
<r-search ref="searchalt" :searchData="searchData" :searchForm="searchForm" :searchHandle="searchHandle" />
</el-card>
<div :style="{height:`${this.contentHeight}px`}" style="padding-top:30px;">
<r-table class="page" :isIndex="false" isHandle :tableData="tableData" :tableCols="tableCols" :tableHandles="tableHandles" :isPagination="true" :tablePage="pagination" @refresh="init()">
<el-upload slot="tip" ref="newset" action="/api/uploadStockTacks" :multiple="false" name="file"
:show-file-list="false" :on-success="inventorySuccess" :before-upload="beforeInventory"
:on-error="inventoryError" style="display:inline-block;margin: 0 10px 0 10px;"
:headers="headers">
<el-button type="primary" plain>报损导入</el-button>
</el-upload>
</r-table>
</div>
<r-form
:isHandle="true"
:formRules="formRules"
:formCols="formCols"
:formHandle="formHandle"
:formData="formData"
labelWidth="100px"
dialogWidth="550px"
:inline="false"
ref="elForm"
/>
</div>
</template>
<script>
export default {
data() {
return {
load:false,
flag:false,
title:'',
param:{},
supplierType:[],
pagination: { size: 10, page: 1, total: 0 },
headers: {
'Authorization': 'Bearer ' + localStorage.getItem("token")
},
searchData: {
keyWord:"",
},
searchForm: [
{ type: "input",label: "关键词", prop: "keyWord",placeholder:'商品名、规格、品牌',width:500},
],
searchHandle: [
{type: "info",icon: "el-icon-refresh", handle: e => this.init(true) },
{label: "搜索",type: "primary",icon: "el-icon-search",handle: e => this.seach()},
],
tableData: [],
formCols: [
{ label: "商品/规格", prop: "good_type",width:350,type:'cascader',options:[]},
{ label: "报损数量", prop: "stock",width:350,type:'number'},
{ label: "采购成本", prop: "buyer_price",width:350,type:'number'},
{ label: "入库成本", prop: "price",width:350,type:'number'},
{ label: "报损日期", prop: "tack_date",width:350,type:'date'},
{ label: "采购员", prop: "buyer_by",width:350,type:'select',options:[]},
{ label: "备注", prop: "remark",width:350,type: "textarea"},
],
tableCols:[
{ label: "报损日期", prop: "tack_date",width:150},
{ label: "商品类型", prop: "type_name",width:150},
{ label: "品牌", prop: "brand_name",width:150},
{ label: "商品名称", prop: "good_name",width:150},
{ label: "商品sku", prop: "sku_name",width:200},
{ label: "采购数量", prop: "stock",width:200},
{ label: "采购成本", prop: "buyer_price",width:150},
{ label: "入库成本", prop: "price",width:150},
{ label: "采购员", prop: "username",width:200},
{ label: "备注", prop: "remark",width:250},
{ label: "创建时间", prop: "created_at",width:200},
{ label: "操作",type: "button",width: 300,
btnList: [
// {label: "",type: "primary",size: "mini",handle: (row) => this.elFormChange(row)},
{label: "删除",type: "danger",size: "mini",handle: (row) => this.elFormDelete(row)},
]
},
],
tableHandles: [
{
label: "新增报损",
type: "primary",
handle: e => {this.elFormVisible(e)} ,
},
],
formData: {
good_type:[],
stock:'',
buyer_price:'',
price:'',
tack_date:"",
buyer_by:"",
remark:"",
},
formRules: {
},
formHandle: [
{
label: "确认",
type: "primary",
icon: "el-icon-circle-plus-outline",
handle: e => this.elFormSubmit(),
},
{
label: "取消",
icon: "el-icon-circle-close",
handle: e => this.elFormVisible(),
},
],
width:500,
height:500,
contentHeight:500,
}
},
mounted() {
let element = document.querySelector('.box-card');
this.width=element.offsetWidth
this.height=element.offsetHeight
let topEl = document.querySelector('.boxcard');
this.contentHeight=element.offsetHeight-topEl.offsetHeight
this.$http.post('/getProSku').then(res=>{
this.formCols[0].options=res.data.product;
this.formCols[5].options=res.data.user;
})
this.init()
},
methods: {
init(param={}) {
// console.log(localStorage.getItem("token"));return;
// console.log(this.$store.state.token);return;
let data=Object.assign(this.pagination,param)
this.$http.get('/listStockTacks',data).then(res=>{
this.pagination.page=res.data.page
this.pagination.size=res.data.size
this.pagination.total=res.data.total
this.tableData=res.data.list;
console.log(this.tableData);
})
},
beforeInventory() {
this.loadingModule = this.$loading({
lock: true,
text: '导入中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
},
inventorySuccess(response) {
this.$message({
message: '导入成功!',
type: "success",
});
this.loadingModule.close();
},
inventoryError(err) {
this.$message({
message: err.errorMessage,
type: "error",
});
this.loadingModule.close();
},
seach(){
this.init(this.searchData)
},
elFormUpload(e){
console.log('导入',e);
},
elFormVisible(title){
if (title==undefined) {
this.title='新增'
this.formData = { ...{} };
}
this.$refs.elForm.dialogFormTitle=this.title
this.$refs.elForm.dialogFormVisible =!this.$refs.elForm.dialogFormVisible
},
elFormChange(e){
this.title='修改'
this.formData={...e}
console.log(this.formData);
this.elFormVisible("修改");
},
elFormDelete(e){
this.$confirm('您确定要删除吗?','提示').then(()=>{
this.$http.post('/delStockTacks',{id:e.id}).then(res=>{
if (res.code == 200) {
this.$msg.success('删除成功!')
this.tableData=this.tableData.filter((obj)=>{
return obj.id!=e.id
})
}
})
}).catch(()=>{})
},
elFormSubmit(e) {
this.$refs.elForm.$refs.ruleForm.validate((valid) => {
if (valid) {
// console.log(this.formData);return;
this.$http.post('/saveStockTacks', this.formData).then((res) => {
console.log(res);
if (res.code==200) {
this.$message({
type: "success",
message: `${this.title}报损成功!`,
duration: 1300,
onClose: (res) => {
this.elFormVisible();
this.init();
},
});
}
});
// this.treeUpload()
}
});
},
},
};
</script>

View File

@ -0,0 +1,123 @@
<template>
<div>
<r-search ref="searchalt" :searchData="searchData" :searchForm="searchForm" :searchHandle="searchHandle" />
<r-table :isIndex="true" :tableData="tableData" :tableCols="tableCols" v-loading="load" :isPagination="true" :tablePage="pagination" @refresh="init()" />
<r-form dialogWidth="500px" labelWidth="120px" :isHandle="true" :formCols="formCols" :formHandle="formHandle" :formRules="formRules" :formData="formData" ref="elForm" />
</div>
</template>
<script>
export default {
data () {
return {
list:[],
load:false,
pagination: { size: 10, page: 1, total: 0 },
searchData: {
keyWord:"",
batteryStatus:'-1',
batteryLockStatus:'-1',
batteryModelId:'',
},
searchForm: [
{ type: "input",label: "关键词", prop: "keyWord",placeholder:'客户名称、电池编号、使用人'},
{ type: "select",label: "电池状态", prop: "batteryStatus",options:[{label:'正常',value:'0'},{label:'故障',value:'1'},{label:'全部',value:'-1'}]},
{ type: "select",label: "电池锁定状态", prop: "batteryLockStatus",options:[{label:'正常',value:'0'},{label:'锁定',value:'1'},{label:'全部',value:'-1'}]},
],
searchHandle: [
{type: "info",icon: "el-icon-refresh", handle: e => this.init(true) },
{label: "搜索",type: "primary",icon: "el-icon-search",handle: e => this.seach()},
],
tableData: [],
tableCols: [
{ label: "电池编号", prop: "batteryNo",width:200},
{ label: "电池型号", prop: "batteryModelName",width:150},
{ label: "是否备件库", prop: "isSpares",formatter:e=>e.isSpares?'是':'否',width:120},
{ label: "所属订单", prop: "byOrderCode",width:150},
{ label: "所属出库单号", prop: "byStockCode",width:150},
{ label: "客户类型", prop: "customerType",width:150,formatter:e=>e.customerType==0?'经销商':'代理商',},
{ label: "所属客户", prop: "customerName",width: 200},
{ label: "所属代理", prop: "parentCustomerName",width:200},
{ label: "使用人", prop: "usePersionName",width:120},
{ label: "电池状态", prop: "batteryStatus",formatter:e=>e.batteryStatus==0?'正常':'故障'},
{ label: "电池锁定状态", prop: "batteryLockStatus",formatter:e=>e.batteryLockStatus==0?'正常':'已锁定',width:120,isDisabled:e=>e.batteryLockStatus?true:false},
{ label: "操作",type: "button",width: 300,
btnList: [
{label: "手动解锁",type: "primary",size: "mini",isShow:e=>e.batteryLockStatus==1?true:false,handle: (row) => this.getLock(row)},
{label: "查看备件更换记录",type: "danger",size: "mini",handle: (row) => this.getChange(row)},
]
},
],
formCols: [
],
formRules:{
},
formData: {
},
formHandle: [
],
}
},
mounted () {
},
created() {
this.$http.post('/battery/model/list').then(res=>{
let lst=res.data;
let arr=[]
lst.forEach((obj)=>{
let list={label:obj.batteryModel,value:obj.id}
arr.push(list)
});
this.searchForm.push({type:'select', label: "电池型号", prop: "batteryModelId",options:arr})
})
this.init();
},
methods: {
init(isref=false,param={}) {
// this.load=true;
let data=Object.assign(this.pagination,param)
this.$http.get('/battery/getBatteryListByCustomer',data).then(res=>{
console.log(res.data);
// this.pagination.pageSize=res.data.pageSize
// this.pagination.page=res.data.currPage
// this.pagination.total=res.data.totalCount
// this.tableData=res.data.list;
})
},
seach(){
this.init(false,this.searchData)
},
getLock(e){
console.log(e);
this.$confirm('您确定要解锁吗?','提示').then(()=>{
this.$http.post('/battery/settinglock',{id:String(e.id)}).then(res=>{
if (res.data==1) {
this.$msg.success('解锁成功!')
this.init()
}else{
this.$msg.error(res.msg)
}
})
})
},
getChange(e){
this.$router.push({
name: 'battery-replace',
query:{
row:JSON.stringify({batteryNo:String(e.batteryNo)}),
}
});
},
}
}
</script>
<style>
</style>

View File

@ -1,7 +1,247 @@
<template>
<div>
<el-row type="flex" justify="end">
<el-col :offset="24"><el-button type="success">新增</el-button></el-col>
</el-row>
<div class="goods">
<el-card class="boxcard" shadow="never">
<r-search ref="searchalt" :searchData="searchData" :searchForm="searchForm" :searchHandle="searchHandle" />
</el-card>
<div :style="{height:`${this.contentHeight}px`}" style="padding-top:30px;">
<r-table class="page" :isIndex="false" isHandle :tableData="tableData" :tableCols="tableCols" :tableHandles="tableHandles" :isPagination="true" :tablePage="pagination" @refresh="init()" />
</div>
<r-form
:isHandle="true"
:formRules="formRules"
:formCols="formCols"
:formHandle="formHandle"
:formData="formData"
labelWidth="100px"
dialogWidth="550px"
:inline="false"
ref="elForm"
/>
</div>
</template>
</template>
<script>
export default {
data() {
return {
load:false,
flag:false,
title:'',
param:{},
supplierType:[],
pagination: { size: 10, page: 1, total: 0 },
searchData: {
keyWord:"",
status:'-1'
},
searchForm: [
{ type: "input",label: "关键词", prop: "keyWord",placeholder:'供应商名称'},
{ type: "select",label: "状态", prop: "status",options:[{label:'禁用',value:'0'},{label:'正常',value:'1'},{label:'全部',value:'-1'}]}
],
searchHandle: [
{type: "info",icon: "el-icon-refresh", handle: e => this.init(true) },
{label: "搜索",type: "primary",icon: "el-icon-search",handle: e => this.seach()},
],
tableData: [],
tableCols:[
{ label: "供应商名称", prop: "supplier_name",width:200},
{ label: "所在类别", prop: "supplier_type",width:150},
{ label: "拥有品类", prop: "type_name",width:200},
{ label: "对公银行", prop: "bank",width:150},
{ label: "对公账号", prop: "bank_num",width:200},
{ label: "联系人", prop: "username",width:150},
{ label: "手机号码", prop: "phone",width:150},
{ label: "公司座机", prop: "tel",width:150},
{ label: "微信号码", prop: "wx",width:150},
{ label: "公司地址", prop: "address",width:200},
{ label: "地址邮编", prop: "code",width:150},
{ label: "状态", prop: "status",formatter:e=>e.status==1?'正常':'禁用'},
{ label: "备注", prop: "remark",width:250},
{ label: "创建时间", prop: "created_at",width:200},
{ label: "操作",type: "button",width: 300,
btnList: [
{label: "修改",type: "primary",size: "mini",handle: (row) => this.elFormChange(row)},
{label: "删除",type: "danger",size: "mini",handle: (row) => this.elFormDelete(row)},
]
},
],
tableHandles: [
{
label: "新增供应商",
type: "primary",
handle: e => {this.elFormVisible(e)} ,
},
],
// pagination: { limit: 10, offset: 1, total: 1 },
formData: {
supplier_name: "",
type_id:'',
good_type_id:'',
bank:"",
bank_num:"",
username:"",
phone:"",
tel:"",
wx:"",
address:"",
code:"",
status:1,
remark:''
},
formCols: [
{type: "input", label: "供应商名称", prop: "supplier_name",width:350},
{type: "select", label: "所在类别", prop: "type_id",width:350,options:[]},
{type: "select", label: "品类", prop: "good_type_id",width:350,options:[]},
{type: "input", label: "对公银行", prop: "bank",width:350},
{type: "input", label: "对公账号", prop: "bank_num",width:350},
{type: "input", label: "联系人", prop: "username",width:350},
{type: "input", label: "手机号码", prop: "phone",width:350},
{type: "input", label: "公司座机", prop: "tel",width:350},
{type: "input", label: "微信号码", prop: "wx",width:350},
{type: "input", label: "公司地址", prop: "address",width:350},
{type: "input", label: "公司邮编", prop: "code",width:350},
{type: "select", label: "状态", prop: "status",width:350,options:[{label:'正常',value:1},{label:'禁用',value:0}]},
{type: "textarea", label: "备注", prop: "remark",width:350},
],
formRules: {
supplier_name: [{ required: true, message: "请输入供应商名称", trigger: "blur" }],
type_id: [{ required: true, message: "请选择所在类别", trigger: "blur" }],
good_type_id: [{ required: true, message: "请选择品类", trigger: "blur" }],
},
formHandle: [
{
label: "确认",
type: "primary",
icon: "el-icon-circle-plus-outline",
handle: e => this.elFormSubmit(),
},
{
label: "取消",
icon: "el-icon-circle-close",
handle: e => this.elFormVisible(),
},
],
width:500,
height:500,
contentHeight:500,
}
},
mounted() {
let element = document.querySelector('.box-card');
this.width=element.offsetWidth
this.height=element.offsetHeight
let topEl = document.querySelector('.boxcard');
this.contentHeight=element.offsetHeight-topEl.offsetHeight
// },
// console.log(el);
// window.addEventListener('resize', this.handleResize);
// const element = this.$refs['box-card'];
// console.log(element);
// element.addEventListener('resize', () => {
// this.handleResize();
// });
this.$http.post('/getSupplierType').then(res=>{
this.supplierType=res.data
let index=this.formCols.findIndex((res,index)=>{
return res.prop=='type_id'
})
this.formCols[index].options=res.data.type
let index2=this.formCols.findIndex((res,index)=>{
return res.prop=='good_type_id'
})
this.formCols[index2].options=res.data.good
})
this.init()
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
},
methods: {
handleResize() {
// 访 this.$el
const elementClass = this.$el.boxcard;
console.log('页面大小变化,当前类名:', elementClass);
//
},
init(param={}) {
// this.load=true;listSupplier
let data=Object.assign(this.pagination,param)
this.$http.get('/listSupplier',data).then(res=>{
this.pagination.page=res.data.page
this.pagination.size=res.data.size
this.pagination.total=res.data.total
this.tableData=res.data.list;
console.log(this.tableData);
})
},
seach(){
this.init(this.searchData)
},
elFormVisible(title){
if (title==undefined) {
this.title='新增'
this.formData = { ...{status:1} };
}
this.$refs.elForm.dialogFormTitle=this.title
this.$refs.elForm.dialogFormVisible =!this.$refs.elForm.dialogFormVisible
},
elFormChange(e){
this.title='修改'
this.formData={...e}
this.elFormVisible("修改");
},
elFormDelete(e){
this.$confirm('您确定要删除吗?','提示').then(()=>{
this.$http.post('/delSupplier',{id:e.id}).then(res=>{
if (res.code == 200) {
this.$msg.success('删除成功!')
this.tableData=this.tableData.filter((obj)=>{
return obj.id!=e.id
})
}
})
}).catch(()=>{})
// this.$confirm(', ?', '', {
// confirmButtonText: '',
// cancelButtonText: '',
// type: 'warning'
// }).then(() => {
// this.$message.success('!');
// //
// }).catch(() => {
// this.$message.info('');
// });
},
elFormSubmit(e) {
this.$refs.elForm.$refs.ruleForm.validate((valid) => {
if (valid) {
console.log(this.formData);
this.$http.post('/saveSupplier', this.formData).then((res) => {
console.log(res);
this.$message({
type: "success",
message: `${this.title}成功!`,
duration: 1300,
onClose: (res) => {
this.elFormVisible();
this.init();
},
});
});
// this.treeUpload()
}
});
},
},
};
</script>

View File

@ -7,8 +7,8 @@ module.exports = {
proxy: {
// 配置代理
"/api": {
// target: "http://erp.local",
target: "http://erp.chutang66.com",
target: "https://erp.lookthere.cn",
// target: "http://erp.chutang66.com",
changeOrigin: true, // 开启代理
pathRewrite: {
// 重命名

View File

@ -477,4 +477,67 @@ return [
'name' => '销售数据列表',
'parent_id' => 180,
],
// 供应商管理
'supplier' => [
'id' => 181,
'name' => '供应商管理',
'parent_id' => 0,
'show' => 1,
],
// 供应商管理--
'supplierList' => [
'id' => 182,
'name' => '供应商列表',
'parent_id' => 181,
'show' => 1,
],
// 库存管理
'stock' => [
'id' => 183,
'name' => '库存管理',
'parent_id' => 0,
'show' => 1,
],
'putWarehouse' => [
'id' => 184,
'name' => '商品入库',
'parent_id' => 183,
'show' => 1,
],
'stockNotice' => [
'id' => 185,
'name' => '库存预警',
'parent_id' => 183,
'show' => 1,
],
'stockTacks' => [
'id' => 186,
'name' => '盘点报损',
'parent_id' => 183,
'show' => 1,
],
'message' => [
'id' => 187,
'name' => '消息列表',
'parent_id' => 0,
'show' => 1,
],
'dataChart' => [
'id' => 188,
'name' => '数据报表',
'parent_id' => 0,
'show' => 1,
],
'salesData' => [
'id' => 189,
'name' => '销售数据',
'parent_id' => 188,
'show' => 1,
],
'afterSales' => [
'id' => 190,
'name' => '售后数据',
'parent_id' => 188,
'show' => 1,
],
];

View File

@ -12,7 +12,10 @@ use App\Http\Controllers\Group\GroupsController;
use App\Http\Controllers\Business\BusinessOrderController;
use App\Http\Controllers\Goods\GoodsSkuLocationController;
use App\Http\Controllers\Goods\GoodsCombinationController;
use App\Http\Controllers\Supplier\SupplierController;
use App\Http\Controllers\Warehouse\WarehouseController;
use App\Http\Controllers\Warehouse\StockNoticeControllers;
use App\Http\Controllers\DataCenter\DataChartControllers;
/*
|--------------------------------------------------------------------------
| API Routes
@ -23,7 +26,11 @@ use App\Http\Controllers\Goods\GoodsCombinationController;
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::get('test', [ShopsController::class, 'test']);
Route::middleware(['auth:api', 'check.permissions'])->group(function () {
// $api->post('index', 'IndexController@IndexHomeQueue');
// 用户
Route::resource('users', 'User\UsersController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]);
// 商品种类
@ -73,6 +80,7 @@ Route::middleware(['auth:api', 'check.permissions'])->group(function () {
// 数据中心
Route::get('data_center/sales_report', [DataCenterController::class, 'salesReport'])->name('sales_report.index');
});
Route::get('stock/goods_skus', [GoodsSkusController::class, 'stockNum'])->middleware('auth:api');
Route::get('goods/filter/{title}', [GoodsCombinationController::class, 'goodsSkus'])->middleware('auth:api');
// 登录
@ -99,6 +107,11 @@ Route::post('business', [ShopsController::class, 'business'])->name('shop.put.bu
Route::post('inventory/goods_skus', [GoodsSkusController::class, 'inventoryImport'])->name('goods_sku.inventory');
// 上新导入
Route::post('new/set/goods_skus', [GoodsSkusController::class, 'newSetImport'])->name('goods_sku.new_set');
// 报损导入
Route::post('uploadStockTacks', [WarehouseController::class, 'uploadStockTacks'])->middleware('auth:api');
// 入库导入
Route::post('uploadPutWarehouse', [WarehouseController::class, 'uploadPutWarehouse'])->middleware('auth:api');
// 商品货架导入
Route::post('goods_sku_location', [GoodsSkuLocationController::class, 'import'])->name('goods_sku_location.import');
// 组合商品导入
@ -107,5 +120,42 @@ Route::post('combination/goods', [GoodsCombinationController::class, 'import'])-
Route::post('today/price', [BusinessGoodsSkusController::class, 'todayPriceImport'])->name('plat.today_price.import');
// 文件上传
Route::post('upload', [UploadController::class, 'store'])->name('upload.file');
// 商品列表
Route::get('goodsSkusList', [GoodsSkusController::class, 'goodsSkusList'])->name('goods_sku.list_for_goods_sku');
/*
* 供应商管理
*
* */
Route::get('listSupplier', [SupplierController::class, 'index'])->middleware('auth:api');
Route::post('saveSupplier', [SupplierController::class, 'save'])->middleware('auth:api');
Route::post('delSupplier', [SupplierController::class, 'delete'])->middleware('auth:api');
Route::post('getSupplierType', [SupplierController::class, 'getSupplierType'])->middleware('auth:api');
/*
* 库存管理
*
* */
Route::post('getProSku', [WarehouseController::class, 'getProSku'])->middleware('auth:api');
Route::post('savePutWarehouse', [WarehouseController::class, 'savePutWarehouse'])->middleware('auth:api');
Route::get('listPutWarehouse', [WarehouseController::class, 'listPutWarehouse'])->middleware('auth:api');
Route::post('delPutWarehouse', [WarehouseController::class, 'delPutWarehouse'])->middleware('auth:api');
Route::post('stockNoticeList', [StockNoticeControllers::class, 'stockNoticeList'])->middleware('auth:api');
Route::post('saveStockNotice', [StockNoticeControllers::class, 'saveStockNotice'])->middleware('auth:api');
Route::post('getNoticeList', [StockNoticeControllers::class, 'getNoticeList'])->middleware('auth:api');
Route::post('getSaveRead', [StockNoticeControllers::class, 'getSaveRead'])->middleware('auth:api');
//报损
Route::post('saveStockTacks', [WarehouseController::class, 'saveStockTacks'])->middleware('auth:api');
Route::post('delStockTacks', [WarehouseController::class, 'delStockTacks'])->middleware('auth:api');
Route::get('listStockTacks', [WarehouseController::class, 'listStockTacks'])->middleware('auth:api');
Route::post('goodChart', [DataChartControllers::class, 'goodChart'])->middleware('auth:api');
Route::post('damageChart', [DataChartControllers::class, 'damageChart'])->middleware('auth:api');
//销售图表
Route::post('salesChart', [DataChartControllers::class, 'salesChart'])->middleware('auth:api');
//交易趋势
Route::post('flagChart', [DataChartControllers::class, 'flagChart'])->middleware('auth:api');
//成本数据
Route::post('costChart', [DataChartControllers::class, 'costChart'])->middleware('auth:api');
Route::post('getOrderServerData', [DataChartControllers::class, 'getOrderServerData'])->middleware('auth:api');

View File

@ -18,7 +18,8 @@ use App\Http\Controllers\Business\BusinessGoodsSkusController;
*/
Route::get('/', function () {
header('Location: ' . url()->current() . "/dist");
// header('Location: ' . url()->current() . "/dist");
return "hello";
});
Route::get('goods_skus/export', [GoodsSkusController::class, 'export'])->name('goods_skus.export');