Vuetify+Laravel 实现的框架,有前途吗?
上次征集了HTML可视化编辑工具的意见,扎心了,暂时先放一放,作为我项目的一个控件用好了。
我现在想重启另外一个项目,大家帮忙看看可行不?
用Vuetify + laravel实现一个后台管理框架,同时支持移动版跟PC版。现在实现了一大部分了,只是没有发布。
目标是,通过简单的PHP代码,就可以做成一个应用。一些,目前实现供功能:
1、登陆
2、用户管理
角色及权限管理
相应代码:
class AdminList extends DataTablePage{
protected $title = '管理员列表';
function editPage(){
return AdminEdit::make();
}
function register(){
parent::register();
$this->breadcrumbs->textItem('系统管理')
->textItem('用户管理')
->textItem('管理员');
$this->hiddenBy('users_module');
$this->hiddenBy('admin_setting');
}
function columns(){
return [
//VularTableColumn::make('id','ID'),
VularTableColumn::make('login_name','登录名')
->searchable(),
VularTableColumn::make('name', '名称')
->sortable()
->searchable(),
VularTableColumn::make('email', '邮箱')
->sortable()
->searchable(),
//->class('text-xs-right'),
//->class('text-xs-right'),
VularTableColumn::make('forbid','状态'),
VularTableColumn::make('rolesShow','角色'),
VularTableColumn::make('created_at','时间')
->sortable()
//->classes('text-xs-right')
];
}
function onRow($row){
parent::onRow($row);
$normalChip = VularNode::make()//VularNode的属性会被赋予单元格
->children(VChip::make('正常')
->color('light-blue')
->textColor('white')
->small()
);
$forbidChip = VularNode::make()
->children(VChip::make('禁用')
->color('red')
->textColor('white')
->small()
);
$row->forbid = $row->forbid ? $forbidChip : $normalChip;
$roleChips = VularNode::make();
foreach ($row->roles as $role) {
$roleChips->children(
VChip::make($role->name)
//->color("transparent")
->small()
);
}
// \Log::notice(json_encode($roleChips));
$row->rolesShow = $roleChips;
//return $row;
}
class RoleEdit extends SimpleFormPage{
protected $modelClass = \Water\Vular\Models\Role::class;
protected $newTitle = '新建角色';
protected $editTitle = '角色编辑';
function register(){
parent::register();
$this->breadcrumbs->textItem('系统管理')
->textItem('用户管理')
->textItem('角色');
}
function fields(){
return [
VTextField::make()
->field('name')
->label('角色名')
->requried()
->unique()
->maxLength(20),
VTextArea::make()
->field('description')
->label('描述')
->rows(3)
->maxLength('500'),
VularTreeSelect::make()
->field('permissions')
->label('权限选择')
->flat()
->tile()
->activatable()
->selectable()
->hoverable()
->openOnClick()
->items($this->permissions())
->itemKey('slug')
->style('border-bottom','solid #bbb 1px')
->showFilter(function($value){
return explode(",", $value);
})
->saveFilter(function($value){
$value = array_filter($value, function($slug){
return ($slug & 'vular-per-group-') !== 'vular-per-group-';
});
return implode(",", $value);
}),
VSwitch::make()
->field('forbid')
->label('禁用')
];
}
function permissions(){
$permissions = [];
$permissionClasses = config('vular.permissions');
foreach ($permissionClasses as $permissionClass) {
$permissions = array_merge_recursive($permissions, (new $permissionClass)->toNodes());
}
return $permissions;
}
}
3、订单管理
相应代码:
class OrderList extends DataTablePage{
protected $title = '订单列表';
function editPage(){
return OrderEdit::make();
}
function register(){
parent::register();
$this->breadcrumbs->textItem('订单管理')
->textItem('订单');
$this->filter(TableFilter::make()
->item(function($item){
$item->input(
VTextField::make()
->field('startTime')
->type('date')
->label('最早合同日期')
)
->field('cotract_date')
->min()
->halfWidth();
//->field('name')
})
->item(function($item){
$item->input(
VTextField::make()
->type('date')
->field('endTime')
->label('最晚合同日期')
)
->field('cotract_date')
->max()
->halfWidth();
//->field('name')
})
);
$this->batchMenu()->itemOfDelete()->hiddenBy('order_delete');
}
function columns(){
return [
//VularTableColumn::make('id','ID'),
VularTableColumn::make('cotract_no', '合同号')
->sortable()
->searchable(),
//->class('text-xs-right'),
VularTableColumn::make('customers.name','客户')
->sortable()
->searchable(),
//->class('text-xs-right'),
VularTableColumn::make('contract_amount','合同金额')
->sortable(),
//VularTableColumn::make('collection_value','已收账款'),
VularTableColumn::make('collection_percent','收款比例')
->sortable(),
VularTableColumn::make('royalty_amount','已付提成')
->sortable(),
VularTableColumn::make('cotract_date','合同日期')
->sortable(),
VularTableColumn::make('passed','状态')
->sortable(),
VularTableColumn::make('user','业务员')
->hiddenBy('order_user_column')
->sortable(),
];
}
function onRow($row){
parent::onRow($row);
$normalChip = VularNode::make()//VularNode的属性会被赋予单元格
->children(VChip::make('完成')
->light()
->textColor('white')
->small()
);
$waitingChip = VularNode::make()
->children(VChip::make('未完成')
->color('red')
->textColor('white')
->small()
);
//\Log::notice(json_encode($row));
$row->passed = $row->passed ? $normalChip : $waitingChip;
$row->collection_percent = number_format($row->collection_percent,0);
$row->collection_percent = $row->collection_percent > 99?100: $row->collection_percent;
$row->collection_percent = $row->collection_percent . '%';
}
function queryBuilder(){
return \DB::table('orders')
->join('customers', 'customers.id', '=', 'orders.customer_id')
->leftJoin('admins', 'admins.id', '=', 'orders.user_id')
->select('orders.*','customers.name as customers.name','admins.name as user')
->selectRaw('
(orders.first_collection_amount + ifnull(orders.second_collection_amount,0))*100/orders.contract_amount as collection_percent
'
);
}
function appendToQuery($queryBuilder){
$queryBuilder->orderBy('passed','asc')
->orderBy('cotract_date','desc');
$user = user();
if($user->isPermitted('order_all_data')){
return $queryBuilder;
}
return $queryBuilder->where('orders.user_id', $user->id);
}
function rowEditButton($btn, $row){
//$btn->hiddenBy('order_edit')
return $btn->disabled($row->passed && !user()->isPermitted('order_edit_passed'));
}
function rowDeleteButton($btn, $row){
return $btn->hiddenBy('order_delete');
}
}
class OrderEdit extends OneColumnFormPage{
protected $modelClass = \Water\Vular\Order\Models\Order::class;
protected $newTitle = '新建订单';
protected $editTitle = '订单编辑';
function register(){
parent::register();
$this->breadcrumbs->textItem('订单管理')
->textItem('订单');
//$dbModel = $this->dbModel();
$this->bottomButton(
VBtn::make(trans('vular.finished'))
->large()
->round()
->light()
->valid()
->color('orange')
//->disabled(!$dbModel || ($dbModel&&$dbModel->passed))
->hiddenBy('order_pass')
//->disabledBy('order_pass')
->click(
VularAction::make()
->action('pass')
->post()
->valid()
->bindsTo($this->form)
)
);
//$this->permitActionBy('save','order_save');
//$this->permitActionBy('saveAndContinue','order_save');
}
function cards(){
return[
VularFormGridCard::make()
->title('客户合同')
->flex(
VSelect::make()
->field('customer')
->label('客户')
->items($this->customers())
->prependInnerIcon('account_box')
//->belongsTo()
->requried(),
'xs4'
)
->flex(
VTextField::make()
->field('cotract_date')
->label('合同日期')
->prependInnerIcon("today")
->type('date')
->requried(),
'xs4'
)
->flex(
VTextField::make()
->field('cotract_no')
->label('合同号')
->prefix("#")
->requried()
->unique()
->maxLength(20),
'xs4'
)
->flex(
VSelect::make()
->field('payment_mode')
->label('付款方式')
->prependInnerIcon('payment')
->items(['T/T 100%/0%','T/T 30%/70%','T/T 20%/80%','T/T 10%/90%','T/T 0%/100%','LC at sight', 'DP at sight'])
->requried(),
'xs4'
)
->flex(
VSelect::make()
->field('currency')
->label('币种')
->prependInnerIcon('monetization_on')
->items([
['id'=>'Dollar', 'name'=>'美元'],
['id'=>'Euro', 'name'=>'欧元'],
['id'=>'RMB', 'name'=>'人民币']
])
->requried()
,
'xs4'
)
->flex(
VTextField::make()
->field('contract_amount')
->label('合同金额')
->prependInnerIcon("attach_money")
->float()
->requried(),
'xs4'
)
->flex(
VTextArea::make()
->field('goods_description')
//->prependInnerIcon('description')
->label('货物描述')
->rows(3)
->requried(),
'xs12'
)
->flex(
VTextField::make()
->field('estimated_shipping_date')
->label('预计发货日期')
->prependInnerIcon("today")
->type('date'),
'xs3'
)
->flex(
VTextField::make()
->field('estimated_arrival_date')
->label('预计到港日期')
->prependInnerIcon("today")
->type('date'),
'xs3'
)
->flex(
VTextField::make()
->field('shipping_date')
->label('实际发货日期')
->prependInnerIcon("today")
->type('date'),
'xs3'
)
->flex(
VTextField::make()
->field('arrival_date')
->label('实际到港日期')
->prependInnerIcon("today")
->type('date'),
'xs3'
)
->flex(
VTextField::make()
->field('first_collection_date')
->label('第一次收汇日期')
->prependInnerIcon("today")
->type('date'),
'xs4'
)
->flex(
VTextField::make()
->field('first_collection_amount')
->label('第一次收汇金额')
->prependInnerIcon("attach_money")
->float(),
'xs4'
)
->flex(
VTextField::make()
->field('first_exchange_rate')
->label('结汇汇率')
->prependInnerIcon('trending_up')
->float(),
'xs4'
)
->flex(
VTextField::make()
->field('second_collection_date')
->label('第二次收汇日期')
->prependInnerIcon("today")
->type('date')
,
'xs4'
)
->flex(
VTextField::make()
->field('second_collection_amount')
->label('第二次收汇金额')
->prependInnerIcon("attach_money")
->float(),
'xs4'
)
->flex(
VTextField::make()
->field('second_exchange_rate')
->label('结汇汇率')
->prependInnerIcon('trending_up')
->float()
,
'xs4'
)
->flex(
VTextArea::make()
->field('remarks')
->label('备注')
->rows(2),
'xs12'
)
,
VularFormHasManyCard::make()
->title('工厂合同')
->field('factoryOrders')
->flex(
VSelect::make()
->field('supplier')
->label('工厂')
->items(Supplier::orderBy('name')->get())
->prependInnerIcon('portrait')
//->belongsTo()
->requried(),
'xs4'
)
->flex(
VTextField::make()
->field('cotract_date')
->label('合同日期')
->prependInnerIcon('today')
->type('date')
->requried(),
'xs4'
)
->flex(
VTextField::make()
->field('cotract_no')
->label('合同号')
->prefix('#')
->requried()
->unique()
->maxLength(20),
'xs4'
)
->flex(
VSelect::make()
->field('payment_mode')
->label('付款方式')
->prependInnerIcon('payment')
->items(['T/T 100%/0%','T/T 30%/70%','T/T 20%/80%','T/T 10%/90%','T/T 0%/100%'])
->requried(),
'xs4'
)
->flex(
VSelect::make()
->field('currency')
->label('币种')
->prependInnerIcon('monetization_on')
->items([
['id'=>'RMB', 'name'=>'人民币'],
['id'=>'Dollar', 'name'=>'美元'],
['id'=>'Euro', 'name'=>'欧元']
])
->requried(),
'xs4'
)
->flex(
VTextField::make()
->field('contract_amount')
->prependInnerIcon("attach_money")
->label('合同金额')
->float()
->requried(),
'xs4'
)
->flex(
VTextArea::make()
->field('goods_description')
->label('货物描述')
->rows(3)
->maxLength(500)
->requried(),
'xs12'
)
->flex(
VTextField::make()
->field('estimated_delivery_date')
->label('预计发货时间')
->prependInnerIcon("today")
->type('date')
,
'xs6'
)
->flex(
VTextField::make()
->field('delivery_date')
->label('实际发货时间')
->prependInnerIcon("today")
->type('date')
,
'xs6'
)
->flex(
VTextField::make()
->field('first_pay_date')
->label('第一次付款日期')
->prependInnerIcon("today")
->type('date')
,
'xs4'
)
->flex(
VTextField::make()
->field('first_pay_amount')
->label('第一次付款金额')
->prependInnerIcon("attach_money")
->float(),
'xs4'
)
->flex(
VTextField::make()
->field('first_pay_exchange_rate')
->label('汇率')
->prependInnerIcon('trending_up')
->float()
,
'xs4'
)
->flex(
VTextField::make()
->field('second_pay_date')
->label('第二次付款日期')
->prependInnerIcon("today")
->type('date')
,
'xs4'
)
->flex(
VTextField::make()
->field('second_pay_amount')
->label('第二次付款金额')
->prependInnerIcon("attach_money")
->float(),
'xs4'
)
->flex(
VTextField::make()
->field('second_pay_exchange_rate')
->label('汇率')
->prependInnerIcon('trending_up')
->float()
,
'xs4'
)
->flex(
VTextField::make()
->field('export_rebate')
->label('退税金额')
->prependInnerIcon("attach_money")
->float(),
'xs12'
)
->flex(
VTextArea::make()
->field('remarks')
->label('备注')
->maxLength(500)
->rows(2),
'xs12'
),
VularFormHasManyCard::make('table')
->title('订单费用')
->field('fees')
->flex(
VTextField::make()
->field('pay_date')
->label('付款日期')
->prependInnerIcon("today")
->type('date')
,
'xs6'
)
->flex(
VTextField::make()
->field('name')
->label('名称')
->prependInnerIcon('payment')
->requried()
,
'xs6'
)
->flex(
VSelect::make()
->field('currency')
->label('币种')
->prependInnerIcon('monetization_on')
->items([
['id'=>'RMB', 'name'=>'人民币'],
['id'=>'Dollar', 'name'=>'美元'],
['id'=>'Euro', 'name'=>'欧元']
])
->requried()
,
'xs4'
)
->flex(
VTextField::make()
->field('amount')
->label('金额')
->prependInnerIcon("attach_money")
->requried()
->float()
,
'xs4'
)
->flex(
VTextField::make()
->field('exchange_rate')
->label('汇率')
->prependInnerIcon('trending_up')
->float()
,
'xs4'
)
->flex(
VTextArea::make()
->field('remarks')
->label('备注')
->rows(2)
,
'xs12'
)
,
VularFormGridCard::make()
->title('提成核算')
->flex(
VTextField::make()
//->field('xxx')
->label('毛利')
->disabled()
->defaultValue(number_format($this->grossProfit(),2)),
'xs6'
)
->flex(
VSlider::make()
->field('royalty_rate')
->label('提成比例')
->thumbLabel('always')
->step(5),
'xs6'
)
->flex(
VTextField::make()
//->field('')
->label('应付提成')
->disabled()
->defaultValue(number_format($this->royalty(),2)),
'xs6'
)
->flex(
VTextField::make()
->field('royalty_amount')
->label('实付提成')
->float(),
'xs6'
)
];
}
function pass($viewModel){
return $this->doSave($viewModel, function($dbModel){
$dbModel->passed = true;
})
->success()
->closePage();
}
function beforeSave($dbModel){
$dbModel->passed = false;
if(!$dbModel->id){
$dbModel->user()->associate(user());
}
return $dbModel;
}
function customers(){
$user = user();
if($user->isPermitted('customer_all_data')){
return Customer::orderBy('name')->get();
}
return Customer::where('user_id', $user->id)->orderBy('name')->get();
}
//计算毛利
protected function grossProfit($model = null){
if(!$this->modelId()){
return 0;
}
$model = $model ? $model : $this->dbModel();
//<----------------
//已收RMB账款
$firstRmbAmount = $model->first_exchange_rate? $model->first_collection_amount * $model->first_exchange_rate: $model->first_collection_amount;
$secondRmbAmount = $model->second_exchange_rate? $model->second_collection_amount * $model->second_exchange_rate: $model->second_collection_amount;
$collectionRmbAmount = $firstRmbAmount + $secondRmbAmount;
//<------------
//已付RMB货款
$rmbPayment = 0;
foreach ($model->factoryOrders as $factoryOrder) {
$firstPayment = $factoryOrder->first_pay_exchange_rate? $factoryOrder->first_pay_amount * $factoryOrder->first_pay_exchange_rate : $factoryOrder->first_pay_amount;
$secondPayment = $factoryOrder->second_pay_exchange_rate? $factoryOrder->second_pay_amount * $factoryOrder->second_pay_exchange_rate : $factoryOrder->second_pay_amount;
$rmbPayment += ($firstPayment + $secondPayment - $factoryOrder->export_rebate);
}
//已付RMB费用
$rmbFee = 0;
foreach ($model->fees as $fee) {
$feeAmount = $fee->exchange_rate ? $fee->amount * $fee->exchange_rate : $fee->amount;
$rmbFee += $feeAmount;
}
return $collectionRmbAmount - $rmbPayment - $rmbFee;
}
//己算提成
protected function royalty(){
if(!$this->modelId()){
return 0;
}
$model = $this->dbModel();
return ($this->grossProfit($model) * $model->royalty_rate)/100;
}
protected function modelId(){
return request('id');
}
protected function dbModel(){
$modelClass =$this->modelClass;
//\Log::notice($modelClass);
return $modelClass::find($this->modelId());
}
}
4、文章管理
代码我就不贴了,基于Vuetify的特性,还可以做移动版应用。
这个框架,我要是把它整理好,发布出来,有用吗?
支持关联编辑,跟编辑属性一样方便。
本作品采用《CC 协议》,转载必须注明作者和本文链接
老哥其实你上个项目我觉得不错,这个也挺有前途。不过人的精力是有限的,你一个人不可能同时把所有东西做好,建议你找个自己最想做的项目用心做下去,到一定程度再发出来。
你发一个半成品出来,当然很多人就没什么兴趣了,你征集这个意见意义不大,不要遇到一点小问题就放弃,别人的意见有时候不是那么重要。
laravel
已经有个laravel-admin
有大量使用者了。我想用
lumen
,laravel
那些功能对于接口多余还影响性能,但是laravel-admin
依赖laravel
如果支持
lumen
,可以拉取一些lumen
用户。我也做了一个后台,但是我的方向不同,,,我只给一个基础的 管理员/角色/权限/菜单 功能,附带两个可用可不用的文件管理和配置管理,,,没有任何什么组件啥的,所以也不是 composer 包,,,这样如果真有人用,那就相当于我给他个脚手架,其他东西他自己来,,,满足不了他需求的地方,随便自己改,,,不管是前端还是后端~~
我发过一个demo,前后端分离的。 博客:基于 hyperf,vuetify,casbin 开发的前后端分离管理系统
纯粹的后台管理肯定没有什么用处的,市面上已经很多了,就想 laravel-admin
还是要和业务结合起来,有具体的业务了,考虑的会更细致,并且也会给自己带来一定的回报,考虑一下
vuetifyjs 在github 是vue 最火的UI 库了,参与项目的成员有一个成都的大学生 ,和他交流过,vuetify 只适合PC, APP端性能很差, 项目目前 在升级 Vue 3.0 ,没有计划APP 优化(听说 创始人是个退伍军人)
@lyxxxh lumen 也没比 laravel 快多少, laravel-s 好点
我觉得现在反而一些简单功能的移动端后台有需求,基于pc的各种后台貌似都有,我目前开发的一个商城项目,店长需要经常在手机上面管理自己的商品,很显然他用pc没有手机方便 而且如果店长是兼职类型的.他上班的时候用手机也可以改.
说实话并没啥软用;很大的原因我觉得就是谷歌的材质化设计语言,并不符合国人的审美;如果好好优化下UI或许还可以,但是个人用过vuetify,UI真的是很丑;况且国内有laravel-admin,国外有Nova;不过如果单单作为项目练手,其实还是可以的,加油吧
我就是楼上说的 vuetify 项目成员,
先声明一下,我不属于 vuetify 核心团队,只是在周边打杂。
这种中后台系统,UI 反倒不是关键,主要是 UX 方面,也就是用户的易用度,还有后端人员开发的难度,
然后设计上面的话,如果对 material design 的配色,布局和动画没有一定的研究的话,只用默认组件拼凑,很难达到一个美观的效果。
然后我看了下你后端生成前端的代码实现,只对于我个人来说,我不会用这种方式,我更倾向于完全分离,对未接触过前端的开发者的易用程度不太懂,不做评论。
至于设计方面的例子,我正在开发的两个站,不能说好看,至少能稍微证明下 Vuetify 也能写出不那么难看的站:
https://gamer.epicdata.net/
https://ui.epicdata.net/