账号管理

This commit is contained in:
邓皓元 2018-12-16 11:26:36 +08:00
parent bb85b55c03
commit 6712991029
19 changed files with 687 additions and 66 deletions

View File

@ -3,6 +3,7 @@ namespace App\Domains\Virtual\Http\Controllers;
use App\Core\Controller;
use Illuminate\Http\Request;
use App\Domains\Virtual\Services\CompanyService;
use App\Domains\Virtual\Services\CompanyAccountService;
class CompanyAccountController extends Controller

View File

@ -26,7 +26,9 @@ class CompanyRepository extends Repository
*/
protected $fieldSearchable = [
'id' => '=',
'created_at' => 'like',
// 'created_at' => 'like',
'name' => 'like',
'accounts.username' => 'like',
];
public function model()

View File

@ -7,6 +7,7 @@ use Illuminate\Validation\Rule;
use App\Exceptions\NotExistException;
use App\Models\Virtual\CompanyAccount;
use Illuminate\Support\Facades\Validator;
use App\Domains\Virtual\Repositories\CompanyRepository;
use Dipper\JWTAuth\ServiceContract as JwtServiceContract;
use App\Domains\Virtual\Repositories\CompanyAccountRepository;
@ -70,7 +71,7 @@ class CompanyAccountService extends Service implements JwtServiceContract
$rule = [
'username' => ['username', 'between:2,12', Rule::unique($this->companyAccountRepository->getTable(), 'username')->ignore($attributes['id']), Rule::notIn(config('domain.account.reserved_account'))],
'nickname' => ['string', 'display_length:2,32'],
'nickname' => ['string', 'between:2,32'],
'mobile' => ['string', 'cn_phone', Rule::unique($this->companyAccountRepository->getTable(), 'mobile')->ignore($attributes['id'])],
'password' => ['string'],
'avatar' => ['image'],
@ -78,19 +79,20 @@ class CompanyAccountService extends Service implements JwtServiceContract
$message = [
'username.required' => '请输入用户名',
'username.between' => '用户名只能以非特殊字符和数字开头,不能包含特殊字符',
'username.display_length' => '用户名长度不合法',
'username.username' => '用户名只能以非特殊字符和数字开头,不能包含特殊字符',
'username.between' => '用户名长度不合法',
'username.unique' => '用户名已经被其他用户所使用',
'username.not_in' => '系统保留用户名,禁止使用',
'nickname.display_length' => '昵称长度不合法',
'nickname.between' => '昵称长度不合法',
'mobile.unique' => '手机号已被其他用户使用',
'mobile.cn_phone' => '手机号不合法',
'password.required' => '请输入密码',
'password.string' => '密码格式不合法',
];
if (!$attributes['id']) {
$rule['password'][] = 'required';
$rule['username'][] = 'required';
$rule['password'][] = 'required';
}
Validator::validate($attributes, $rule, $message);
@ -116,6 +118,8 @@ class CompanyAccountService extends Service implements JwtServiceContract
$this->companyAccountRepository->setModel($node)->update($attributes);
}
app(CompanyRepository::class)->forgetCached();
return $node;
}

View File

@ -11,9 +11,14 @@ class Company extends CompanyBase
protected $fillable = ['id', 'name' , 'contacts', 'mobile', 'address', 'remark'];
public function accounts()
{
return $this->hasMany(CompanyAccount::class, 'company_id', 'id');
}
public function cards()
{
return $this->hasMany(Card::class, 'virtual_company_id', 'id');
return $this->hasMany(Card::class, 'company_id', 'id');
}
public function orders()

View File

