Просмотр исходного кода

Merge branch 'Haozi' of ssh://was.baoshi56.com:10022/var/git/bswas

LD 5 лет назад
Родитель
Сommit
286ba5dcf6
31 измененных файлов с 1561 добавлено и 1 удалено
  1. 81 0
      app/Demand.php
  2. 27 0
      app/DemandProcess.php
  3. 63 0
      app/Filters/DemandFilters.php
  4. 10 0
      app/Http/Controllers/AuthorityController.php
  5. 171 0
      app/Http/Controllers/DemandController.php
  6. 52 0
      app/Http/Controllers/DemandProcessController.php
  7. 8 0
      app/Http/Controllers/TestController.php
  8. 81 0
      app/Http/Requests/Demand/DemandRequest.php
  9. 50 0
      app/Http/Requests/Demand/Process/DemandProcessRequest.php
  10. 5 1
      app/Providers/AppServiceProvider.php
  11. 13 0
      app/Services/DemandProcessService.php
  12. 105 0
      app/Services/DemandService.php
  13. 18 0
      database/factories/DemandFactory.php
  14. 15 0
      database/factories/DemandProcessFactory.php
  15. 37 0
      database/migrations/2021_04_02_160659_create_demands_table.php
  16. 34 0
      database/migrations/2021_04_02_162507_create_demand_processes_table.php
  17. 40 0
      database/migrations/2021_04_06_092844_create_demand_authorities.php
  18. 17 0
      database/seeds/DemandProcessSeeder.php
  19. 18 0
      database/seeds/DemandSeeder.php
  20. 14 0
      resources/sass/layout.scss
  21. 99 0
      resources/views/demand/_create.blade.php
  22. 88 0
      resources/views/demand/_createjs.blade.php
  23. 10 0
      resources/views/demand/menu.blade.php
  24. 87 0
      resources/views/demand/search/_table.blade.php
  25. 30 0
      resources/views/demand/search/_uploadFile.blade.php
  26. 347 0
      resources/views/demand/search/index.blade.php
  27. 10 0
      resources/views/demand/search/menu.blade.php
  28. 2 0
      resources/views/layouts/app.blade.php
  29. 5 0
      resources/views/layouts/menu.blade.php
  30. 17 0
      routes/apiLocal.php
  31. 7 0
      routes/web.php

+ 81 - 0
app/Demand.php

@@ -0,0 +1,81 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelLogChanging;
+use App\Traits\ModelTimeFormat;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Database\Eloquent\Relations\HasOne;
+use Illuminate\Database\Eloquent\Builder;
+
+class Demand extends Model
+{
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    protected $fillable = ['authority_id', 'initiator', 'description', 'type', 'status', 'handler'];
+
+    // 需求类型
+    protected static $type = ['需求', '问题'];
+
+    // 状态
+    protected static $status = ['未处理', '处理中', '已处理'];
+
+
+    public function authority(): BelongsTo
+    {
+        return $this->belongsTo(Authority::class);
+    }
+
+    public function initiator(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'initiator', 'id');
+    }
+
+    public function handle(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'handler', 'id');
+    }
+
+    public function uploadFile(): HasOne
+    {
+        return $this->hasOne(UploadFile::class, 'table_id', 'id')->where('table_name', 'Demands');
+    }
+
+    public function processes(): HasMany
+    {
+        return $this->hasMany(DemandProcess::class, 'demand_id', 'id');
+    }
+
+    public function scopeFilter($query, $filters)
+    {
+        return $filters->apply($query);
+    }
+
+    /**
+     * 保存文件创建对应的文件对象
+     *
+     * @param $fileName
+     * @param  $fileSuffix
+     * @return Builder|Model
+     */
+    public function saveFile($fileName,$fileSuffix)
+    {
+        return UploadFile::query()->create(['table_name' => $this->getTable(), 'table_id' => $this['id'], 'url' => '/files/issue/'.$fileName, 'type' => $fileSuffix]);
+    }
+
+    /**
+     * 删除需求的同时删除处理过程
+     *
+     * @return bool|null
+     * @throws \Exception
+     */
+    public function delete()
+    {
+        $this->processes()->delete();
+        return parent::delete();
+    }
+
+}

+ 27 - 0
app/DemandProcess.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelLogChanging;
+use App\Traits\ModelTimeFormat;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class DemandProcess extends Model
+{
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    protected $fillable = ['demand_id', 'processor', 'explain'];
+
+    public function demand(): BelongsTo
+    {
+        return $this->belongsTo(Demand::class);
+    }
+
+    public function user(): BelongsTo
+    {
+        return $this->belongsTo(User::class, 'processor', 'id');
+    }
+
+}

+ 63 - 0
app/Filters/DemandFilters.php

@@ -0,0 +1,63 @@
+<?php
+
+
+namespace App\Filters;
+
+
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+class DemandFilters
+{
+    protected $request;
+    protected $queryBuilder;
+    protected $filters = ['created_at_start', 'created_at_end', 'type'];
+    protected $array_filter;
+    protected $params = [];
+
+    public function __construct(Request $request)
+    {
+        $this->request = $request;
+        $this->params = $request->all();
+        $this->array_filter = array_filter($this->request->only($this->filters));
+    }
+
+    public function apply($builder)
+    {
+        $this->queryBuilder = $builder;
+        $this->before();
+        foreach ($this->array_filter as $filter => $value) {
+            if (method_exists($this, $filter)) {
+                $this->$filter($value, $this->queryBuilder);
+            }
+        }
+        return $this->queryBuilder;
+    }
+
+    public function created_at_start($created_at)
+    {
+        $this->queryBuilder->where('demands.created_at', '>=', $created_at . ' 00:00:00');
+    }
+
+    public function created_at_end($created_at)
+    {
+        $this->queryBuilder->where('demands.created_at', '<=', $created_at . ' 23:59:59');
+    }
+
+    public function type($type)
+    {
+        $this->queryBuilder->where('demands.type',$type);
+    }
+
+    public function before()
+    {
+        $user = Auth::user();
+        if($user->isSuperAdmin()){
+            return;
+        }
+        $authorities = $user->authorities();
+        $authorities = data_get($authorities,'*.id');
+        $authorities[] = null;
+        $this->queryBuilder->whereIn('authority_id',$authorities);
+    }
+}

+ 10 - 0
app/Http/Controllers/AuthorityController.php

@@ -3,16 +3,20 @@
 namespace App\Http\Controllers;
 
 use App\Authority;
+use App\Components\AsyncResponse;
 use App\Owner;
 use Exception;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