@ -162,7 +162,7 @@ class CreateBaseTables extends Migration
Schema::create("virtual_company_accounts", function (Blueprint $table) {
$table->increments('id')->comment('自增ID');
$table->string('company_id', 32)->comment('企业ID');
$table->integer('company_id')->unsigned()->default(0)->comment('企业ID');
$table->string('nickname', 32)->default('')->comment('昵称');
$table->string('mobile', 20)->default('')->comment('手机号');
$table->string('username', 32)->default('')->comment('登录名');
@ -177,7 +177,7 @@ class CreateBaseTables extends Migration
Schema::create("virtual_company_addresses", function (Blueprint $table) {
$table->increments('id')->comment('自增ID');
$table->string('company_id', 32)->comment('企业ID');
$table->integer('company_id')->unsigned()->default(0)->comment('企业ID');
$table->string('contacts', 20)->default('')->comment('联系人');
$table->string('mobile', 20)->default('')->comment('手机号');
$table->string('area')->default('')->comment('区域');
@ -192,7 +192,7 @@ class CreateBaseTables extends Migration
$table->increments('id')->comment('自增ID');
$table->string('sn', 32)->comment('产品编码');
$table->string('name', 32)->comment('产品名称');
$table->string('company_id', 32)->comment('企业ID');
$table->integer('company_id')->unsigned()->default(0)->comment('企业ID');
$table->integer('package_id')->unsigned()->default(0)->comment('套餐ID');
$table->integer('base_price')->unsigned()->default(0)->comment('基础价格');
$table->integer('renewal_price')->unsigned()->default(0)->comment('续费价格');

View File

@ -3,7 +3,7 @@
*/
/**
* [index 账号列表]
* [index 企业列表]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
@ -14,7 +14,7 @@ export function index(data) {
}
/**
* [create 创建账号]
* [create 创建企业]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
@ -23,7 +23,7 @@ export function create(data) {
}
/**
* [update 修改账号]
* [update 修改企业]
* @param {[type]} data [description]
* @param {[type]} id [角色id]
* @return {[type]} [description]
@ -33,7 +33,7 @@ export function update(data, id) {
}
/**
* [destroy 删除账号]
* [destroy 删除企业]
* @param {[type]} data [description]
* @return {[type]} [description]
*/

View File

@ -0,0 +1,42 @@
/**
* 企业账号管理
*/
/**
* [index 账号列表]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function index(data) {
return service.get('api/virtual/company/account/index', {
params: data
});
}
/**
* [create 创建账号]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function create(data) {
return serviceForm.post('api/virtual/company/account/create', data);
}
/**
* [update 修改账号]
* @param {[type]} data [description]
* @param {[type]} id [角色id]
* @return {[type]} [description]
*/
export function update(data, id) {
return serviceForm.post(`api/virtual/company/account/update/${id}`, data);
}
/**
* [destroy 删除账号]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function destroy(data) {
return service.post('api/virtual/company/account/destroy', data);
}

View File

@ -50,4 +50,3 @@ window.md5 = md5;
window.jquery = window.$ = jquery;
window.service = service;
window.serviceForm = serviceForm;
window.axios = axios;

View File

@ -1,4 +1,5 @@
import { mapGetters } from 'vuex';
import { objectDot } from 'service/util';
import default_head from 'images/head.png';
export default {
@ -102,10 +103,10 @@ export default {
}
// search 项
search_data = objectDot(search_data);
for (let k in search_data) {
if (search_data[k]) {
search.push([k, search_data[k]].join(':'));
}
search.push([k, search_data[k]].join(':'));
}
if (search.length) {
@ -145,24 +146,24 @@ export default {
* @return {[type]} [description]
*/
return h('p', {
style: {
fontSize: '14px',
marginTop: '15px'
}
},
style: {
fontSize: '14px',
marginTop: '15px'
}
},
[
h('span', data.message + ' 请点击下载:'),
h('span', {
domProps: {
innerHTML: '导入失败.xls'
},
class: ['primary-color', 'c-p'],
on: {
click: () => {
this.downloadExcel(tHeader, this.formatJson(filterVal, data.result), '导入失败');
}
domProps: {
innerHTML: '导入失败.xls'
},
class: ['primary-color', 'c-p'],
on: {
click: () => {
this.downloadExcel(tHeader, this.formatJson(filterVal, data.result), '导入失败');
}
})
}
})
]);
},
exportExcelInfo(h, data) {
@ -174,29 +175,29 @@ export default {
*/
return h('p', {
style: {
fontSize: '14px',
marginTop: '15px'
}
},
[
h('span', data.message + ' 请点击下载:'),
h('span', {
domProps: {
innerHTML: '导入失败.xls'
},
class: ['primary-color', 'c-p'],
on: {
click: () => {
if (data.url !== '') {
window.open(data.url);
} else {
this.$Message.info('无数据可下载');
}
style: {
fontSize: '14px',
marginTop: '15px'
}
},
[
h('span', data.message + ' 请点击下载:'),
h('span', {
domProps: {
innerHTML: '导入失败.xls'
},
class: ['primary-color', 'c-p'],
on: {
click: () => {
if (data.url !== '') {
window.open(data.url);
} else {
this.$Message.info('无数据可下载');
}
}
})
]
}
})
]
);
}
}

View File

@ -17,7 +17,8 @@ const routes = [
{ path: '/logs', name: 'Logs', component: load('system/logs/index'), meta: { title: '日志管理' } },
{ path: '/accounts', name: 'Accounts', component: load('user/accounts/index'), meta: { title: '账号管理' } },
{ path: '/iframe', name: 'Iframe', component: load('iframe/index'), meta: { title: 'iframe' } },
{ path: '/companies', name: 'Companies', component: load('virtual/companies/index'), meta: { title: '企业管理' } }
{ path: '/companies', name: 'Companies', component: load('virtual/companies/index'), meta: { title: '企业管理' } },
{ path: '/company/accounts', name: 'CompanyAccounts', component: load('virtual/company_accounts/index'), meta: { title: '账号管理' } }
]
},
{ path: '*', redirect: { path: '/home' } }

View File

@ -25,8 +25,6 @@ export const serviceForm = axios.create({
baseURL: domain
});
export default axios;
// http request 拦截器
let request = function(config) {
const token = getToken();
@ -95,4 +93,6 @@ service.interceptors.response.use(response, response_err);
serviceForm.interceptors.request.use(request, request_err);
serviceForm.interceptors.response.use(response, response_err);
axios.interceptors.response.use(response, response_err);
axios.interceptors.response.use(response, response_err);
window.axios = axios;

View File

@ -41,11 +41,11 @@ export function getByteLength(char) {
}
/**
* [exportTable 导出excel表格]
* @param {[arry]} tableData [excel表格数据]
* @param {[type]} fileName [excel文件名称]
* @return {[type]} [description]
*/
* [exportTable 导出excel表格]
* @param {[arry]} tableData [excel表格数据]
* @param {[type]} fileName [excel文件名称]
* @return {[type]} [description]
*/
export function exportTable(tableData, fileName) {
// [['标题1','标题2'],['数据1','数据2']]
if (Object.prototype.toString.call(tableData) == '[object Array]') {
@ -115,3 +115,24 @@ export function downloadFile(url, filename) {
};
xhr.send();
}
/**
* [objectDot 对象平铺]
* @param {[type]} object [对象]
* @return {[type]} [一维对象]
*/
export function objectDot(object, prepend = '') {
let results = {};
for (let k in object) {
if ((typeof object[k] == 'object') && Object.keys(object[k]).length) {
results = Object.assign(results, objectDot(object[k], prepend + k + '.'));
} else {
if (object[k]) {
results[prepend + k] = object[k];
}
}
}
return results;
}

View File

@ -1,5 +1,5 @@
<template>
<Modal :closable="false" :mask-closable="false" :title="data?'编辑账号':'添加账号'" @on-visible-change="visibleChange" v-model="my_show">
<Modal :closable="false" :mask-closable="false" :title="data?'编辑企业':'添加企业'" @on-visible-change="visibleChange" v-model="my_show">
<div class="page-edit-wrap uinn-lr20">
<ui-loading :show="page_loading.show"></ui-loading>

View File

@ -92,6 +92,10 @@ export default {
},
clear() {
for (let k in this.params) {
this.params[k] = '';
}
this.my_show = false;
}
}

View File

@ -30,7 +30,8 @@ export default {
},
{
title: '企业名称',
key: 'name'
key: 'name',
width: 300
},
{
title: '联系人',
@ -52,7 +53,6 @@ export default {
{
title: '操作',
key: 'action',
width: 300,
render: (h, {
row,
column,
@ -149,7 +149,7 @@ export default {
* @return {[type]} [description]
*/
index(page = 1) {
let data = this.searchDataHandle(this.params, { page }, { 'trashed': this.trashed });
let data = this.searchDataHandle(this.params, { page }, { 'trashed': this.trashed, 'orderBy': 'id', 'sortedBy': 'asc' });
this.isShowLoading(true);
API.index(data).then(res => {
this.isShowLoading(false);

View File

@ -0,0 +1,76 @@
<template>
<Modal :closable="false" :mask-closable="false" :title=" isUpdate ? '编辑账号' : '添加账号'" @on-visible-change="visibleChange" v-model="my_show">
<div class="page-edit-wrap uinn-lr20">
<ui-loading :show="page_loading.show"></ui-loading>
<ul>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require" v-if="!isUpdate">*</span>用户名
</div>
<div class="ui-list-content">
<p>
<Input :disabled="isUpdate ? true : false" v-model.trim="params.username"></Input>
</p>
<ul class="common-tips-wraper umar-t5" v-if="!isUpdate">
<li class="t-title">提示</li>
<li class="t-content">以字母开头长度在4-32之间只能包含字母数字</li>
</ul>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require">*</span>昵称
</div>
<div class="ui-list-content">
<p>
<Input :maxlength="32" v-model.trim="params.nickname"></Input>
</p>
<ul class="common-tips-wraper umar-t5">
<li class="t-title">提示</li>
<li class="t-content">长度在2-32之间</li>
</ul>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require" v-show="!isUpdate">*</span>密码
</div>
<div class="ui-list-content">
<div>
<Input type="password" v-model.trim="params.password"></Input>
</div>
<ul class="common-tips-wraper umar-t5">
<li class="t-title">提示</li>
<li class="t-content">长度在6-18之间只能包含字母数字和下划线</li>
</ul>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require" v-show="!isUpdate">*</span>确认密码
</div>
<div class="ui-list-content">
<Input type="password" v-model.trim="params.confirm_password"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">手机号</div>
<div class="ui-list-content">
<Input v-model.trim="params.mobile"></Input>
</div>
</li>
</ul>
</div>
<footer class="ta-c" slot="footer">
<Button @click="clear" class="w-80" ghost type="primary">取消</Button>
<Button :loading="loading" @click="ok" class="w-80" type="primary">提交</Button>
</footer>
</Modal>
</template>
<script src="./js/edit.js"></script>

View File

@ -0,0 +1,66 @@
<template>
<div class="page-wrap">
<ui-loading :show="page_loading.show"></ui-loading>
<div class="page-handle-wrap">
<ul class="handle-wraper bd-b">
<li class="f-l">
<div class="text-exp">
<b>全部信息</b>
</div>
</li>
<li class="f-r">
<div class="handle-item">
<Button @click="search.show=!search.show" ghost icon="ios-search" type="primary">搜索</Button>
</div>
<div class="handle-item">
<Button @click="index(1)" icon="md-refresh">刷新</Button>
</div>
</li>
</ul>
<div class="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-250">
<Input clearable placeholder="请输入企业名称" v-model.trim="params.name"></Input>
</li>
<li class="handle-item w-250">
<Input clearable placeholder="请输入用户名称" v-model.trim="params.accounts.username"></Input>
</li>
</ul>
<ul class="handle-wraper">
<li class="f-r">
<div class="handle-item">
<Button @click="index(1)" ghost type="primary">立即搜索</Button>
</div>
<div class="handle-item">
<Button @click="resetSearch" ghost type="warning">重置搜索</Button>
</div>
</li>
</ul>
</div>
</div>
<div class="page-list-wrap">
<Table :columns="table_titles" :data="list_data ? list_data.data : []"></Table>
</div>
<div class="page-turn-wrap" v-if="list_data">
<Page
:current="Number(list_data.current_page)"
:page-size="Number(list_data.per_page)"
:total="Number(list_data.total)"
@on-change="index"
show-elevator
show-total
></Page>
</div>
<ui-edit :data="editObj.data" :show.sync="editObj.show" @add-success="index" @update-success="index(list_data.current_page)"></ui-edit>
</div>
</template>
<script src="./js/index.js"></script>

View File

@ -0,0 +1,175 @@
import * as API from 'api/virtual/company_accounts';
import { isPhone, isPsw, isUserName } from 'validate';
export default {
props: {
isUpdate: {
type: Boolean,
default: false
},
show: {
type: Boolean,
default: false
},
data: {
type: Object,
default() {
return null;
}
}
},
watch: {
show(bool) {
this.my_show = bool;
if (bool) {
if (this.data) {
for (let k in this.data) {
if (k in this.params) {
this.params[k] = this.data[k];
}
}
}
}
}
},
data() {
return {
my_show: false,
loading: false,
params: {
company_id: '',
username: '',
nickname: '',
mobile: '',
password: '',
confirm_password: ''
}
};
},
methods: {
ok() {
if (!this.params.username) {
this.$Message.info('请填写用户名');
return;
}
if (!isUserName(this.params.username)) {
this.$Message.info('用户名填写不合法');
return;
}
if (!this.params.nickname) {
this.$Message.info('请填写昵称');
return;
}
if (!(/[\s\S]{2,32}/.test(this.params.nickname))) {
this.$Message.info('昵称长度在2-32之间');
return;
}
if (!this.params.mobile) {
this.$Message.info('请填写手机号');
return;
}
if (!isPhone(this.params.mobile)) {
this.$Message.info('手机号填写不正确');
return;
}
if (!this.isUpdate) {
if (!this.params.password) {
this.$Message.info('请填写密码');
return;
}
if (!isPsw(this.params.password)) {
this.$Message.info('密码长度在6-18之间只能包含字母、数字和下划线');
return;
}
if (!this.params.confirm_password) {
this.$Message.info('请填写确认密码');
return;
}
if (this.params.password != this.params.confirm_password) {
this.$Message.info('密码与确认密码填写不一致');
return;
}
this.params.password = md5(this.params.password);
} else {
if (this.params.password) {
if (!isPsw(this.params.password)) {
this.$Message.info('密码长度在6-18之间只能包含字母、数字和下划线');
return;
}
if (!this.params.confirm_password) {
this.$Message.info('请填写确认密码');
return;
}
if (this.params.password != this.params.confirm_password) {
this.$Message.info('密码与确认密码填写不一致');
return;
}
}
this.params.password = md5(this.params.password);
}
let data = new FormData();
for (let k in this.params) {
if (k != 'confirm_password') {
if (this.params[k]) {
data.append(k, this.params[k]);
}
}
}
if (this.isUpdate) {
// 编辑
API.update(data, this.data.id).then(res => {
this.loading = false;
if (res.code == 0) {
this.$emit('update-success');
this.$Message.success('更新成功');
this.clear();
}
}).catch(err => {
this.loading = false;
});
} else {
// 添加
API.create(data).then(res => {
this.loading = false;
if (res.code == 0) {
this.$emit('add-success');
this.$Message.success('添加成功');
this.clear();
}
}).catch(err => {
this.loading = false;
});
}
},
visibleChange(bool) {
if (!bool) {
this.$emit('update:show', false);
}
},
clear() {
for (let k in this.params) {
this.params[k] = '';
}
this.my_show = false;
}
}
};

View File

@ -0,0 +1,224 @@
import * as API from 'api/virtual/companies';
import * as AccountAPI from 'api/virtual/company_accounts';
export default {
name: 'Companies',
components: {
UiEdit: resolve => require(['views/virtual/company_accounts/edit'], resolve)
},
data() {
return {
params: {
name: '',
accounts: {
username: ''
}
},
trashed: '',
list_data: null,
editObj: {
show: false,
data: null
},
search: {
show: false
},
table_titles: [
{
title: 'ID',
key: 'id',
width: 80
},
{
title: '企业名称',
key: 'name',
width: 300
},
{
title: '用户名',
key: '',
render: (h, { row, column, index }) => {
if (row.accounts && row.accounts.length) {
return h('span', row.accounts[0].username);
}
}
},
{
title: '电话',
key: '',
render: (h, { row, column, index }) => {
if (row.accounts && row.accounts.length) {
return h('span', row.accounts[0].mobile);
}
}
},
{
title: '昵称',
key: '',
render: (h, { row, column, index }) => {
if (row.accounts && row.accounts.length) {
return h('span', row.accounts[0].nickname);
}
}
},
{
title: '创建时间',
key: 'created_at',
width: 170
},
{
title: '操作',
key: 'action',
render: (h, {
row,
column,
index
}) => {
let html = [];
if (this.haveJurisdiction('create')) {
if (!row.accounts || !row.accounts.length) {
html.push(h('Button', {
props: {
type: 'error',
size: 'small',
disabled: false,
icon: 'ios-create'
},
class: ['btn'],
on: {
click: (event) => {
this.openEdit(true, row);
}
}
}, '创建'));
}
}
if (this.haveJurisdiction('update')) {
if (row.accounts && row.accounts.length) {
html.push(h('Button', {
props: {
type: 'primary',
size: 'small',
disabled: false,
icon: 'ios-create'
},
class: ['btn'],
on: {
click: (event) => {
this.openEdit(true, row);
}
}
}, '编辑'));
}
}
if (this.haveJurisdiction('destroy')) {
if (row.accounts && row.accounts.length) {
html.push(h('Button', {
props: {
type: 'error',
size: 'small',
disabled: false,
icon: 'md-trash'
},
class: ['btn'],
on: {
click: () => {
this.$Modal.confirm({
title: '提示',
content: '删除后该账号不可使用,请谨慎操作',
onOk: () => {
AccountAPI.destroy({
ids: row.accounts[0].id
}).then(res => {
if (res.code == 0) {
this.$Message.success('删除成功');
this.request();
}
});
}
});
}
}
}, '删除'));
}
}
if (html.length) {
return h('div', html);
}
}
}
]
};
},
created() {
this.index(1);
},
methods: {
/**
* [index 列表]
* @param {Number} page [description]
* @return {[type]} [description]
*/
index(page = 1) {
let data = this.searchDataHandle(this.params, { page }, { 'with': 'accounts', 'orderBy': 'id', 'sortedBy': 'asc' });
this.isShowLoading(true);
API.index(data).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.list_data = res.data;
}
}).catch(() => {
this.isShowLoading(false);
});
},
/**
* [openEdit 打开编辑弹窗]
* @return {[type]} [description]
*/
openEdit(bool, row = null) {
let data = {};
let isUpdate = false;
data.company_id = row.id;
if (row && row.accounts && row.accounts.length) {
data = Object.assign(data, row.accounts[0]);
isUpdate = true;
}
this.editObj = {
show: bool,
data,
isUpdate
};
},
/**
* [request 刷新]
* @return {[type]} [description]
*/
request() {
const result = this.list_data;
let page = result.current_page;
if (this.list_data.data.length == 1) {
page = this.returnPage(result.total, result.current_page, result.per_page);
}
this.index(page);
},
resetSearch() {
for (let k in this.params) {
this.params[k] = '';
}
this.trashed = '';
this.index(1);
}
}
};