+use Illuminate\Queue\RedisQueue;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Gate;
 use Illuminate\Support\Facades\Validator;
 
 class AuthorityController extends Controller
 {
+    use AsyncResponse;
+
     /**
      * Display a listing of the resource.
      *
@@ -159,4 +163,10 @@ class AuthorityController extends Controller
         $re=$authority->delete();
         return ['success'=>$re];
     }
+
+    public function getAuthoritiesApi()
+    {
+        $authority = Authority::query()->orderBy('name')->get();
+        $this->success($authority);
+    }
 }

+ 171 - 0
app/Http/Controllers/DemandController.php

@@ -0,0 +1,171 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Demand;
+use App\Components\AsyncResponse;
+use App\Filters\DemandFilters;
+use App\Http\Requests\Demand\DemandRequest;
+use App\Services\DemandService;
+use App\UploadFile;
+use Illuminate\Http\Request;
+use Illuminate\Contracts\Foundation\Application;
+use Illuminate\Contracts\View\Factory;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\View\View;
+
+class DemandController extends Controller
+{
+
+    use AsyncResponse;
+
+    /**
+     * @param Request $request
+     * @param DemandFilters $filters
+     * @return Application|Factory|View
+     */
+    public function index(Request $request, DemandFilters $filters)
+    {
+        // 查询权限
+
+        $demands = Demand::query()->with(['initiator', 'handle', 'uploadFile', 'processes.user'])->filter($filters)->orderByDesc('demands.id')->paginate($request['paginate'] ?? 50);
+
+        return view('demand.search.index', compact('demands'));
+    }
+
+    /**
+     * @param DemandRequest $request
+     * @param DemandService $service
+     */
+    public function storeApi(DemandRequest $request, DemandService $service)
+    {
+        /** @var Demand $demand */
+        $params = $request->all();
+        $params['initiator'] = Auth::user()['id'];
+
+        $demand = Demand::query()->create($params);
+
+        if ($demand) {
+
+            $file = $request->file('file');
+            if (isset($file)) $service->saveUPLoadFile($demand, $file);
+
+            $this->success($demand);
+        } else {
+            $this->error('需求创建出现异常');
+        }
+    }
+
+    /**
+     * @param DemandRequest $request
+     */
+    public function updateApi(DemandRequest $request)
+    {
+        $demand = Demand::query()->where('id',$request['id'])->first();
+
+        if($demand['initiator'] != Auth::user()['id'])
+            $this->error('非当前需求创建人不可修改');
+
+        $bool = $demand->update($request->all());
+
+        if ($bool) {
+            $demand->loadMissing('authority', 'initiator', 'handle', 'uploadFile', 'processes');
+            $this->success($demand);
+        }
+        $this->error('需求更新出现异常');
+    }
+
+    /**
+     * @param DemandRequest $request
+     * @param DemandService $service
+     */
+    public function uploadFileApi(DemandRequest $request, DemandService $service)
+    {
+        /** @var Demand $demand */
+        $demand = Demand::query()->where('id',$request['id'])->first();
+
+        $service->saveUPLoadFile($demand, $request->file('file'));
+
+        $demand->loadMissing('authority', 'initiator', 'handle', 'uploadFile', 'processes');
+
+        $this->success($demand);
+    }
+
+    /**
+     * @param DemandRequest $request
+     */
+    public function destroyFileApi(DemandRequest $request)
+    {
+        /** @var Demand $demand */
+        $demand = Demand::query()->where('id',$request['id'])->first();
+
+        /** @var UploadFile $uploadFile */
+        $uploadFile = $demand->uploadFile();
+
+        try {
+            $bool = $uploadFile->delete();
+            if($bool)$this->success();
+            $this->error('删除出现异常');
+        } catch (\Exception $e) {
+            $this->error($e->getMessage());
+        }
+
+    }
+
+
+    /**
+     * 删除需求
+     *
+     * @param DemandRequest $request
+     */
+    public function destroyApi(DemandRequest $request)
+    {
+        $this->gate('需求管理-问题-删除');
+
+        $demand = Demand::query()->find($request['id']);
+        try {
+            $bool = $demand->delete();
+            if ($bool) $this->success();
+            else $this->error('删除失败');
+        } catch (\Exception $e) {
+            $this->error($e->getMessage());
+        }
+    }
+
+    /**
+     * 完结需求
+     *
+     * @param DemandRequest $request
+     * @param DemandService $service
+     */
+    public function finishApi(DemandRequest $request,DemandService $service)
+    {
+
+        $demand = Demand::query()->where('id',$request['id'])->first();
+        $result = $service->finishDemand($demand);
+        if($result['success']){
+            $this->success($result['data']);
+        }
+        $this->error($result['message']);
+    }
+
+    /**
+     * 需求认领
+     *
+     * @param DemandRequest $request
+     * @param DemandService $service
+     */
+    public function claimApi(DemandRequest $request,DemandService $service)
+    {
+        /** @var Demand  $demand */
+        $demand = Demand::query()->where('id',$request['id'])->first();
+        $handler = Auth::user()['id'];
+
+        $result = $service->claimDemand($demand,$handler);
+        if($result['success']){
+            $this->success($result['data']);
+        }
+        $this->error($result['message']);
+    }
+
+}

+ 52 - 0
app/Http/Controllers/DemandProcessController.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Components\AsyncResponse;
+use App\DemandProcess;
+use App\Http\Requests\Demand\Process\DemandProcessRequest;
+use Illuminate\Support\Facades\Auth;
+
+class DemandProcessController extends Controller
+{
+    use AsyncResponse;
+
+    /**
+     * 添加处理记录
+     *
+     * @param DemandProcessRequest $request
+     */
+    public function storeApi(DemandProcessRequest $request)
+    {
+        $params = $request->all();
+        $params['processor'] = Auth::user()['id'];
+
+        $demandProcess = DemandProcess::query()->create($params);
+
+        $demandProcess->loadMissing('user');
+
+        $this->success($demandProcess);
+    }
+
+    /**
+     * @param DemandProcessRequest $request
+     * @throws \Exception
+     */
+    public function destroyApi(DemandProcessRequest  $request)
+    {
+
+        $demandProcess = DemandProcess::query()->where('id',$request['id'])->first();
+
+        if($demandProcess['processor']!==Auth::user()['id'])$this->error('非当前过程创建人不可删除');
+
+        try {
+            $bool = $demandProcess->delete();
+            if($bool)$this->success();
+            $this->error('删除失败');
+        } catch (\Exception $e) {
+            $this->error('删除异常'.$e->getMessage());
+        }
+
+
+    }
+}

+ 8 - 0
app/Http/Controllers/TestController.php

@@ -1269,4 +1269,12 @@ where purch.islower=1 and deliver.id>'.$id);
         }
         ProcurementCheckSheet::query()->insert($insert_);
     }
+
+    public function testAuth()
+    {
+        $user = Auth::user();
+        $authorities = $user->authorities();
+        $authorities = data_get($authorities,'*.id');
+        dd($authorities);
+    }
 }

+ 81 - 0
app/Http/Requests/Demand/DemandRequest.php

@@ -0,0 +1,81 @@
+<?php
+
+
+namespace App\Http\Requests\Demand;
+
+use App\Traits\RequestApiFormValidation;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Support\Facades\Request;
+use Illuminate\Support\Facades\Route;
+
+class DemandRequest extends FormRequest
+{
+    use RequestApiFormValidation;
+
+    protected $storeApiRules = [
+        'description' => 'required',
+        'type' => 'required|integer',
+    ];
+
+    protected $storeApiMessage = [
+        'description.required' => '需求描述为必填项',
+        'type.required' => '需求类型为必填项',
+    ];
+
+    protected $updateApiRules = [];
+
+    protected $updateApiMessage = [];
+
+    protected $uploadFileApiRules = [
+        'file' => 'required|file|image'
+    ];
+
+    protected $uploadFileApiMessage = [
+        'file.required' => '请选择上传文件',
+        'file.file' => '未选择上传文件',
+        'file.image' => '上传文件需为图片',
+    ];
+
+    protected $destroyApiRules = ['id' => 'required'];
+
+    protected $destroyApiMessage = ['id.required' => 'id不能为空'];
+
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        $routeName = Route::currentRouteName();
+        switch ($routeName) {
+            case 'demand.storeApi':
+                return $this->storeApiRules;
+            case 'demand.updateApi':
+                return $this->updateApiRules;
+            case 'demand.uploadFileApi':
+                return $this->uploadFileApiRules;
+            case 'demand.destroyApi':
+                return $this->destroyApiRules;
+            default :
+                return [];
+        }
+    }
+
+    public function messages(): array
+    {
+        $routeName = Route::currentRouteName();
+        switch ($routeName) {
+            case 'demand.storeApi':
+                return $this->storeApiMessage;
+            case 'demand.updateApi':
+                return $this->updateApiMessage;
+            case 'demand.uploadFileApi':
+                return $this->uploadFileApiMessage;
+            case 'demand.destroyApi':
+                return $this->destroyApiMessage;
+            default :
+                return [];
+        }
+    }
+}

+ 50 - 0
app/Http/Requests/Demand/Process/DemandProcessRequest.php

@@ -0,0 +1,50 @@
+<?php
+
+
+namespace App\Http\Requests\Demand\Process;
+
+use App\Traits\RequestApiFormValidation;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Support\Facades\Route;
+
+class DemandProcessRequest extends FormRequest
+{
+    use RequestApiFormValidation;
+
+    private $storeApiRules = [
+        'demand_id' => 'required',
+        'explain' => 'required'
+    ];
+
+    private $storeApiMessage = [
+        'demand_id.required' => '问题未指定',
+        'explain.required' => '说明不能为空'
+    ];
+
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        $routeName = Route::currentRouteName();
+        switch ($routeName) {
+            case 'demand.process.storeApi':
+                return $this->storeApiRules;
+            default :
+                return [];
+        }
+    }
+
+    public function messages(): array
+    {
+        $routeName = Route::getCurrentRoute();
+        switch ($routeName) {
+            case 'demand.process.storeApi':
+                return $this->storeApiMessage;
+            default :
+                return [];
+        }
+    }
+}

+ 5 - 1
app/Providers/AppServiceProvider.php

@@ -16,6 +16,9 @@ use App\Services\CustomerLogStatusService;
 use App\Services\CustomerService;
 use App\Services\DepositoryService;
 use App\Services\FacilitatorService;
+use App\Services\DemandService;
+use App\Services\DemandProcessService;
+use App\Services\DischargeTaskDossierService;
 use App\Services\FeatureService;
 use App\Services\ForeignHaiRoboticsService;
 use App\Services\InventoryAccountMissionService;
@@ -165,6 +168,8 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('CustomerService',CustomerService::class);
         app()->singleton('DataHandlerService',DataHandlerService::class);
         app()->singleton('DeliveryAppointmentService',DeliveryAppointmentService::class);
+        app()->singleton('DemandProcessService',DemandProcessService::class);
+        app()->singleton('DemandService',DemandService::class);
         app()->singleton('DepositoryService',DepositoryService::class);
         app()->singleton('DischargeTaskService',DischargeTaskService::class);
         app()->singleton('FacilitatorService',FacilitatorService::class);
@@ -248,7 +253,6 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('WarehouseService',WarehouseService::class);
         app()->singleton('WaybillFinancialService',WaybillFinancialService::class);
         app()->singleton('WeighExceptedService',WeighExceptedService::class);
-//        app()->singleton('ApiUserService',\App\Services\api\UserService::class);
     }
 
 

+ 13 - 0
app/Services/DemandProcessService.php

@@ -0,0 +1,13 @@
+<?php 
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\DemandProcess;
+
+class DemandProcessService
+{
+    use ServiceAppAop;
+    protected $modelClass=DemandProcess::class;
+
+}

+ 105 - 0
app/Services/DemandService.php

@@ -0,0 +1,105 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\Demand;
+use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\Auth;
+use Intervention\Image\Facades\Image;
+use Ramsey\Uuid\Uuid;
+
+class DemandService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = Demand::class;
+
+    /**
+     * saveUPLoadFile
+     * @param Demand $demand
+     * @param UploadedFile $file
+     * @return array
+     */
+    public function saveUPLoadFile(Demand $demand, UploadedFile $file): array
+    {
+        $tmpFile = $file->getRealPath();
+
+        if (!$demand->uploadFile()) return ['success' => false, 'message' => '该需求已有描述图片'];
+        if (!$file) return ['success' => false, 'message' => '上传图片不得为空'];
+        if (!$file->isValid()) return ['success' => false, 'message' => '找不到上传图片'];
+        if (!is_uploaded_file($tmpFile)) return ['success' => false, 'message' => '文件错误'];
+        if ($file->getSize() > 5 * 1024 * 1024) return ['success' => false, 'message' => '文件不能大于5MB'];
+        $fileSuffix = $file->getClientOriginalExtension();
+
+        $dirPath = storage_path('app\public\files\issue');
+        if (!file_exists($dirPath)) {
+            mkdir($dirPath);
+        }
+
+        $fileName = date('ymd') . '-' . Uuid::uuid1();
+
+        $thumbnailName = storage_path('app\public\files\issue\\' . $fileName . '-thumbnail.' . $fileSuffix);
+        $commonName = storage_path('app\public\files\issue\\' . $fileName . '-common.' . $fileSuffix);
+        $bulkyName = storage_path('app\public\files\issue\\' . $fileName . '-bulky.' . $fileSuffix);
+
+        $result = move_uploaded_file($tmpFile, $bulkyName);
+
+        if ($result == false) return ['success' => false, 'data' => '文件上传失败'];
+
+        $img = Image::make($bulkyName);
+
+        if ($img->height() > $img->width()) {
+            $img->heighten(250)->save($commonName);
+        } else {
+            $img->widen(250)->save($commonName);
+        }
+        $img->heighten(28)->save($thumbnailName);
+
+        $upLoadFile = $demand->saveFile($fileName, $fileSuffix);
+
+        if (!$upLoadFile) return ['success' => false, 'message' => '文件上传失败'];
+
+        return ['success' => true, 'data' => $demand];
+    }
+
+    /**
+     * 完结问题
+     *
+     * @param Demand $demand
+     * @return array
+     */
+    public function finishDemand(Demand $demand): array
+    {
+        $currentId = Auth::user()['id'];
+        if ($currentId != $demand['handler']) {
+            return ['success' => false, 'message' => '非当前问题处理人不能完结'];
+        }
+        if ($demand->update(['status' => 2])){
+            return ['success' => true, 'data' => $demand];
+        }
+        else
+            return ['success' => false, 'message' => '修改失败'];
+    }
+
+    /**
+     * 认领问题
+     *
+     * @param Demand $demand
+     * @param $handler
+     * @return array
+     */
+    public function claimDemand(Demand $demand, $handler): array
+    {
+        if ($demand['status'] != 0)
+            return ['success' => false, 'message' => '任务已被认领'];
+
+        $bool = $demand->update(['handler' => $handler, 'status' => 1]);
+        if ($bool) {
+            $demand->loadMissing('initiator', 'handle', 'uploadFile', 'processes');
+            return ['success' => true, 'data' => $demand];
+        }
+        return ['success' => false, 'message' => '任务认领失败'];
+    }
+
+}

+ 18 - 0
database/factories/DemandFactory.php

@@ -0,0 +1,18 @@
+<?php
+
+
+use App\Demand;
+use Faker\Generator as Faker;
+use Illuminate\Database\Eloquent\Factory;
+
+/** @var Factory $factory */
+$factory->define(Demand::class, function (Faker $faker) {
+    return [
+        'authority_id' => rand(1,10),
+        'initiator' => rand(1,10),
+        'description'=> $faker->text(30),
+        'type' =>rand(0,1),
+        'status' => rand(0,2),
+        'handler' => rand(1,10)
+    ];
+});

+ 15 - 0
database/factories/DemandProcessFactory.php

@@ -0,0 +1,15 @@
+<?php
+
+
+use App\DemandProcess;
+use Faker\Generator as Faker;
+use Illuminate\Database\Eloquent\Factory;
+
+/** @var Factory $factory */
+$factory->define(DemandProcess::class, function (Faker $faker) {
+    return [
+        'demand_id' =>rand(1,10),
+        'processor' => $faker->text(20),
+        'explain' =>rand(1,10),
+    ];
+});

+ 37 - 0
database/migrations/2021_04_02_160659_create_demands_table.php

@@ -0,0 +1,37 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateDemandsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('demands', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger('authority_id')->index()->nullable()->comment('关联权限');
+            $table->bigInteger('initiator')->index()->comment('发起人');
+            $table->string('description')->comment('描述');
+            $table->tinyInteger('type')->default(0)->comment('类型');
+            $table->tinyInteger('status')->default(0)->comment('状态');
+            $table->bigInteger('handler')->comment('处理人');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('demands');
+    }
+}

+ 34 - 0
database/migrations/2021_04_02_162507_create_demand_processes_table.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateDemandProcessesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('demand_processes', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger('demand_id')->index()->comment('关联需求表');
+            $table->bigInteger('processor')->index()->comment('经手人');
+            $table->string('explain')->nullable()->comment('说明');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('demand_processes');
+    }
+}

+ 40 - 0
database/migrations/2021_04_06_092844_create_demand_authorities.php

@@ -0,0 +1,40 @@
+<?php
+
+use App\Authority;
+use Illuminate\Database\Migrations\Migration;
+
+class CreateDemandAuthorities extends Migration
+{
+
+    private $authorities = [
+        '需求管理-问题-删除'
+    ];
+
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        foreach ($this->authorities as $name){
+            Authority::query()->firstOrCreate(
+                ['name' => $name],
+                ['name' => $name,'alias_name' => $name]
+            );
+        }
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        foreach ($this->authorities as $name){
+            Authority::query()->where('name' ,$name)->delete();
+        }
+
+    }
+}

+ 17 - 0
database/seeds/DemandProcessSeeder.php

@@ -0,0 +1,17 @@
+<?php
+use App\DemandProcess;
+use Illuminate\Database\Seeder;
+
+class DemandProcessSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        $params = factory(DemandProcess::class)->times(10)->make()->toArray();
+        DemandProcess::query()->insert($params);
+    }
+}

+ 18 - 0
database/seeds/DemandSeeder.php

@@ -0,0 +1,18 @@
+<?php
+
+use App\Demand;
+use Illuminate\Database\Seeder;
+
+class DemandSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        $params = factory(Demand::class)->times(10)->make()->toArray();
+        Demand::query()->insert($params);
+    }
+}

+ 14 - 0
resources/sass/layout.scss

@@ -135,3 +135,17 @@ table,table.table-striped tbody{
     background-color: rgba(0, 0, 0, .05)
 }
 
+.btn-circle {
+    width: 45px;
+    height: 45px;
+    text-align: center;
+    align-content: center;
+    padding: 1px 0;
+    font-size: 12px;
+    line-height: 1.428571429;
+    border-radius: 22.5px;
+    :hover{
+        color: #fffff8;
+        text-decoration: none;
+    }
+}

+ 99 - 0
resources/views/demand/_create.blade.php

@@ -0,0 +1,99 @@
+@auth
+<div id="demand-div">
+    <div class="container-fluid position-absolute" style="min-width:10px;z-index:300;top: 100px;">
+        <div class="float-right position-absolute" style="right: 50px" >
+            <button class="btn-circle btn-outline-primary" type="button" id="dropdownMenu2" data-toggle="dropdown" style="width: 45px;height: 45px"
+                    aria-haspopup="true" aria-expanded="false">
+                <span>提出<br/>问题</span>
+            </button>
+            <div class="dropdown-menu" aria-labelledby="dropdownMenu2">
+                <button class="dropdown-item" type="button" @click="showAddDemand()">
+                    提出问题
+                </button>
+                <a href={{url('demand/')}} class="dropdown-item">问题列表</a>
+            </div>
+        </div>
+    </div>
+
+
+    <div class="modal" tabindex="-1" id="add-demand">
+        <div class="modal-dialog modal-lg modal-dialog-centered">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4>问题详情添加</h4>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">&times;</span>
+                    </button>
+                </div>
+                <div class="modal-body">
+                    <div class="form-group row">
+                        <label for="add-demand-auth" class="col-sm-2 col-form-label text-right">问题可见权限</label>
+                        <div class="col-sm-10 form-inline">
+                            <select name="add-demand-auth" id="add-demand-auth" class="form-control col-sm-7"
+                                    v-model="addDemand.authority_id"
+                                    :class="demandErrors.authority_id?'is-invalid':''"
+                                    @focus="demandErrors.authority_id!==null ? demandErrors.authority_id=null:''">
+                                <option v-for="(authority,index) in authoritiesFilter" :value="authority.id">@{{ authority.name }}</option>
+                            </select>
+                            <input type="text" class="form-control col-sm-3 ml-1" id="add-demand-auth-filter" @input="filterAuth($event)"
+                                   placeholder="输入权限进行删选">
+                            <div class="invalid-feedback" v-if="demandErrors.authority_id">
+                                @{{ demandErrors.authority_id[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="add-demand-type" class="col-sm-2 col-form-label text-right text-primary">问题类型&nbsp;*</label>
+                        <div class="col-sm-10 form-inline">
+                            <select name="add-demand-auth" id="add-demand-type" class="form-control col-sm-10"
+                                    v-model="addDemand.type"
+                                    :class="demandErrors.type?'is-invalid':''"
+                                    @focus="demandErrors.type!==null ? demandErrors.type=null:''">
+                                <option v-for="(type,index) in types" :value="type.name">@{{ type.value }}</option>
+                            </select>
+                            <div class="invalid-feedback" v-if="demandErrors.type">
+                                @{{ demandErrors.type[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="add-demand-description" class="col-sm-2 col-form-label text-right text-primary">问题描述&nbsp;*</label>
+                        <div class="col-sm-10 form-inline">
+                            <textarea  id="add-demand-description" class="form-control col-sm-10"
+                                       v-model="addDemand.description"
+                                      :class="demandErrors.description?'is-invalid':''"
+                                      @focus="demandErrors.description!==null ? demandErrors.description=null:''"
+                            ></textarea>
+
+                            <div class="invalid-feedback" v-if="demandErrors.description">
+                                @{{ demandErrors.description[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+
+                    <div class="form-group row">
+                        <label for="add-demand-file" class="col-sm-2 col-form-label text-right">上传文件</label>
+                        <div class="col-sm-10 form-inline">
+                            <input type="file" name="name" id="add-demand-file-create" class="form-control-file col-sm-10"
+                                   placeholder="输入配置名称"
+                                   :class="demandErrors.file?'is-invalid':''"
+                                   @focus="demandErrors.file!==null ? demandErrors.file=null:''">
+                            <div class="invalid-feedback" v-if="demandErrors.file">
+                                @{{ demandErrors.file[0] }}
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                    <button type="button" class="btn btn-primary" @click="createDemand">提交</button>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>
+@endauth

+ 88 - 0
resources/views/demand/_createjs.blade.php

@@ -0,0 +1,88 @@
+@auth
+<script>
+    new Vue({
+        el: "#demand-div",
+        data: {
+            types: [{name:'0',value:'需求'},{name:'1',value:'问题'}],
+            addDemand: {},
+            authorities: [],
+            authoritiesFilter: [],
+            demandErrors: {}
+        },
+        created() {
+            this.authoritiesFilter = JSON.parse(JSON.stringify(this.authorities));
+            this.getAuthority();
+        },
+        mounted() {
+
+        },
+        methods: {
+            /** 筛选 */
+            filterAuth($e) {
+                let value = $($e.target).val();
+                let authorities = JSON.parse(JSON.stringify(this.authorities));
+                if (value === null) {
+                    this.authoritiesFilter = authorities;
+                    return;
+                }
+                this.authoritiesFilter = authorities.filter(function (item) {
+                    return item['name'].includes(value);
+                });
+                if(this.authoritiesFilter.length > 0)
+                    this.addDemand.authority_id = this.authoritiesFilter[0]['id'];
+            },
+            /** 创建 */
+            showAddDemand() {
+                this.addDemand = {};
+                this.authoritiesFilter = JSON.parse(JSON.stringify(this.authorities));
+                $('#add-demand-auth-filter').val('');
+                $('#add-demand').modal('show')
+                if(this.authoritiesFilter.length === 0){
+                    if(this.authorities.length !==0)this.authoritiesFilter = JSON.parse(JSON.stringify(this.authorities));
+                    else this.getAuthority();
+                }
+            },
+            /** 创建 */
+            createDemand() {
+                let url = '{{url('apiLocal/demand/store')}}';
+
+                window.tempTip.setIndex(1999);
+                window.tempTip.setDuration(3000);
+
+                let formData = new FormData();
+                let file = document.querySelector('#add-demand-file-create').files[0];
+                if(this.addDemand['authority_id'])formData.append('authority_id', this.addDemand['authority_id']);
+                if(this.addDemand['type'])formData.append('type', this.addDemand['type']);
+                if(this.addDemand['description'])formData.append('description', this.addDemand['description']);
+                if(file)formData.append('file', file);
+
+                window.axios.post(url, formData, {
+                    'Content-Type': 'multipart/form-data'
+                }).then(res => {
+                    if (res.data.success) {
+                        window.tempTip.showSuccess('问题提交成功');
+                        $('#add-demand').modal('hide')
+                        return;
+                    }
+                    if(res.data.errors) this.demandErrors = res.data.errors;
+                    else  window.tempTip.show('问题提交失败');
+                }).catch(err => {
+                    window.tempTip.show(err);
+                });
+            },
+            getAuthority(){
+                let url = '{{url('apiLocal/authority/get')}}';
+                window.axios.get(url).then(res=>{
+                    if(res.data.success){
+                        this.authorities = res.data.data;
+                        this.authoritiesFilter = res.data.data;
+                        this.$forceUpdate();
+                    }
+                }).catch(err=>{
+                    window.tempTip.show(err);
+                });
+            },
+        }
+    });
+</script>
+@endauth

+ 10 - 0
resources/views/demand/menu.blade.php

@@ -0,0 +1,10 @@
+<div class="container-fluid nav2" id="nav2">
+    <div class="card">
+        <ul class="nav nav-pills">
+            <li class="nav-item">
+                <a target="finance/instantBill" class="nav-link" href="{{url('demand/')}}" :class="{active:isActive('demand',1)}">问题</a>
+            </li>
+        </ul>
+    </div>
+</div>
+

+ 87 - 0
resources/views/demand/search/_table.blade.php

@@ -0,0 +1,87 @@
+<table class="table table-striped table-bordered table-sm table-hover">
+    <thead class="text-center">
+    <tr class="align-text-bottom">
+        <th rowspan="2">序号</th>
+        <th rowspan="2">类型</th>
+        <th rowspan="2">需求描述</th>
+        <th rowspan="1">过程</th>
+        <th rowspan="2">附件</th>
+        <th rowspan="2">发起人</th>
+        <th rowspan="2">处理人</th>
+        <th rowspan="2">发起时间</th>
+        <th rowspan="2">状态</th>
+        <th rowspan="2">操作</th>
+    </tr>
+    <tr >
+        <td class="p-0">
+            <table class="table m-0 p-0">
+                <td>说明</td>
+                <td>经手人</td>
+                <td>时间</td>
+            </table>
+        </td>
+    </tr>
+    </thead>
+    <tbody>
+    <template v-if="demands.length>0">
+        <tr class="text-center" v-for="(demand,i) in demands" @click="selectTr===i+1?selectTr=0:selectTr=i+1"
+            :class="selectTr===i+1?'focusing' : ''">
+            <td>@{{ i+1 }}</td>
+            <td>@{{ demand.type }}</td>
+            <td class="">
+                <textarea class="form-control" rows="2" cols="15" :value="demand.description" @change="updateDemand(demand,'description',$event)" :disabled="demand['status'] !== '未处理'"></textarea>
+            </td>
+            <td class="text-left p-0" @mouseenter="showAddBtn(demand)" @mouseleave="showAddBtn(demand)" >
+                <button class="btn btn-primary position-absolute d-none" :id="'addBtn_'+demand['id']"  style="margin-top: -35px;" @click="toggleAddDiv(demand,demand['id'])" v-if="demand['status'] !== '已处理'">新</button>
+                <div>
+                    <div :id="'addDiv_'+demand['id']" class="form-inline float-right d-none">
+                        <input type="text" class="form-control" :id="'addProcess'+demand['id']"  style="width: 300px">
+                        <button class="btn btn-sm btn-primary ml-1" @click="addProcess(demand)">添加</button>
+                    </div>
+                    <div>
+                    <template v-if="demand['processes'].length > 0 ">
+                        <table class="table p-0 m-0 table-striped">
+                            <template v-for="(process,process_i) in demand['processes']">
+                                <tr  class="text-center" @mouseenter="toggleDelBtn(demand['id'],process)" @mouseleave="toggleDelBtn(demand['id'],process)" >
+                                    <td>@{{ process.explain }}</td>
+                                    <td>@{{ process.user ? process.user.name : '' }}</td>
+                                    <td>@{{ process.created_at }}</td>
+                                    <td class="p-0 m-0" style="width: 35px;max-width: 35px">
+                                        <button :id="'demand-'+demand['id']+'process-'+process['id']" type="button" @click="deleteProcess(demand,process,process_i,i)" class="btn btn-sm btn-outline-danger d-none m-0" v-if="demand['status'] !== '已处理'">删</button>
+                                    </td>
+                                </tr>
+                            </template>
+                        </table>
+                    </template>
+                    </div>
+                </div>
+            </td>
+            <td>
+                <div class="text-center" @mouseleave="removeCommonImg('imgBulky_'+demand.id)" @mouseenter="commonImg('img_'+demand.id,demand,i)">
+                    <img v-if="demand['upload_file']" :id="'img_'+demand.id" :data-src="demand['upload_file']['url']" src="{{url('icon/img404-thumbnail.jpg')}}" alt="">
+                    <button v-else type="button" class="btn btn-outline-secondary" @click="showUploadDiv(demand,i)">上传文件</button>
+                </div>
+            </td>
+            <td>@{{ demand.initiator ? demand.initiator.name : '' }}</td>
+            <td>@{{ demand.handle ? demand.handle.name : '' }}</td>
+            <td>@{{ demand.created_at }}</td>
+            <td>@{{ demand.status }}</td>
+            <td>
+                <button class="btn btn-outline-success" @click="finishDemand(demand,i)" v-if="demand['status'] === '处理中'">完结</button>
+                <button class="btn btn-outline-success" @click="claimDemand(demand,i)" v-if="demand['status'] === '未处理'">认领</button>
+                <button class="btn btn-outline-danger" @click="destroyDemand(demand,i)">删</button>
+            </td>
+        </tr>
+    </template>
+    <template v-else>
+        <tr>
+            <td colspan="11">
+                <div class="alert alert-info text-center">
+                    按条件查询的数据结果未空
+                </div>
+            </td>
+        </tr>
+    </template>
+    </tbody>
+</table>
+{{ $demands->withQueryString()->links() }}

+ 30 - 0
resources/views/demand/search/_uploadFile.blade.php

@@ -0,0 +1,30 @@
+<div class="modal fade" tabindex="-1" role="dialog" id="uploadFile">
+    <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5>文件上传</h5>
+                <button type="button" class="close" data-dismiss="modal">&times;</button>
+            </div>
+            <div class="modal-body">
+                <div class="container-fluid">
+                    <div class="text-center small row mb-1" v-if="uploadError && uploadError.length > 0">
+                        <div class="col-3 text-danger">上传失败</div>
+                        <div class="col-8">
+                            <b v-for="err in uploadError">@{{ err }}<br></b>
+                        </div>
+                    </div>
+                </div>
+                <div>
+                    <input type="file" class=" form-control-file" id="upLoadFile-input" ref="file" @focus="uploadError = null">
+                </div>
+                <div>
+                    <span class="text-secondary mt-1">文件大小不能超过5MB</span>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button class="btn btn-success" data-dismiss="modal" >取消</button>
+                <button class="btn btn-success" @click="uploadFile()">开始上传</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 347 - 0
resources/views/demand/search/index.blade.php

@@ -0,0 +1,347 @@
+@extends('layouts.app')
+
+@section('title','需求')
+
+@section('content')
+    <nav id="nav2">
+        @component('demand.menu')@endcomponent
+        @component('demand.search.menu')@endcomponent
+    </nav>
+
+    <div class="container-fluid d-none" id="list">
+        <div id="form_div"></div>
+
+        @include('demand.search._table')
+        @include('demand.search._uploadFile')
+
+    </div>
+@endsection
+
+@section('lastScript')
+    <script src="{{mix('js/queryForm/queryForm.js')}}"></script>
+
+    <script>
+        let demand_vue = new Vue({
+            el: '#list',
+            data: {
+                demands: {!! $demands->toJson() !!}['data'],
+                status: [
+                    {name: 0, value: '未处理'}, {name: 1, value: '处理中'}, {name: 2, value: '已处理'},
+                ],
+                types: [
+                    {name: 0, value: '需求'}, {name: 1, value: '问题'}
+                ],
+                selectTr: null,
+                uploadError: [],
+                selectDemand: null,
+                selectIndex: null,
+                imgs: [],
+            },
+            created() {
+                let that = this;
+                this.demands.forEach(function (item, index, self) {
+                    that.initDemand(self[index]);
+                });
+            },
+            mounted() {
+                this.imgs = Array.from(document.getElementById('list').querySelectorAll('img'));
+                this.lazy();
+                if (this.imgs && this.imgs.length > 0) {
+                    window.addEventListener('scroll', this.lazy)
+                }
+                console.log(this.imgs);
+                $('#list').removeClass('d-none');
+                let data = [
+                    [
+                        {name: 'created_at_start', type: 'time', tip: '创建开始时间'},
+                        {name: 'created_at_end', type: 'time', tip: '创建结束时间'},
+                        {name: 'type', type: 'select', data: this.types, placeholder: '类型'},
+                    ]
+                ];
+                let form = new query({
+                    el: "#form_div",
+                    condition: data,
+                });
+                form.init();
+            },
+            methods: {
+                initDemand(demand) {
+                    demand['status'] = this.status[demand['status']]['value'] ?? '';
+                    demand['type'] = this.types[demand['type']]['value'] ?? '';
+                    demand['showAddDiv']= false;
+                    demand['showAddBtn']= false;
+                    if (demand['upload_file']) this.setImgUrl(demand['upload_file']);
+                    if (demand['processes']){
+                        demand['processes'].forEach(function(item,index,self){
+                            self[index]['status'] = false;
+                        });
+                    }
+                },
+                setImgUrl(uploadFile) {
+                    let url = '{{url('/storage/')}}';
+                    let urlPath = uploadFile['url'];
+                    let type = uploadFile.type;
+                    uploadFile['url'] = url + urlPath + '-thumbnail.' + type;
+                    uploadFile['bulkyUrl'] = url + urlPath + '-bulky.' + type;
+                    uploadFile['commonUrl'] = url + urlPath + '-common.' + type;
+                },
+                /** 完结需求 */
+                finishDemand(demand,index) {
+                    let url = '{{url('apiLocal/demand/finish')}}';
+                    window.tempTip.setDuration(3000);
+                    window.axios.post(url, {id: demand['id']}).then(res => {
+                        if (res.data.success) {
+                            window.tempTip.showSuccess('需求完结成功');
+                            demand.status = '已处理'
+                            return;
+                        }
+                        window.tempTip.show('需求完结失败' + res.data.data);
+                    }).catch(err => {
+                        window.tempTip.show('需求完结异常' + err);
+                    });
+                },
+                /** 删除 */
+                destroyDemand(demand, index) {
+                    if (!confirm('确定要删除当前需求吗?')) {
+                        return;
+                    }
+                    let url = '{{url('apiLocal/demand/destroy?id=')}}' + demand['id'];
+
+                    window.axios.delete(url).then(res => {
+                        if (res.data.success) {
+                            window.tempTip.showSuccess('删除成功!');
+                            this.$delete(this.demands,index);
+                            this.imgs = Array.from(document.getElementById('list').querySelectorAll('img'));
+                            this.lazy();
+                            this.$forceUpdate();
+                            return;
+                        }
+                        window.tempTip.show(res.data.data);
+                    }).catch(err => {
+                        window.tempTip.show('删除出现异常' + err);
+                    });
+                },
+                /** 添加处理过程 */
+                addProcess(demand) {
+                    window.tempTip.setDuration(3000);
+                    let url = '{{url('apiLocal/demand/process/store')}}';
+                    let process = $('#addProcess'+demand['id']).val();
+                    let data = {'demand_id': demand['id'], 'explain': process};
+                    if(process === null || process.length === 0){
+                        window.tempTip.show('输入内容');
+                        $('#addProcess'+demand['id']).focus();
+                        return;
+                    }
+                    window.axios.post(url, data).then(res => {
+                        if (res.data.success) {
+                            if(!demand['processes'])demand['processes']= [res.data.data];
+                            else demand['processes'].unshift(res.data.data);
+                            this.$forceUpdate();
+                            window.tempTip.showSuccess('添加处理过程成功');
+                            $('#addProcess'+demand['id']).val();
+                            this.toggleAddDiv(demand,demand['id']);
+                            return;
+                        }
+                        $('#addProcess'+demand['id']).focus();
+                        window.tempTip.show('添加处理过程失败')
+                    }).catch(err => {
+                        $('#addProcess'+demand['id']).focus();
+                        window.tempTip.show('添加处理过程异常:' + err);
+                    });
+
+                },
+                /** 文件上传 */
+                uploadFile() {
+                    let fileInput = document.querySelector('#upLoadFile-input');
+                    let url = '{{url('apiLocal/demand/uploadFile')}}';
+
+                    let formData = new FormData();
+                    formData.append('id', this.selectDemand);
+                    let file = fileInput.files[0];
+                    formData.append('file', file);
+                    tempTip.setDuration(9999)
+                    tempTip.setIndex(1051)
+                    tempTip.waitingTip('上传中......');
+
+                    if(fileInput.files.length === 0){
+                        tempTip.cancelWaitingTip();
+                        this.uploadError = ['请选择上传文件'];
+                        return;
+                    }
+
+                    let that = this;
+                    window.axios.post(url, formData, {
+                        'Content-Type': 'multipart/form-data'
+                    }).then(res => {
+                        tempTip.cancelWaitingTip()
+                        tempTip.setDuration(2000)
+                        if (res.data.success) {
+                            this.initDemand(res.data.data);
+                            this.$set(this.demands, this.selectIndex, res.data.data);
+                            $('#uploadFile').modal('hide');
+                            setTimeout(function(){
+                                that.imgs.push(document.getElementById('img_'+res.data.data['id']));
+                                that.lazy();
+                            },1);
+                            window.tempTip.showSuccess('文件上传成功');
+                            this.$forceUpdate();
+                            return;
+                        }
+                        if(res.data.errors)this.uploadError = res.data.errors;
+                        window.tempTip.show('文件上传失败');
+                    }).catch(err => {
+                        tempTip.cancelWaitingTip()
+                        window.tempTip.show('文件上传异常:' + err);
+                    });
+                },
+                /** 修改需求描述 */
+                updateDemand(demand, column, $e) {
+                    let url = '{{url('apiLocal/demand/update')}}';
+                    let data = {'id': demand['id']};
+                    let value = $($e.target).val();
+                    data[column] = value;
+
+                    window.tempTip.setDuration(3000);
+                    window.axios.post(url, data).then(res => {
+                        if (res.data.success) {
+                            demand[column] = value;
+                            this.$forceUpdate();
+                            window.tempTip.showSuccess('修改需求成功');
+                            return;
+                        }
+                        window.tempTip.show(res.data.data);
+                    }).catch(err => {
+                        window.tempTip.show('修改需求描述异常:' + err);
+                    });
+                },
+                /** 问题认领 */
+                claimDemand(demand, index) {
+                    console.log(demand);
+                    let url = '{{url('apiLocal/demand/claim')}}';
+                    window.tempTip.setDuration(3000);
+                    window.axios.post(url, {id: demand['id']}).then(res => {
+                        if (res.data.success) {
+                            this.initDemand(res.data.data);
+                            this.$set(this.demands, index, res.data.data);
+                            window.tempTip.showSuccess('认领成功!');
+                            return;
+                        }
+                        if (res.data.errors) window.tempTip.show(res.data.errors);
+                        else window.tempTip.show(res.data.data);
+                    }).catch(err => {
+                        window.tempTip.show('认领出现异常' + err);
+                    });
+                },
+                showUploadDiv(demand, index) {
+                    this.selectDemand = demand['id'];
+                    this.selectIndex = index;
+                    this.uploadError = [];
+                    $('#uploadFile').modal('show');
+                },
+                lazy() {
+                    //可视区域高度
+                    let height = window.innerHeight;
+                    //滚动区域高度
+                    let scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
+                    let _this = this;
+                    this.imgs.forEach(function (img, i) {
+                        if ((height + scrollHeight) > $('#' + img.getAttribute('id')).offset().top && img.getAttribute('data-src')) {
+                            let temp = new Image();
+                            temp.src = img.getAttribute('data-src');
+                            temp.onload = function () {
+                                img.src = img.getAttribute('data-src');
+                                _this.$delete(_this.imgs, i);
+                            }
+                        }
+                    });
+                },
+                removeCommonImg(id) {
+                    $('#' + id).remove();
+                },
+                commonImg(id, demand,index) {
+                    if (!demand['upload_file']) return;
+                    let bulkyUrl = demand['upload_file']['bulkyUrl'];
+                    let commonUrl = demand['upload_file']['commonUrl'];
+                    $('#' + id).after(
+                        "<div id=\"imgBulky_" + demand['id'] + "\" style='position: absolute;padding-top: 2px;z-index: 99'>" +
+                        "<div style='position:absolute'>" +
+                            "<div >" +
+                            "<a target='_blank' href='" + bulkyUrl + "'>" +
+                                "<img src='" + commonUrl + "'" + "style='position: relative;left:-50px;' >" +
+                            "</a>" +
+                            "</div>" +
+                                "<button type='button' class='btn btn-sm btn-danger' onclick='demand_vue.btnDeleteImg(this,"+index+")' value='" + id + "' style='position: relative;float: right;margin-right: 51px;margin-top: -30px;' >删除</button>" +
+                        "</div>" +
+                        "</div>");
+                },
+                btnDeleteImg(e,index) {
+                    let idStr = $(e).val()
+                    let id = idStr.substr(idStr.indexOf('_') + 1)
+                    if (!confirm('确定要删除所选图片吗?')) return;
+                    this.destroyImg(id,index);
+                },
+                destroyImg(id,index) {
+                    let url = '{{url('apiLocal/demand/destroyFile/?id=')}}'+id;
+                    window.tempTip.setDuration(3000);
+                    window.axios.delete(url).then(res=>{
+                        if(res.data.success){
+                            window.tempTip.showSuccess('附件删除成功!');
+                            this.$delete(this.demands[index],['upload_file']);
+                            this.$forceUpdate();
+                            return ;
+                        }
+                        window.tempTip.show('附件删除异常:'+res.data.data);
+                    }).catch(err=>{
+                        window.tempTip.show(err);
+                    });
+                },
+                toggleAddDiv(demand,id){
+                    let div = $('#addDiv_'+id);
+                    if(demand['showAddDiv']){
+                        div.addClass('d-none');
+                    }else{
+                        div.removeClass('d-none');
+                    }
+                    demand['showAddDiv'] = !demand['showAddDiv'];
+                },
+                toggleDelBtn(demandId,process){
+                    let btn = $('#demand-'+demandId+'process-'+process['id']);
+                    if(process['status']){
+                        btn.addClass('d-none');
+                    }else{
+                        btn.removeClass('d-none');
+                    }
+                    process['status'] = !process['status'];
+                },
+                showAddBtn(demand){
+                    if(demand['showAddBtn']){
+                        $("#addBtn_"+demand.id).addClass('d-none');
+                    }else{
+                        $("#addBtn_"+demand.id).removeClass('d-none');
+                    }
+                    demand['showAddBtn'] = !demand['showAddBtn'];
+                },
+                deleteProcess(demand,process,process_i,index){
+                    if (!confirm('确定要删除当前描述吗?')) {
+                        return;
+                    }
+
+                    let url = '{{url('apiLocal/demand/process/destroy/?id=')}}'+ process['id'];
+                    window.tempTip.setDuration(3000);
+                    window.axios.delete(url).then(res=>{
+                        if(res.data.success){
+                            this.$delete(this.demands[index]['processes'],process_i);
+                            demand.processes.slice(process_i,0);
+                            window.tempTip.showSuccess('描述删除成功!');
+                            this.$forceUpdate();
+                            return;
+                        }
+                        window.tempTip.show('描述删除失败!'+res.data.data ? res.data.data : '' );
+                    }).catch(err=>{
+                        window.tempTip.show(err);
+                    });
+                }
+            }
+        });
+    </script>
+@endsection

+ 10 - 0
resources/views/demand/search/menu.blade.php

@@ -0,0 +1,10 @@
+<div class="container-fluid nav3">
+    <div class="card">
+        <ul class="nav nav-pills">
+            <li class="nav-item">
+                <a target="finance/instantBill" class="nav-link" href="{{url('demand/')}}" :class="{active:isActive('demand',1)}">查询</a>
+            </li>
+        </ul>
+    </div>
+</div>
+

+ 2 - 0
resources/views/layouts/app.blade.php

@@ -25,6 +25,7 @@
             @component('layouts.menu')@endcomponent
         </div>
     </nav>
+    @component('demand._create')@endcomponent
     @yield('content')
     <hr>
 </div>
@@ -94,5 +95,6 @@
         }
     });
 </script>
+@component('demand._createjs')@endcomponent
 @yield('lastScript')
 </html>

+ 5 - 0
resources/views/layouts/menu.blade.php

@@ -62,6 +62,11 @@
                                         :class="{active:isActive('procurement',1)}">
                         <span class="fa fa-cart-plus" style="color: #1b4b72"></span>
                         采购管理</a></li>@endcan
+
+            <li class="nav-item"><a href="{{url("demand/")}}" class="nav-link"
+                                    :class="{active:isActive('demand',1),'d-none':!isActive('demand',1)}">
+                    <span class="fa fa-cart-plus" style="color: #1b4b72"></span>
+                    需求管理</a></li>
         @can('基础设置')
             <li class="nav-item"><a href="{{url("maintenance")}}" class="nav-link" target="maintenance"
                                     :class="{active:isActive('maintenance',1)}">

+ 17 - 0
routes/apiLocal.php

@@ -162,3 +162,20 @@ Route::group(['prefix' => 'facilitator'], function () {
     Route::delete('destroy', 'FacilitatorController@destroyApi')->name('facilitator.destroyApi');
     Route::post('gainStatement', 'FacilitatorController@gainStatementApi')->name('facilitator.gainStatementApi');
 });
+
+/** 需求管理 */
+Route::group(['prefix'=>'demand'],function(){
+    Route::post('store','DemandController@storeApi')->name('demand.storeApi');
+    Route::post('update','DemandController@updateApi')->name('demand.updateApi');
+    Route::post('uploadFile','DemandController@uploadFileApi')->name('demand.uploadFileApi');
+    Route::delete('destroy','DemandController@destroyApi')->name('demand.destroyApi');
+    Route::delete('destroyFile','DemandController@destroyFileApi')->name('demand.destroyFileApi');
+    Route::post('finish','DemandController@finishApi')->name('demand.finishApi');
+    Route::post('claim','DemandController@claimApi')->name('demand.claimApi');
+    Route::group(['prefix'=>'process'],function (){
+        Route::post('store','DemandProcessController@storeApi')->name('demand.process.storeApi');
+        Route::delete('destroy','DemandProcessController@destroyApi')->name('demand.process.destroyApi');
+    });
+});
+
+Route::get('/authority/get','AuthorityController@getAuthoritiesApi')->name('authority.getAuthoritiesApi');

+ 7 - 0
routes/web.php

@@ -256,6 +256,9 @@ Route::group(['prefix'=>'maintenance'],function(){
     Route::post('mail/updateTemplate', 'SendEmailsController@updateTemplate')->name('mail.updateTemplate');
     Route::post('mail/updateRemark', 'SendEmailsController@updateRemark')->name('mail.updateRemark');
     Route::post('mail/active', 'SendEmailsController@active')->name('mail.active');
+
+
+
 });
 Route::get('maintenance', function () {return view('maintenance.index');});
 
@@ -828,3 +831,7 @@ Route::group(['prefix'=>'procurement'],function () {
     Route::get('relating',function (){return view('procurement.menuProcurement');});
 });
 
+/** 需求管理 */
+Route::group(['prefix'=>'demand'],function (){
+    Route::get('/','DemandController@index');
+});