init
This commit is contained in:
87
src/views/admin/createUser.vue
Normal file
87
src/views/admin/createUser.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<h1 class="page-heading">
|
||||
创建后台用户
|
||||
</h1>
|
||||
<el-form ref="createForm" :rules="createRules" label-position="left" style='width:80%' :model="form" label-width="100px">
|
||||
<el-form-item label="用户邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="公司邮箱"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="权限选择" >
|
||||
<el-select style="width: 100%" v-model="form.role" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in roleList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loading" @click.native.prevent="onSubmit">立即创建</el-button>
|
||||
<el-button>
|
||||
<router-link class="normal_link" to="/index">
|
||||
取消
|
||||
</router-link>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { createNewUser, getRoleList } from 'api/adminUser';
|
||||
import { isWscnEmail } from 'utils/validate';
|
||||
|
||||
export default{
|
||||
name: 'createUser',
|
||||
data() {
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (!isWscnEmail(value)) {
|
||||
callback(new Error('邮箱错误'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
roleList: [],
|
||||
loading: false,
|
||||
form: {
|
||||
email: '',
|
||||
role: ''
|
||||
},
|
||||
createRules: {
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{ validator: validateEmail }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.$refs.createForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
const data = {
|
||||
email: this.form.email,
|
||||
roles: this.form.role
|
||||
};
|
||||
createNewUser(data).then(() => {
|
||||
this.$message.success('创建成功');
|
||||
});
|
||||
} else {
|
||||
this.$message.error('error submit!!');
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getRoleList().then(response => {
|
||||
const roleMap = response.data.role_map;
|
||||
const keyArr = Object.keys(roleMap);
|
||||
this.roleList = keyArr.map(v => ({ value: v, label: roleMap[v] }));
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
404
src/views/admin/profile.vue
Normal file
404
src/views/admin/profile.vue
Normal file
@@ -0,0 +1,404 @@
|
||||
<template>
|
||||
<div class="profile-container clearfix">
|
||||
<div style="position: relative;margin-left: 30px;">
|
||||
<PanThumb :image="avatar"> 你的权限:
|
||||
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
|
||||
</PanThumb>
|
||||
<el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;"
|
||||
@click="handleImagecropper">修改头像
|
||||
</el-button>
|
||||
</div>
|
||||
<!--popover-->
|
||||
<el-popover
|
||||
ref="popoverWX"
|
||||
placement="top"
|
||||
width="160"
|
||||
trigger="click"
|
||||
v-model="popoverVisibleWX">
|
||||
<p>你确定要解绑么?</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="mini" type="text" @click="popoverVisibleWX = false">取消</el-button>
|
||||
<el-button type="primary" size="mini" @click="handleUnbind('wechat')">确定</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover
|
||||
ref="popoverQQ"
|
||||
placement="top"
|
||||
width="160"
|
||||
trigger="click"
|
||||
v-model="popoverVisibleQQ">
|
||||
<p>你确定要解绑么?</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="mini" type="text" @click="popoverVisibleQQ = false">取消</el-button>
|
||||
<el-button type="primary" size="mini" @click="handleUnbind('tencent')">确定</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
<!--popover End-->
|
||||
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="line-height: 36px;">个人资料</span>
|
||||
</div>
|
||||
<div class="box-item">
|
||||
<span class="field-label">昵称</span>
|
||||
<div class="field-content">
|
||||
{{name}}
|
||||
<el-button class="edit-btn" @click="handleEditName" type="primary" icon="edit"
|
||||
size="small"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-item">
|
||||
<span class="field-label">简介</span>
|
||||
<div class="field-content">
|
||||
{{introduction.length==0?'未填写':introduction}}
|
||||
<el-button class="edit-btn" @click="handleIntroductionName" type="primary" icon="edit"
|
||||
size="small"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-item" style="margin-bottom: 10px;">
|
||||
<span class="field-label">密码</span>
|
||||
<div class="field-content">
|
||||
<el-button type="primary" @click="resetPSWdialogVisible=true">修改密码</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-item" style="margin-top: 5px;">
|
||||
<div class="field-content">
|
||||
<span class="wx-svg-container"><wscn-icon-svg icon-class="weixin" class="icon"/></span>
|
||||
<el-button class="unbind-btn" v-popover:popoverWX type="danger">解绑微信</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-item">
|
||||
<div class="field-content">
|
||||
<span class="qq-svg-container"><wscn-icon-svg icon-class="QQ" class="icon"/></span>
|
||||
<el-button class="unbind-btn" v-popover:popoverQQ style="padding: 10px 18px" type="danger">
|
||||
解绑QQ
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="line-height: 36px;">偏好设置</span>
|
||||
<el-button @click="updateSetting" style="float: right;margin-top: 5px;" size="small" type="success">
|
||||
更新偏好
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<div class="box-item">
|
||||
<span class="field-label">文章平台默认项选择:</span>
|
||||
<el-checkbox-group v-model="articlePlatform">
|
||||
<el-checkbox label="wscn-platform">见闻</el-checkbox>
|
||||
<el-checkbox label="gold-platform">黄金头条</el-checkbox>
|
||||
<el-checkbox label="weex-platform">WEEX</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<span class="field-label">使用自定义主题:</span>
|
||||
<el-switch
|
||||
v-model="theme"
|
||||
on-text=""
|
||||
off-text="">
|
||||
</el-switch>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
|
||||
<ImageCropper field="img"
|
||||
:width="300"
|
||||
:height="300"
|
||||
url="/upload"
|
||||
@crop-upload-success="cropSuccess"
|
||||
:key="imagecropperKey"
|
||||
v-show="imagecropperShow"></ImageCropper>
|
||||
|
||||
<el-dialog title="昵称" v-model="nameDialogFormVisible">
|
||||
<el-form label-position="left" label-width="50px">
|
||||
<el-form-item label="昵称" style="width: 300px;">
|
||||
<input class="input" ref="nameInput" :value="name" autocomplete="off" :maxlength=10>
|
||||
<span>(最多填写十个字符)</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="nameDialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="setName">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="简介" v-model="introductionDialogFormVisible">
|
||||
<el-form label-position="left" label-width="50px">
|
||||
<el-form-item label="简介" style="width: 500px;">
|
||||
<textarea :row=3 class="textarea" ref="introductionInput" :value="introduction"></textarea>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="introductionDialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="setIntroduction">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="提示" v-model="resetPSWdialogVisible" size="tiny">
|
||||
<span>你确定要重设密码么? <strong>     ( 注:重设密码将会登出,请注意!!! )</strong></span>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="resetPSWdialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="resetPSW">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { updateInfo, unbind, updateSetting } from 'api/adminUser';
|
||||
import ImageCropper from 'components/ImageCropper';
|
||||
import PanThumb from 'components/PanThumb';
|
||||
import { toggleClass } from 'utils'
|
||||
|
||||
export default{
|
||||
components: { ImageCropper, PanThumb },
|
||||
data() {
|
||||
return {
|
||||
nameDialogFormVisible: false,
|
||||
introductionDialogFormVisible: false,
|
||||
resetPSWdialogVisible: false,
|
||||
popoverVisibleQQ: false,
|
||||
popoverVisibleWX: false,
|
||||
imagecropperShow: false,
|
||||
imagecropperKey: 0,
|
||||
articlePlatform: [],
|
||||
theme: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.setting.articlePlatform) {
|
||||
this.articlePlatform = this.setting.articlePlatform
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'name',
|
||||
'avatar',
|
||||
'email',
|
||||
'introduction',
|
||||
'roles',
|
||||
'uid',
|
||||
'setting'
|
||||
])
|
||||
},
|
||||
watch: {
|
||||
theme() {
|
||||
toggleClass(document.body, 'custom-theme')
|
||||
// this.$store.dispatch('setTheme', value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetPSW() {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
this.$router.push({ path: '/sendpwd' })
|
||||
});
|
||||
},
|
||||
toggleResetDialog(state) {
|
||||
this.resetDialogVisible = state;
|
||||
},
|
||||
handleEditName() {
|
||||
this.nameDialogFormVisible = true;
|
||||
},
|
||||
handleIntroductionName() {
|
||||
this.introductionDialogFormVisible = true;
|
||||
},
|
||||
setName() {
|
||||
const displayName = this.$refs.nameInput.value;
|
||||
const data = {
|
||||
display_name: displayName,
|
||||
uid: this.uid
|
||||
};
|
||||
updateInfo(data).then(() => {
|
||||
this.$store.commit('SET_NAME', displayName);
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '昵称修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
this.nameDialogFormVisible = false;
|
||||
},
|
||||
setIntroduction() {
|
||||
const introduction = this.$refs.introductionInput.value;
|
||||
const data = {
|
||||
introduction,
|
||||
uid: this.uid
|
||||
};
|
||||
updateInfo(data).then(() => {
|
||||
this.$store.commit('SET_INTRODUCTION', introduction);
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '简介修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
this.introductionDialogFormVisible = false;
|
||||
},
|
||||
handleUnbind(unbindType) {
|
||||
const data = {
|
||||
unbind_type: unbindType
|
||||
};
|
||||
unbind(data).then(() => {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '解绑成功,即将登出',
|
||||
type: 'success'
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
this.$router.push({ path: '/login' })
|
||||
});
|
||||
}, 3000)
|
||||
});
|
||||
|
||||
this.popoverVisibleQQ = false;
|
||||
this.popoverVisibleWX = false;
|
||||
},
|
||||
handleImagecropper() {
|
||||
this.imagecropperShow = true;
|
||||
this.imagecropperKey = this.imagecropperKey + 1;
|
||||
},
|
||||
cropSuccess(url) {
|
||||
this.imagecropperShow = false;
|
||||
const data = {
|
||||
image: url,
|
||||
uid: this.uid
|
||||
};
|
||||
updateInfo(data).then(() => {
|
||||
this.$store.commit('SET_AVATAR', url);
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '头像修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
},
|
||||
updateSetting() {
|
||||
const obj = Object.assign(this.setting, { articlePlatform: this.articlePlatform });
|
||||
updateSetting({ setting: JSON.stringify(obj) }).then(() => {
|
||||
this.$store.commit('SET_SETTING', this.setting);
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '更新偏好成功',
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.input {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #bfcbd9;
|
||||
color: #1f2d3d;
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
height: 36px;
|
||||
line-height: 1;
|
||||
padding: 3px 10px;
|
||||
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
height: 90px;
|
||||
display: block;
|
||||
resize: vertical;
|
||||
padding: 5px 7px;
|
||||
line-height: 1.5;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: #1f2d3d;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #bfcbd9;
|
||||
border-radius: 4px;
|
||||
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: #fff;
|
||||
font-size: 30px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.wx-svg-container, .qq-svg-container {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 55px;
|
||||
}
|
||||
|
||||
.wx-svg-container {
|
||||
background-color: #8dc349;
|
||||
}
|
||||
|
||||
.qq-svg-container {
|
||||
background-color: #6BA2D6;
|
||||
}
|
||||
|
||||
.unbind-btn {
|
||||
position: absolute;
|
||||
right: -60px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.profile-container {
|
||||
padding: 20px;
|
||||
.box-card {
|
||||
width: 400px;
|
||||
margin: 30px;
|
||||
float: left;
|
||||
.field-label {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 36px;
|
||||
color: #333;
|
||||
padding-right: 30px;
|
||||
}
|
||||
.box-item {
|
||||
.field-content {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
.edit-btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: -50px;
|
||||
top: -5px;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.edit-btn {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pan-info-roles {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
92
src/views/admin/quicklycreate.vue
Normal file
92
src/views/admin/quicklycreate.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="app-container quicklyCreateUser-container">
|
||||
<el-form ref="form" :rules="rules" :model="form" label-position="left" label-width="60px">
|
||||
<el-card style=" margin-top: 50px;width: 600px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="昵称" prop="display_name">
|
||||
<el-input v-model="form.display_name"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button type="success" @click="onSubmit">立即创建</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-button style="height: 150px;width: 150px;" @click="handleImagecropper" type="primary">上传头像
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<img style=" float:right;width: 150px;height: 150px;border-radius: 50%;margin-left: 50px;"
|
||||
:src="form.image">
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-form>
|
||||
|
||||
|
||||
<el-tooltip style="position: absolute;margin-left: 750px;top: 380px" placement="top">
|
||||
<el-button>Tooltip</el-button>
|
||||
<div slot="content">昵称为必填项<br/><br/>一键创建只能创建后台虚拟账号<br/><br/>没有任何实际操作能力</div>
|
||||
</el-tooltip>
|
||||
|
||||
<ImageCropper field="img"
|
||||
:width="300"
|
||||
:height="300"
|
||||
url="/upload"
|
||||
@crop-upload-success="cropSuccess"
|
||||
:key="imagecropperKey"
|
||||
v-show="imagecropperShow">
|
||||
|
||||
</ImageCropper>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { createNewUser } from 'api/adminUser';
|
||||
import ImageCropper from 'components/ImageCropper';
|
||||
|
||||
export default{
|
||||
name: 'quicklyCreateUser',
|
||||
components: { ImageCropper },
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
display_name: '',
|
||||
image: '',
|
||||
role: ['virtual_editor']
|
||||
},
|
||||
imagecropperShow: false,
|
||||
imagecropperKey: 0,
|
||||
rules: {
|
||||
display_name: [{ required: true, message: '昵称必填', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
createNewUser(this.form).then(() => {
|
||||
this.$message.success('创建成功')
|
||||
});
|
||||
} else {
|
||||
console.log('error submit!!');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleImagecropper() {
|
||||
this.imagecropperShow = true;
|
||||
this.imagecropperKey = this.imagecropperKey + 1;
|
||||
},
|
||||
cropSuccess(url) {
|
||||
this.imagecropperShow = false;
|
||||
this.form.image = url
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
241
src/views/admin/usersList.vue
Normal file
241
src/views/admin/usersList.vue
Normal file
@@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<div class="app-container adminUsers-list-container">
|
||||
<div class="filter-container">
|
||||
<el-input @keyup.enter.native="handleFilter" style="width:135px;" class="filter-item" placeholder="ID" type="number" v-model="listQuery.uid">
|
||||
</el-input>
|
||||
|
||||
<el-input style="width:135px;" class="filter-item" placeholder="Name" @keyup.enter.native="handleFilter" v-model="listQuery.display_name">
|
||||
</el-input>
|
||||
|
||||
<el-input class="filter-item" style="width:300px;display: inline-table" placeholder="email" @keyup.enter.native="handleFilter"
|
||||
v-model="listQuery.email">
|
||||
<template slot="append">@wallstreetcn.com</template>
|
||||
</el-input>
|
||||
</el-input>
|
||||
|
||||
<el-select style="width: 250px" class="filter-item" v-model="listQuery.role" clearable placeholder="权限">
|
||||
<el-option v-for="item in roleOptions" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-button class="filter-item" type="primary" icon="search" @click="handleFilter">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" type="danger" @click="resetFilter">重置筛选项</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row>
|
||||
<el-table-column label="ID" width="130">
|
||||
<template scope="scope">
|
||||
<span>{{scope.row.uid}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Name">
|
||||
<template scope="scope">
|
||||
<span>{{scope.row.display_name}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Email">
|
||||
<template scope="scope">
|
||||
<span>{{scope.row.email}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Role">
|
||||
<template scope="scope">
|
||||
<el-tag style="margin-right: 5px;" v-for='item in scope.row.roles' :key='item+scope.row.uid' type="primary">
|
||||
{{item}}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="170">
|
||||
<template scope="scope">
|
||||
<el-button size="small" type="success" @click="handleEdit(scope.row)">编辑权限</el-button>
|
||||
<el-button size="small" v-if='scope.row.roles.indexOf("virtual_editor")>=0||hasRoleEdit' type="primary" @click="handleInfo(scope.row)">修改</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-show="!listLoading" class="pagination-container">
|
||||
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page" :page-sizes="[10,20,30, 50]"
|
||||
:page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
|
||||
</el-pagination>
|
||||
</div>
|
||||
|
||||
<el-dialog title="编辑" v-model="dialogFormVisible" size='small'>
|
||||
<el-form :model="tempForm" label-position="left" label-width="70px">
|
||||
<el-form-item label="权限">
|
||||
<el-select style="width: 100%" v-model="tempForm.roles" multiple placeholder="请选择">
|
||||
<el-option v-for="item in roleOptions" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="updateUser">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="编辑简介" v-model="dialogInfoVisible">
|
||||
<el-form :model="infoForm">
|
||||
<el-form-item label="昵称">
|
||||
<el-input v-model="infoForm.display_name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介">
|
||||
<el-input type="textarea" :autosize="{ minRows: 2}" v-model="infoForm.introduction"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像">
|
||||
|
||||
</el-form-item>
|
||||
<div style='width:200px;height:200px;'>
|
||||
<singleImageUpload2 v-model="infoForm.image"></singleImageUpload2>
|
||||
</div>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogInfoVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitInfo">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getRoleList, updateInfo } from 'api/adminUser';
|
||||
import { getUserList } from 'api/remoteSearch';
|
||||
import { objectMerge } from 'utils';
|
||||
import singleImageUpload2 from 'components/Upload/singleImage2';
|
||||
|
||||
export default {
|
||||
name: 'adminUsersList',
|
||||
components: { singleImageUpload2 },
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
total: null,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
role: '',
|
||||
uid: undefined,
|
||||
display_name: ''
|
||||
},
|
||||
roleOptions: [],
|
||||
dialogFormVisible: false,
|
||||
tempForm: {
|
||||
uid: null,
|
||||
roles: []
|
||||
},
|
||||
dialogInfoVisible: false,
|
||||
infoForm: {
|
||||
uid: null,
|
||||
image: '',
|
||||
display_name: '',
|
||||
introduction: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'roles'
|
||||
]),
|
||||
hasRoleEdit() {
|
||||
if (this.roles.indexOf('admin') >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getAdminRoleList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
getUserList(this.listQuery).then(response => {
|
||||
const data = response.data;
|
||||
this.list = data.items;
|
||||
this.total = data.count;
|
||||
this.listLoading = false;
|
||||
})
|
||||
},
|
||||
resetFilter() {
|
||||
this.listQuery = {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
role: '',
|
||||
uid: undefined,
|
||||
display_name: ''
|
||||
}
|
||||
this.getList();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.listQuery.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.listQuery.page = val;
|
||||
this.getList();
|
||||
},
|
||||
handleFilter() {
|
||||
this.getList();
|
||||
},
|
||||
getAdminRoleList() {
|
||||
getRoleList().then(response => {
|
||||
const roleMap = response.data.role_map;
|
||||
const keyArr = Object.keys(roleMap);
|
||||
this.roleOptions = keyArr.map(v => ({ value: v, label: roleMap[v] }));
|
||||
})
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.dialogFormVisible = true;
|
||||
objectMerge(this.tempForm, row);
|
||||
},
|
||||
updateUser() {
|
||||
updateInfo(this.tempForm).then(() => {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
for (const item of this.list) {
|
||||
if (item.uid === this.tempForm.uid) {
|
||||
const index = this.list.indexOf(item);
|
||||
this.list[index] = objectMerge(this.list[index], this.tempForm);
|
||||
break
|
||||
}
|
||||
}
|
||||
this.dialogFormVisible = false;
|
||||
});
|
||||
},
|
||||
handleInfo(row) {
|
||||
this.dialogInfoVisible = true;
|
||||
objectMerge(this.infoForm, row);
|
||||
},
|
||||
submitInfo() {
|
||||
updateInfo(this.infoForm).then(() => {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改成功',
|
||||
type: 'success'
|
||||
});
|
||||
for (const item of this.list) {
|
||||
if (item.uid === this.infoForm.uid) {
|
||||
const index = this.list.indexOf(item);
|
||||
this.list[index] = objectMerge(this.list[index], this.infoForm);
|
||||
break
|
||||
}
|
||||
}
|
||||
this.dialogInfoVisible = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
61
src/views/components/404.vue
Normal file
61
src/views/components/404.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="errorpage-container"> 404
|
||||
<splitPane v-on:resize="resize" split="vertical">
|
||||
<template slot="paneL">
|
||||
<div class="left-container"></div>
|
||||
</template>
|
||||
<template slot="paneR">
|
||||
<splitPane split="horizontal">
|
||||
<template slot="paneL">
|
||||
<div class="top-container"></div>
|
||||
</template>
|
||||
<template slot="paneR">
|
||||
<div class="bottom-container">
|
||||
</div>
|
||||
</template>
|
||||
</splitPane>
|
||||
</template>
|
||||
</splitPane>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import splitPane from 'components/SplitPane/SplitPane'
|
||||
export default {
|
||||
components: { splitPane },
|
||||
methods: {
|
||||
resize() {
|
||||
console.log('resize')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.errorpage-container{
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
}
|
||||
.left-container {
|
||||
background-color: #F38181;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.right-container {
|
||||
background-color: #FCE38A;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.top-container {
|
||||
background-color: #FCE38A;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
width: 100%;
|
||||
background-color: #95E1D3;
|
||||
height: 100%;
|
||||
|
||||
}
|
||||
</style>
|
22
src/views/components/markdown.vue
Normal file
22
src/views/components/markdown.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="components-container">
|
||||
<code>公司做的后台主要是一个cms系统,公司也是已自媒体为核心的,所以富文本是后台很核心的功能。在选择富文本的过程中也走了不少的弯路,市面上常见的富文本都基本用过了,最终选择了tinymce</code>
|
||||
<div class="editor-container">
|
||||
<MdEditor id='contentEditor' ref="contentEditor" v-model='content' :height="150"></MdEditor>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import MdEditor from 'components/MdEditor';
|
||||
|
||||
export default {
|
||||
components: { MdEditor },
|
||||
data() {
|
||||
return {
|
||||
content: 'Simplemde'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
28
src/views/components/tinymce.vue
Normal file
28
src/views/components/tinymce.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="components-container">
|
||||
<code>公司做的后台主要是一个cms系统,公司也是已自媒体为核心的,所以富文本是后台很核心的功能。在选择富文本的过程中也走了不少的弯路,市面上常见的富文本都基本用过了,最终选择了tinymce</code>
|
||||
<div class="editor-container">
|
||||
<Tinymce :height=200 ref="editor" v-model="content"></Tinymce>
|
||||
</div>
|
||||
<!--<div class='editor-content'>
|
||||
{{content}}
|
||||
</div>-->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Tinymce from 'components/Tinymce';
|
||||
|
||||
export default {
|
||||
components: { Tinymce },
|
||||
data() {
|
||||
return {
|
||||
content: 'Tinymce'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
75
src/views/dashboard/default/index.vue
Normal file
75
src/views/dashboard/default/index.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="dashboard-editor-container">
|
||||
<div class=" clearfix">
|
||||
<PanThumb style="float: left" :image="avatar"> 你的权限:
|
||||
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
|
||||
</PanThumb>
|
||||
<div class="info-container">
|
||||
<span class="display_name">{{name}}</span>
|
||||
<span style='font-size:20px;padding-top:20px;display:inline-block;'>赶紧把你们想要的快捷键报给产品锦鲤!</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img class='emptyGif' :src="emptyGif" >
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import PanThumb from 'components/PanThumb';
|
||||
import emptyGif from 'assets/gifs/business_fella.gif';
|
||||
export default {
|
||||
name: 'dashboard-default',
|
||||
components: { PanThumb },
|
||||
data() {
|
||||
return {
|
||||
emptyGif
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'name',
|
||||
'avatar',
|
||||
'email',
|
||||
'uid',
|
||||
'introduction',
|
||||
'roles'
|
||||
])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.emptyGif {
|
||||
display: block;
|
||||
width: 45%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.dashboard-editor-container {
|
||||
background-color: #e3e3e3;
|
||||
min-height: 100vh;
|
||||
margin-top: -50px;
|
||||
padding: 100px 60px 0px;
|
||||
.pan-info-roles {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
display: block;
|
||||
}
|
||||
.info-container {
|
||||
position: relative;
|
||||
margin-left: 190px;
|
||||
height: 150px;
|
||||
line-height: 200px;
|
||||
.display_name {
|
||||
font-size: 48px;
|
||||
line-height: 48px;
|
||||
color: #212121;
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
34
src/views/dashboard/editor/articlesChart.vue
Normal file
34
src/views/dashboard/editor/articlesChart.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="articlesChart-container">
|
||||
<span class="articlesChart-container-title">每天撸文</span>
|
||||
<line-chart :listData='listData' ></line-chart>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import LineChart from 'components/Charts/line';
|
||||
export default {
|
||||
name: 'articlesChart',
|
||||
components: { LineChart },
|
||||
props: {
|
||||
listData: {
|
||||
type: Array,
|
||||
default: [],
|
||||
require: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.articlesChart-container {
|
||||
width: 100%;
|
||||
}
|
||||
.articlesChart-container-title {
|
||||
color: #7F8C8D;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
284
src/views/dashboard/editor/index.vue
Normal file
284
src/views/dashboard/editor/index.vue
Normal file
@@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<div class="dashboard-editor-container">
|
||||
<div class=" clearfix">
|
||||
<PanThumb style="float: left" :image="avatar"> 你的权限:
|
||||
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
|
||||
</PanThumb>
|
||||
<div class="info-container">
|
||||
<span class="display_name">{{name}}</span>
|
||||
<div class="info-wrapper">
|
||||
<router-link class="info-item" :to="'/article/wscnlist?uid='+uid">
|
||||
<span class="info-item-num">{{statisticsData.article_count | toThousandslsFilter}}</span>
|
||||
<span class="info-item-text">文章</span>
|
||||
<wscn-icon-svg icon-class="a" class="dashboard-editor-icon"/>
|
||||
</router-link>
|
||||
<div class="info-item" style="cursor: auto">
|
||||
<span class="info-item-num"> {{statisticsData.pageviews_count | toThousandslsFilter}}</span>
|
||||
<span class="info-item-text">浏览量</span>
|
||||
<wscn-icon-svg icon-class="b" class="dashboard-editor-icon"/>
|
||||
</div>
|
||||
<router-link class="info-item" :to="'/comment/commentslist?res_author_id='+uid">
|
||||
<span class="info-item-num">{{statisticsData.comment_count | toThousandslsFilter}}</span>
|
||||
<span class="info-item-text">评论</span>
|
||||
<wscn-icon-svg icon-class="c" class="dashboard-editor-icon"/>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<router-link class="pan-btn blue-btn" to="/article/create">发表文章</router-link>
|
||||
<router-link class="pan-btn light-blue-btn" to="/livenews/create">发布快讯</router-link>
|
||||
<router-link class="pan-btn red-btn" to="/push/create">推送</router-link>
|
||||
<router-link class="pan-btn pink-btn" to="/comment/commentslist">评论管理</router-link>
|
||||
<router-link class="pan-btn green-btn" to="/article/wscnlist">文章列表</router-link>
|
||||
<router-link class="pan-btn tiffany-btn" to="/livenews/list">实时列表</router-link>
|
||||
</div>
|
||||
|
||||
<div class="clearfix main-dashboard-container">
|
||||
<div class="chart-container">
|
||||
<MonthKpi style="border-bottom: 1px solid #DEE1E2;"
|
||||
:articlesComplete='statisticsData.month_article_count'></MonthKpi>
|
||||
<ArticlesChart :listData='statisticsData.week_article'></ArticlesChart>
|
||||
</div>
|
||||
<div class="recent-articles-container">
|
||||
<div class="recent-articles-title">最近撸了</div>
|
||||
<div class="recent-articles-wrapper">
|
||||
<template v-if="recentArticles.length!=0">
|
||||
<div class="recent-articles-item" v-for="item in recentArticles">
|
||||
<span class="recent-articles-status">{{item.status | statusFilter}}</span>
|
||||
<router-link class="recent-articles-content" :to="'/article/edit/'+item.id">
|
||||
<span>{{item.title}}</span>
|
||||
</router-link>
|
||||
<span class="recent-articles-time"><i style="padding-right: 4px;" class="el-icon-time"></i>{{item.display_time | parseTime('{m}-{d} {h}:{i}')}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="recent-articles-emptyTitle">你太懒了最近都没有撸</div>
|
||||
<!--<img class="emptyGif" :src="emptyGif">-->
|
||||
</template>
|
||||
</div>
|
||||
<router-link class="recent-articles-more" :to="'/article/wscnlist?uid='+uid">
|
||||
Show more
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import PanThumb from 'components/PanThumb';
|
||||
import MonthKpi from './monthKpi';
|
||||
import ArticlesChart from './articlesChart';
|
||||
// import { getStatistics } from 'api/article';
|
||||
|
||||
import emptyGif from 'assets/compbig.gif';
|
||||
export default {
|
||||
name: 'dashboard-editor',
|
||||
components: { PanThumb, MonthKpi, ArticlesChart },
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
statisticsData: {
|
||||
article_count: undefined,
|
||||
comment_count: undefined,
|
||||
latest_article: [],
|
||||
month_article_count: undefined,
|
||||
pageviews_count: undefined,
|
||||
week_article: []
|
||||
},
|
||||
emptyGif
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'name',
|
||||
'avatar',
|
||||
'email',
|
||||
'uid',
|
||||
'introduction',
|
||||
'roles'
|
||||
]),
|
||||
recentArticles() {
|
||||
return this.statisticsData.latest_article.slice(0, 7)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
// getStatistics().then(response => {
|
||||
// this.statisticsData = response.data;
|
||||
// this.statisticsData.week_article = this.statisticsData.week_article.slice().reverse();
|
||||
// })
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: '已发布',
|
||||
draft: '草稿中',
|
||||
deleted: '已删除'
|
||||
};
|
||||
return statusMap[status];
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.emptyGif {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.recent-articles-emptyTitle {
|
||||
font-size: 16px;
|
||||
color: #95A5A6;
|
||||
padding-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dashboard-editor-container {
|
||||
padding: 30px 50px;
|
||||
.pan-info-roles {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
display: block;
|
||||
}
|
||||
.info-container {
|
||||
position: relative;
|
||||
margin-left: 190px;
|
||||
height: 150px;
|
||||
line-height: 200px;
|
||||
.display_name {
|
||||
font-size: 48px;
|
||||
line-height: 48px;
|
||||
color: #212121;
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
}
|
||||
.info-wrapper {
|
||||
line-height: 40px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
.info-item {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-right: 95px;
|
||||
.info-item-num {
|
||||
color: #212121;
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.info-item-text {
|
||||
color: #727272;
|
||||
font-size: 14px;
|
||||
padding-right: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dashboard-editor-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
margin: 30px 36px 30px 0;
|
||||
}
|
||||
.main-dashboard-container {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
border: 1px solid #DEE1E2;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.chart-container {
|
||||
float: left;
|
||||
position: relative;
|
||||
padding-right: 10px;
|
||||
width: 40%;
|
||||
border-right: 1px solid #DEE1E2;
|
||||
}
|
||||
.recent-articles-container {
|
||||
padding: 12px 12px 0px;
|
||||
float: left;
|
||||
width: 60%;
|
||||
position: relative;
|
||||
.recent-articles- {
|
||||
&title {
|
||||
font-size: 16px;
|
||||
color: #95A5A6;
|
||||
letter-spacing: 1px;
|
||||
padding-left: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #DEE1E2;
|
||||
}
|
||||
&more {
|
||||
color: #2C3E50;
|
||||
font-size: 12px;
|
||||
float: right;
|
||||
margin-right: 25px;
|
||||
line-height: 40px;
|
||||
&:hover {
|
||||
color: #3A71A8;
|
||||
}
|
||||
}
|
||||
&wrapper {
|
||||
padding: 0 20px 0 22px;
|
||||
.recent-articles- {
|
||||
&item {
|
||||
cursor: pointer;
|
||||
padding: 16px 100px 16px 16px;
|
||||
border-bottom: 1px solid #DEE1E2;
|
||||
position: relative;
|
||||
&:before {
|
||||
content: "";
|
||||
height: 104%;
|
||||
width: 0px;
|
||||
background: #30B08F;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: 0px;
|
||||
top: -2px;
|
||||
transition: 0.3s ease all;
|
||||
}
|
||||
&:hover {
|
||||
&:before {
|
||||
opacity: 1;
|
||||
width: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&status {
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
color: #9B9B9B;
|
||||
padding-right: 6px;
|
||||
}
|
||||
&content {
|
||||
font-size: 13px;
|
||||
color: #2C3E50;
|
||||
&:hover {
|
||||
color: #3A71A8;
|
||||
}
|
||||
}
|
||||
&time {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
color: #9B9B9B;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
61
src/views/dashboard/editor/monthKpi.vue
Normal file
61
src/views/dashboard/editor/monthKpi.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="monthKpi-container">
|
||||
<span class="monthKpi-container-title">{{month}}月</span>
|
||||
<BarPercent class="monthKpi-container-chart" :dataNum='articlesComplete'></BarPercent>
|
||||
<span class="monthKpi-container-text">文章完成比例</span>
|
||||
<span class="monthKpi-container-count">{{articlesComplete}}<b>篇</b></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BarPercent from 'components/Charts/barPercent';
|
||||
export default {
|
||||
name: 'monthKpi',
|
||||
components: { BarPercent },
|
||||
props: {
|
||||
articlesComplete: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
month: new Date().getMonth() + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.monthKpi-container{
|
||||
width: 100%;
|
||||
}
|
||||
.monthKpi-container-title {
|
||||
color: #7F8C8D;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.monthKpi-container-chart {
|
||||
margin-left: 100px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.monthKpi-container-text {
|
||||
margin-left: 112px;
|
||||
color: #9EA7B3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.monthKpi-container-count {
|
||||
color: #30B08F;
|
||||
font-size: 34px;
|
||||
position: absolute;
|
||||
left: 260px;
|
||||
top: 60px;
|
||||
b {
|
||||
padding-left: 10px;
|
||||
color: #9EA7B3;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
38
src/views/dashboard/index.vue
Normal file
38
src/views/dashboard/index.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="dashboard-container">
|
||||
<component v-bind:is="currentRole"> </component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import EditorDashboard from './editor/index';
|
||||
import DefaultDashboard from './default/index';
|
||||
export default {
|
||||
name: 'dashboard',
|
||||
components: { EditorDashboard, DefaultDashboard },
|
||||
data() {
|
||||
return {
|
||||
currentRole: 'EditorDashboard'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'name',
|
||||
'avatar',
|
||||
'email',
|
||||
'introduction',
|
||||
'roles'
|
||||
])
|
||||
},
|
||||
created() {
|
||||
if (this.roles.indexOf('admin') >= 0) {
|
||||
return;
|
||||
}
|
||||
const isEditor = this.roles.some(v => v.indexOf('editor') >= 0)
|
||||
if (!isEditor) {
|
||||
this.currentRole = 'DefaultDashboard';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
82
src/views/error/401.vue
Normal file
82
src/views/error/401.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="errPage-container">
|
||||
<el-button @click="back" icon='arrow-left' class="pan-back-btn">返回</el-button>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h1 class="text-jumbo text-ginormous">Oops!</h1>
|
||||
<h2>你没有权限去该页面</h2>
|
||||
<h6>如有不满请联系你领导</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li>或者你可以去:</li>
|
||||
<li class="link-type">
|
||||
<router-link to="/dashboard">回首页</router-link>
|
||||
</li>
|
||||
<li class="link-type"><a href="https://www.taobao.com/">随便看看</a></li>
|
||||
<li><a @click="dialogVisible=true" href="#">点我看图</a></li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-dialog title="随便看" v-model="dialogVisible" size="large">
|
||||
<img class="pan-img" :src="ewizardClap">
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import errGif from 'assets/401.gif';
|
||||
import ewizardClap from 'assets/gifs/wizard_clap.gif';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
errGif: errGif + '?' + +new Date(),
|
||||
ewizardClap,
|
||||
dialogVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.errPage-container {
|
||||
width: 800px;
|
||||
margin: 100px auto;
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
}
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
.pan-img{
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.text-jumbo {
|
||||
font-size: 60px;
|
||||
font-weight: 700;
|
||||
color: #484848;
|
||||
}
|
||||
.list-unstyled {
|
||||
font-size: 14px;
|
||||
li {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
a {
|
||||
color: #008489;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
61
src/views/error/404.vue
Normal file
61
src/views/error/404.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="errorpage-container"> 404
|
||||
<splitPane v-on:resize="resize" split="vertical">
|
||||
<template slot="paneL">
|
||||
<div class="left-container"></div>
|
||||
</template>
|
||||
<template slot="paneR">
|
||||
<splitPane split="horizontal">
|
||||
<template slot="paneL">
|
||||
<div class="top-container"></div>
|
||||
</template>
|
||||
<template slot="paneR">
|
||||
<div class="bottom-container">
|
||||
</div>
|
||||
</template>
|
||||
</splitPane>
|
||||
</template>
|
||||
</splitPane>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import splitPane from 'components/SplitPane/SplitPane'
|
||||
export default {
|
||||
components: { splitPane },
|
||||
methods: {
|
||||
resize() {
|
||||
console.log('resize')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.errorpage-container{
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
}
|
||||
.left-container {
|
||||
background-color: #F38181;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.right-container {
|
||||
background-color: #FCE38A;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.top-container {
|
||||
background-color: #FCE38A;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
width: 100%;
|
||||
background-color: #95E1D3;
|
||||
height: 100%;
|
||||
|
||||
}
|
||||
</style>
|
20
src/views/layout/AppMain.vue
Normal file
20
src/views/layout/AppMain.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<section class="app-main" style="min-height: 100%">
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view :key="key"></router-view>
|
||||
</transition>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppMain',
|
||||
computed: {
|
||||
key() {
|
||||
return this.$route.name !== undefined
|
||||
? this.$route.name + +new Date()
|
||||
: this.$route + +new Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
98
src/views/layout/Layout.vue
Normal file
98
src/views/layout/Layout.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
|
||||
<div class="sidebar-wrapper">
|
||||
<Sidebar class="sidebar-container"/>
|
||||
</div>
|
||||
<div class="main-container">
|
||||
<Navbar/>
|
||||
<App-main/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Navbar, Sidebar, AppMain } from 'views/layout';
|
||||
import store from 'store';
|
||||
import router from 'router';
|
||||
import permission from 'store/permission';
|
||||
// import { Loading } from 'element-ui';
|
||||
// let loadingInstance;
|
||||
export default {
|
||||
name: 'layout',
|
||||
components: {
|
||||
Navbar,
|
||||
Sidebar,
|
||||
AppMain
|
||||
},
|
||||
computed: {
|
||||
sidebar() {
|
||||
return this.$store.state.app.sidebar;
|
||||
}
|
||||
},
|
||||
beforeRouteEnter: (to, from, next) => {
|
||||
console.log('b')
|
||||
const roles = store.getters.roles;
|
||||
if (roles.length !== 0) {
|
||||
next();
|
||||
return
|
||||
}
|
||||
// loadingInstance = Loading.service({ fullscreen: true, text: '玩命加载中' });
|
||||
store.dispatch('GetInfo').then(() => {
|
||||
permission.init({
|
||||
roles: store.getters.roles,
|
||||
router: router.options.routes
|
||||
});
|
||||
// loadingInstance.close();
|
||||
next();
|
||||
}).catch(err => {
|
||||
// loadingInstance.close();
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
@import "src/styles/mixin.scss";
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding-left: 180px;
|
||||
&.hideSidebar {
|
||||
padding-left: 40px;
|
||||
.sidebar-wrapper {
|
||||
transform: translate(-140px, 0);
|
||||
.sidebar-container {
|
||||
transform: translate(132px, 0);
|
||||
}
|
||||
&:hover {
|
||||
transform: translate(0, 0);
|
||||
.sidebar-container {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sidebar-wrapper {
|
||||
width: 180px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow-x: hidden;
|
||||
transition: all .28s ease-out;
|
||||
@include scrollBar;
|
||||
}
|
||||
.sidebar-container {
|
||||
transition: all .28s ease-out;
|
||||
}
|
||||
.main-container {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
transition: all .28s ease-out;
|
||||
}
|
||||
}
|
||||
</style>
|
48
src/views/layout/Levelbar.vue
Normal file
48
src/views/layout/Levelbar.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-levelbar" separator="/">
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item">
|
||||
<router-link v-if='item.redirect==="noredirect"||index==levelList.length-1' to="" class="no-redirect">{{item.name}}</router-link>
|
||||
<router-link v-else :to="item.path">{{item.name}}</router-link>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created() {
|
||||
this.getBreadcrumb()
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
levelList: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumb() {
|
||||
let matched = this.$route.matched.filter(item => item.name);
|
||||
const first = matched[0];
|
||||
if (first && (first.name !== '首页' || first.path !== '')) {
|
||||
matched = [{ name: '首页', path: '/' }].concat(matched)
|
||||
}
|
||||
this.levelList = matched;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.getBreadcrumb();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.app-levelbar.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 10px;
|
||||
.no-redirect{
|
||||
color: #97a8be;
|
||||
cursor:text;
|
||||
}
|
||||
}
|
||||
</style>
|
107
src/views/layout/Navbar.vue
Normal file
107
src/views/layout/Navbar.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<el-menu class="navbar" mode="horizontal">
|
||||
<Hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></Hamburger>
|
||||
<levelbar></levelbar>
|
||||
<ErrLog v-if="log.length>0" class="errLog-container" :logsList="log"></ErrLog>
|
||||
<el-dropdown class="avatar-container" trigger="click">
|
||||
<div class="avatar-wrapper">
|
||||
<img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'">
|
||||
<i class="el-icon-caret-bottom"/>
|
||||
</div>
|
||||
<el-dropdown-menu class="user-dropdown" slot="dropdown">
|
||||
<router-link class='inlineBlock' to="/">
|
||||
<el-dropdown-item>
|
||||
首页
|
||||
</el-dropdown-item>
|
||||
</router-link>
|
||||
<router-link class='inlineBlock' to="/admin/profile">
|
||||
<el-dropdown-item>
|
||||
设置
|
||||
</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item divided><span @click="logout" style="display:block;">退出登录</span></el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Levelbar from './Levelbar';
|
||||
import Hamburger from 'components/Hamburger';
|
||||
import ErrLog from 'components/ErrLog';
|
||||
import errLogStore from 'store/errLog';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Levelbar,
|
||||
Hamburger,
|
||||
ErrLog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
log: errLogStore.state.errLog
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
'name',
|
||||
'avatar'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('ToggleSideBar')
|
||||
},
|
||||
logout() {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
this.$router.push({ path: '/login' })
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.navbar {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
border-radius: 0px !important;
|
||||
.hamburger-container {
|
||||
line-height: 58px;
|
||||
height: 50px;
|
||||
float: left;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.errLog-container {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 150px;
|
||||
}
|
||||
.avatar-container {
|
||||
height: 50px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 35px;
|
||||
.avatar-wrapper {
|
||||
cursor: pointer;
|
||||
margin-top:5px;
|
||||
position: relative;
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.el-icon-caret-bottom {
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
48
src/views/layout/Sidebar.vue
Normal file
48
src/views/layout/Sidebar.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<el-menu mode="vertical" theme="dark" :default-active="$route.path">
|
||||
<template v-for="item in permissionRoutes" v-if="!item.hidden">
|
||||
<el-submenu :index="item.name" v-if="!item.noDropdown">
|
||||
<template slot="title">
|
||||
<wscn-icon-svg :icon-class="item.icon||'wenzhang1'"/>
|
||||
{{item.name}}
|
||||
</template>
|
||||
<router-link v-for="child in item.children" :key="child.path" v-if="!child.hidden"
|
||||
class="title-link" :to="item.path+'/'+child.path + '#'+ +new Date()">
|
||||
<el-menu-item :index="item.path+'/'+child.path">
|
||||
{{child.name}}
|
||||
</el-menu-item>
|
||||
</router-link>
|
||||
</el-submenu>
|
||||
<router-link v-if="item.noDropdown&&item.children.length>0" class="title-link"
|
||||
:to="item.path+'/'+item.children[0].path">
|
||||
<el-menu-item
|
||||
:index="item.path+'/'+item.children[0].path +'#'+ +new Date()">
|
||||
<wscn-icon-svg :icon-class="item.icon||'geren1'"/>
|
||||
{{item.children[0].name}}
|
||||
</el-menu-item>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import permissionRoutes from 'store/permission';
|
||||
|
||||
export default {
|
||||
name: 'Sidebar',
|
||||
data() {
|
||||
return {
|
||||
permissionRoutes: permissionRoutes.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.el-menu {
|
||||
min-height: 100%;
|
||||
}
|
||||
.wscn-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
7
src/views/layout/index.js
Normal file
7
src/views/layout/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export { default as Navbar } from './Navbar';
|
||||
|
||||
export { default as Sidebar } from './Sidebar';
|
||||
|
||||
export { default as Levelbar } from './Sidebar';
|
||||
|
||||
export { default as AppMain } from './AppMain';
|
10
src/views/login/authredirect.vue
Normal file
10
src/views/login/authredirect.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'authredirect',
|
||||
created() {
|
||||
const hash = window.location.search.slice(1);
|
||||
window.opener.location.href = window.location.origin + '/login#' + hash;
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
</script>
|
188
src/views/login/index.vue
Normal file
188
src/views/login/index.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<el-form autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left"
|
||||
label-width="0px"
|
||||
class="card-box login-form">
|
||||
<h3 class="title">系统登录</h3>
|
||||
<el-form-item prop="email">
|
||||
<span class="svg-container"><wscn-icon-svg icon-class="jiedianyoujian"/></span>
|
||||
<el-input name="email" type="text" v-model="loginForm.email" autoComplete="on"
|
||||
placeholder="邮箱"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<span class="svg-container"><wscn-icon-svg icon-class="mima"/></span>
|
||||
<el-input name="password" type="password" @keyup.enter.native="handleLogin" v-model="loginForm.password"
|
||||
autoComplete="on" placeholder="密码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" style="width:100%;" :loading="loading" @click.native.prevent="handleLogin">
|
||||
登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<router-link to="/sendpwd" class="forget-pwd">
|
||||
忘记密码?(或首次登录)
|
||||
</router-link>
|
||||
</el-form>
|
||||
<el-dialog title="第三方验证" v-model="showDialog">
|
||||
邮箱登录成功,请选择第三方验证
|
||||
<socialSign></socialSign>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { isWscnEmail } from 'utils/validate';
|
||||
import { getQueryObject } from 'utils';
|
||||
import socialSign from './socialsignin';
|
||||
|
||||
export default {
|
||||
components: { socialSign },
|
||||
name: 'login',
|
||||
data() {
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (!isWscnEmail(value)) {
|
||||
callback(new Error('请输入正确的合法邮箱'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validatePass = (rule, value, callback) => {
|
||||
if (value.length < 6) {
|
||||
callback(new Error('密码不能小于6位'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
loginForm: {
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
loginRules: {
|
||||
email: [
|
||||
{ required: true, trigger: 'blur', validator: validateEmail }
|
||||
],
|
||||
password: [
|
||||
{ required: true, trigger: 'blur', validator: validatePass }
|
||||
]
|
||||
},
|
||||
loading: false,
|
||||
showDialog: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'status',
|
||||
'auth_type'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
handleLogin() {
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
this.$store.dispatch('LoginByEmail', this.loginForm).then(() => {
|
||||
this.loading = false;
|
||||
this.$router.push({ path: '/' });
|
||||
// this.showDialog = true;
|
||||
}).catch(err => {
|
||||
this.$message.error(err);
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
console.log('error submit!!');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
afterQRScan() {
|
||||
const hash = window.location.hash.slice(1);
|
||||
const hashObj = getQueryObject(hash);
|
||||
const originUrl = window.location.origin;
|
||||
history.replaceState({}, '', originUrl);
|
||||
const codeMap = {
|
||||
wechat: 'code',
|
||||
tencent: 'code'
|
||||
};
|
||||
const codeName = hashObj[codeMap[this.auth_type]];
|
||||
if (!codeName) {
|
||||
alert('第三方登录失败');
|
||||
} else {
|
||||
this.$store.dispatch('LoginByThirdparty', codeName).then(() => {
|
||||
this.$router.push({ path: '/' });
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.addEventListener('hashchange', this.afterQRScan);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('hashchange', this.afterQRScan);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoprd>
|
||||
@import "src/styles/mixin.scss";
|
||||
|
||||
.login-container {
|
||||
@include relative;
|
||||
height: 100vh;
|
||||
background-color: #2d3a4b;
|
||||
|
||||
input:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0px 1000px #293444 inset !important;
|
||||
-webkit-text-fill-color: #fff !important;
|
||||
}
|
||||
input {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 0px;
|
||||
padding: 12px 5px 12px 15px;
|
||||
color: #eeeeee;
|
||||
height: 47px;
|
||||
}
|
||||
.el-input {
|
||||
display: inline-block;
|
||||
height: 47px;
|
||||
width: 85%;
|
||||
}
|
||||
.svg-container {
|
||||
padding: 6px 5px 6px 15px;
|
||||
color: #889aa4;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
color: #eeeeee;
|
||||
margin: 0px auto 40px auto;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 400px;
|
||||
padding: 35px 35px 15px 35px;
|
||||
margin: 120px auto;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
color: #454545;
|
||||
}
|
||||
|
||||
.forget-pwd {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
178
src/views/login/reset.vue
Normal file
178
src/views/login/reset.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div class="reset-container">
|
||||
<el-form autoComplete="on" :model="resetForm" :rules="resetRules" ref="resetForm" label-position="left"
|
||||
label-width="0px"
|
||||
class="card-box reset-form">
|
||||
<div>
|
||||
<router-link to="/login" class="back-icon">
|
||||
<i class="el-icon-arrow-left"></i>
|
||||
</router-link>
|
||||
<h3 class="title">重设密码</h3>
|
||||
</div>
|
||||
<el-form-item prop="email">
|
||||
<el-input name="email" type="text" v-model="resetForm.email"
|
||||
placeholder="邮箱"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-input name="code" type="text" v-model="resetForm.code"
|
||||
placeholder="验证码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input name="password" :type="passwordType" v-model="resetForm.password"
|
||||
placeholder="密码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="checkPass">
|
||||
<el-input name="checkPass" :type="passwordType"
|
||||
v-model="resetForm.checkPass"
|
||||
placeholder="确认密码"></el-input>
|
||||
<i @click="togglePasswordType" class="el-icon-information"></i>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item style="width:100%;">
|
||||
<el-button type="primary" style="width:100%;" :loading="loading" @click.native.prevent="setPWD">
|
||||
修改密码
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isWscnEmail } from 'utils/validate';
|
||||
// import { restPWD } from 'api/login';
|
||||
|
||||
export default {
|
||||
name: 'reset',
|
||||
data() {
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (!isWscnEmail(value)) {
|
||||
callback(new Error('邮箱错误'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validaePass = (rule, value, callback) => {
|
||||
if (value.length < 6) {
|
||||
callback(new Error('密码不能小于6位'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validatePass2 = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'));
|
||||
} else if (value !== this.resetForm.password) {
|
||||
callback(new Error('两次输入密码不一致!'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
resetForm: {
|
||||
email: '',
|
||||
password: '',
|
||||
checkPass: '',
|
||||
code: ''
|
||||
},
|
||||
passwordType: 'password',
|
||||
resetRules: {
|
||||
email: [
|
||||
{ required: true, trigger: 'blur', validator: validateEmail }
|
||||
],
|
||||
password: [
|
||||
{ required: true, trigger: 'blur', validator: validaePass }
|
||||
],
|
||||
checkPass: [
|
||||
{ required: true, trigger: 'blur', validator: validatePass2 }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '必填项', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setPWD() {
|
||||
this.loading = true;
|
||||
const _this = this;
|
||||
this.$refs.resetForm.validate(valid => {
|
||||
if (valid) {
|
||||
const data = {
|
||||
email: this.resetForm.email,
|
||||
code: this.resetForm.code,
|
||||
new_password: this.resetForm.checkPass
|
||||
};
|
||||
// restPWD(data).then(() => {
|
||||
// this.$message.success('密码设置成功,五秒后调整到登录页');
|
||||
// setTimeout(() => {
|
||||
// _this.$router.push({ path: '/login' })
|
||||
// }, 5 * 1000)
|
||||
// });
|
||||
} else {
|
||||
this.$message.error('error submit!!');
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
togglePasswordType() {
|
||||
if (this.passwordType === 'text') {
|
||||
this.passwordType = 'password'
|
||||
} else {
|
||||
this.passwordType = 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
@import "src/styles/mixin.scss";
|
||||
|
||||
.reset-container {
|
||||
input:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0px 1000px #293444 inset !important;
|
||||
-webkit-text-fill-color: #3E3E3E !important;
|
||||
}
|
||||
@include relative;
|
||||
height: 100vh;
|
||||
background-color: #324057;
|
||||
.back-icon {
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.el-icon-information {
|
||||
position: absolute;
|
||||
right: -18px;
|
||||
top: 10px;
|
||||
}
|
||||
.reset-form {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 350px;
|
||||
padding: 35px 35px 15px 35px;
|
||||
margin: 120px auto;
|
||||
}
|
||||
|
||||
.card-box {
|
||||
padding: 20px;
|
||||
box-shadow: 0 0px 8px 0 rgba(0, 0, 0, 0.06), 0 1px 0px 0 rgba(0, 0, 0, 0.02);
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
background-clip: padding-box;
|
||||
margin-bottom: 20px;
|
||||
background-color: #F9FAFC;
|
||||
width: 400px;
|
||||
border: 2px solid #8492A6;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0px auto 40px auto;
|
||||
text-align: center;
|
||||
color: #505458;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
117
src/views/login/sendpwd.vue
Normal file
117
src/views/login/sendpwd.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div class="sendpwd-container">
|
||||
<el-form autoComplete="on" :model="resetForm" :rules="resetRules" ref="resetForm" label-position="left"
|
||||
label-width="0px"
|
||||
class="card-box reset-form">
|
||||
<div>
|
||||
<router-link to="/login" class="back-icon">
|
||||
<i class="el-icon-arrow-left"></i>
|
||||
</router-link>
|
||||
<h3 class="title">发送验证码至邮箱</h3>
|
||||
</div>
|
||||
<el-form-item prop="email">
|
||||
<el-input name="email" type="text" v-model="resetForm.email"
|
||||
placeholder="邮箱"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item style="width:100%;">
|
||||
<el-button type="primary" style="width:100%;" :loading="loading" @click.native.prevent="handleSendPWD">
|
||||
发送验证码至邮箱
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<router-link to="/reset">
|
||||
<el-button type="info" style="width:100%;">
|
||||
已收到验证码,去重设密码
|
||||
</el-button>
|
||||
</router-link>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isWscnEmail } from 'utils/validate';
|
||||
// import { sendPWD2Email } from 'api/login';
|
||||
|
||||
export default {
|
||||
name: 'reset',
|
||||
data() {
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (!isWscnEmail(value)) {
|
||||
callback(new Error('请输入正确的邮箱'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
resetForm: {
|
||||
email: ''
|
||||
},
|
||||
resetRules: {
|
||||
email: [
|
||||
{ required: true, trigger: 'blur' },
|
||||
{ validator: validateEmail }
|
||||
]
|
||||
},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSendPWD() {
|
||||
this.loading = true;
|
||||
this.$refs.resetForm.validate(valid => {
|
||||
if (valid) {
|
||||
// sendPWD2Email(this.resetForm.email).then(() => {
|
||||
// this.$message.success('密码有发送至邮箱,请查收')
|
||||
// });
|
||||
} else {
|
||||
this.$message.error('错误提交!!');
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.sendpwd-container {
|
||||
height: 100vh;
|
||||
background-color: #2d3a4b;
|
||||
input:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0px 1000px #293444 inset !important;
|
||||
-webkit-text-fill-color: #3E3E3E !important;
|
||||
}
|
||||
.back-icon{
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.reset-form {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 350px;
|
||||
padding: 35px 35px 15px 35px;
|
||||
margin: 120px auto;
|
||||
}
|
||||
|
||||
.card-box {
|
||||
padding: 20px;
|
||||
box-shadow: 0 0px 8px 0 rgba(0, 0, 0, 0.06), 0 1px 0px 0 rgba(0, 0, 0, 0.02);
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
background-clip: padding-box;
|
||||
margin-bottom: 20px;
|
||||
background-color: #F9FAFC;
|
||||
width: 400px;
|
||||
border: 2px solid #8492A6;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0px auto 40px auto;
|
||||
text-align: center;
|
||||
color: #505458;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
68
src/views/login/socialsignin.vue
Normal file
68
src/views/login/socialsignin.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="social-signup-container">
|
||||
<div class="sign-btn" @click="wechatHandleClick('wechat')">
|
||||
<span class="wx-svg-container"><wscn-icon-svg icon-class="weixin" class="icon"/></span>
|
||||
微信
|
||||
</div>
|
||||
<div class="sign-btn" @click="tencentHandleClick('tencent')">
|
||||
<span class="qq-svg-container"><wscn-icon-svg icon-class="QQ" class="icon"/></span>
|
||||
QQ
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import openWindow from 'utils/openWindow';
|
||||
|
||||
export default {
|
||||
name: 'social-signin',
|
||||
methods: {
|
||||
wechatHandleClick(thirdpart) {
|
||||
this.$store.commit('SET_AUTH_TYPE', thirdpart);
|
||||
const appid = 'wxff5aaaad72308d46';
|
||||
const redirect_uri = encodeURIComponent('http://wallstreetcn.com/auth/redirect?redirect=' + window.location.origin + '/authredirect');
|
||||
const url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + appid + '&redirect_uri=' + redirect_uri + '&response_type=code&scope=snsapi_login#wechat_redirect';
|
||||
openWindow(url, thirdpart, 540, 540);
|
||||
},
|
||||
tencentHandleClick(thirdpart) {
|
||||
this.$store.commit('SET_AUTH_TYPE', thirdpart);
|
||||
const client_id = '101150108';
|
||||
const redirect_uri = encodeURIComponent('http://wallstreetcn.com/auth/redirect?redirect=' + window.location.origin + '/authredirect');
|
||||
const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri;
|
||||
openWindow(url, thirdpart, 540, 540);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.social-signup-container {
|
||||
margin: 20px 0;
|
||||
.sign-btn {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon {
|
||||
color: #fff;
|
||||
font-size: 30px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.wx-svg-container, .qq-svg-container {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.wx-svg-container {
|
||||
background-color: #8dc349;
|
||||
}
|
||||
.qq-svg-container {
|
||||
background-color: #6BA2D6;
|
||||
margin-left: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
102
src/views/others/mediaUpload.vue
Normal file
102
src/views/others/mediaUpload.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="app-container mediaUpload-container">
|
||||
<el-upload
|
||||
class="upload-container"
|
||||
action="https://upload.qbox.me"
|
||||
:data="dataObj"
|
||||
drag
|
||||
:multiple="true"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:on-success="handleSuccess"
|
||||
:before-upload="beforeUpload">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
|
||||
<template v-if='fileList.length!=0'>
|
||||
<el-table
|
||||
:data="fileList"
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column label="名字">
|
||||
<template scope="scope">
|
||||
{{scope.row.name}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="url">
|
||||
<template scope="scope">
|
||||
{{scope.row.url}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getToken } from 'api/qiniu';
|
||||
|
||||
export default{
|
||||
data() {
|
||||
return {
|
||||
image_uri: [],
|
||||
dataObj: { token: '', key: '' },
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
token() {
|
||||
return this.$store.getters.token
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
beforeUpload() {
|
||||
const _self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
getToken(this.token).then(response => {
|
||||
const key = response.data.qiniu_key;
|
||||
const token = response.data.qiniu_token;
|
||||
this.addFile(key, response.data.qiniu_url);
|
||||
_self._data.dataObj.token = token;
|
||||
_self._data.dataObj.key = key;
|
||||
resolve(true);
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
reject(false)
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRemove(file, fileList) {
|
||||
console.log(file, fileList);
|
||||
},
|
||||
handlePreview(file) {
|
||||
console.log(file);
|
||||
},
|
||||
handleSuccess(response, file) {
|
||||
const key = response.key;
|
||||
for (let i = this.fileList.length; i--;) {
|
||||
const item = this.fileList[i];
|
||||
if (item.key === key) {
|
||||
this.fileList[i].name = file.name;
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addFile(key, url) {
|
||||
this.fileList.push({
|
||||
key,
|
||||
url,
|
||||
name: ''
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.mediaUpload-container {
|
||||
.upload-container {
|
||||
margin: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
20
src/views/previewLayout/Layout.vue
Normal file
20
src/views/previewLayout/Layout.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<section class="app-main" style="min-height: 100%">
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view :key="key"></router-view>
|
||||
</transition>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppMain',
|
||||
computed: {
|
||||
key() {
|
||||
return this.$route.name !== undefined
|
||||
? this.$route.name
|
||||
: this.$route
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
118
src/views/user/components/info.vue
Normal file
118
src/views/user/components/info.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="fedUser-info-container">
|
||||
<el-button type="success" v-if="hasPermission" style='position: absolute; top: 20px;left: 50%'
|
||||
@click="updateForm">更新
|
||||
</el-button>
|
||||
<el-form style="margin:0 50px;width: 400px" label-position="left"
|
||||
label-width="100px">
|
||||
<el-form-item label="昵称">
|
||||
<el-input v-model="form.base_info.display_name"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="form.base_info.new_password"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="form.base_info.email"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="form.base_info.mobile"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="真实姓名">
|
||||
<el-input v-model="form.extented_info.real_name"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="生日">
|
||||
<el-input v-model="form.extented_info.birthday"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="form.base_info.gender" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in genderOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="教育背景">
|
||||
<el-select v-model="form.extented_info.education" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in educationOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="收入">
|
||||
<el-select v-model="form.extented_info.income" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in incomeOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="自我介绍">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4}"
|
||||
placeholder="请输入内容"
|
||||
v-model=" form.extented_info.introduction">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { updateUserInfo } from 'api/user';
|
||||
export default {
|
||||
name: 'fedUser-info',
|
||||
props: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
genderOptions: ['male', 'female', 'other'],
|
||||
educationOptions: ['high_school', 'bachelor', 'master', 'Ph.D.', 'other'],
|
||||
incomeOptions: ['3000', '5000', '8000', 'other']
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
form() {
|
||||
return this.info
|
||||
},
|
||||
hasPermission() {
|
||||
return ~this.$store.getters.roles.indexOf('admin')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateForm() {
|
||||
updateUserInfo(this.form).then(() => {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '更新成功',
|
||||
type: 'success'
|
||||
});
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.fedUser-detail-container {
|
||||
|
||||
}
|
||||
</style>
|
125
src/views/user/detail.vue
Normal file
125
src/views/user/detail.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="fedUser-detail-container" v-loading="!fetchSuccess">
|
||||
<div v-if="fetchSuccess" class="top-container clearfix">
|
||||
<el-col :span="5">
|
||||
<img class="info-avatar" :src="userInfo.base_info.image">
|
||||
</el-col>
|
||||
<el-col :span="16" :offset="2">
|
||||
<div class="info-item">
|
||||
<span class="info-label">用户名</span>
|
||||
<span class="info-text">{{userInfo.base_info.username}}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">昵称</span>
|
||||
<span class="info-text">{{userInfo.base_info.display_name}}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">手机号</span>
|
||||
<span class="info-text">{{userInfo.base_info.mobile}}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">余额</span>
|
||||
<span class="info-text">{{userInfo.banance}}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">ios余额</span>
|
||||
<span class="info-text">{{userInfo.ios_banance}}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">注册日期</span>
|
||||
<span class="info-text">{{userInfo.created_at | parseTime('{y}-{m}-{d} {h}:{i}')}} 注册渠道:{{userInfo.signup_method}}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">最后登录</span>
|
||||
<span class="info-text">{{userInfo.last_signin_time | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</div>
|
||||
|
||||
|
||||
<el-tabs v-if="fetchSuccess" v-model="activeTab">
|
||||
|
||||
<el-tab-pane label="基本信息" name="info">
|
||||
<Info :info="userInfo"></Info>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="评论记录" name="information">
|
||||
<Comment :user_id="userInfo.uid"></Comment>
|
||||
</el-tab-pane>
|
||||
|
||||
<!--<el-tab-pane label="消费记录" name="stream">
|
||||
|
||||
</el-tab-pane>-->
|
||||
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userInfo } from 'api/user';
|
||||
import Info from './components/info';
|
||||
import Comment from '../comment/commentsList'
|
||||
|
||||
export default {
|
||||
name: 'fedUser-detail',
|
||||
components: { Info, Comment },
|
||||
data() {
|
||||
return {
|
||||
userInfo: {},
|
||||
activeTab: 'info',
|
||||
fetchSuccess: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
userInfo(this.$route.params.id).then(response => {
|
||||
this.userInfo = response.data;
|
||||
this.fetchSuccess = true;
|
||||
}).catch(err => {
|
||||
this.fetchSuccess = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.fedUser-detail-container {
|
||||
padding: 30px;
|
||||
.top-container {
|
||||
margin-bottom: 30px;
|
||||
.info-item {
|
||||
line-height: 14px;
|
||||
padding-bottom: 18px;
|
||||
.info-label {
|
||||
display: inline-block;
|
||||
color: #1f2f3d;
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
}
|
||||
.info-text {
|
||||
margin-left: 120px;
|
||||
font-size: 14px;
|
||||
color: #5e6d82;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.info-avatar {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
183
src/views/user/list.vue
Normal file
183
src/views/user/list.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div class="app-container topic-list-container">
|
||||
<div class="filter-container">
|
||||
<el-input
|
||||
style="width:200px"
|
||||
@keyup.enter.native="handleFilter"
|
||||
class="filter-item"
|
||||
placeholder="display_name"
|
||||
v-model="display_name">
|
||||
</el-input>
|
||||
<el-input
|
||||
style="width:200px"
|
||||
@keyup.enter.native="handleFilter"
|
||||
class="filter-item"
|
||||
placeholder="username"
|
||||
v-model="username">
|
||||
</el-input>
|
||||
<el-select v-model="status" placeholder="状态" >
|
||||
<el-option
|
||||
v-for="item in statusOptions"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button class="filter-item" style="margin-left: 30px;" type="primary" icon="search"
|
||||
@click="handleFilter">搜索
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row>
|
||||
<el-table-column header prop="id" label="uid" width="160">
|
||||
<template scope="scope">
|
||||
<span style="margin-left: 10px">{{scope.row.uid}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="display_name" show-overflow-tooltip>
|
||||
<template scope="scope">
|
||||
<router-link class="link-type" :to="'/user/'+scope.row.uid">
|
||||
{{scope.row.display_name}}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="username" show-overflow-tooltip>
|
||||
<template scope="scope">
|
||||
<router-link class="link-type" :to="'/user/'+scope.row.uid">
|
||||
{{scope.row.username}}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="手机号" width="150">
|
||||
<template scope="scope">
|
||||
<span>{{scope.row.mobile}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="120" align='center'>
|
||||
<template scope="scope">
|
||||
<el-button v-if='condition.status==""' size="small" type="warning" @click="handleModifyUserStatus('frozen',scope.row)">
|
||||
注销用户
|
||||
</el-button>
|
||||
<el-button v-else type="info" size="small" @click="handleModifyUserStatus('',scope.row)">解禁用户
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-show="!listLoading" class="pagination-container">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="listQuery.page"
|
||||
:page-sizes="[10,20,30, 50]"
|
||||
:page-size="listQuery.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { usersList, modifyStatus } from 'api/user';
|
||||
export default {
|
||||
name: 'fedUserList',
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
total: null,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
app_type: 'wscn',
|
||||
condition: ''
|
||||
},
|
||||
display_name: undefined,
|
||||
username: undefined,
|
||||
status: '',
|
||||
statusOptions: [{ label: '正常', value: '' }, { label: '已删除', value: 'frozen' }],
|
||||
condition: {
|
||||
status: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchList();
|
||||
},
|
||||
watch: {
|
||||
display_name(value) {
|
||||
if (!value) return;
|
||||
this.condition = {
|
||||
display_name: value
|
||||
};
|
||||
this.username = '';
|
||||
},
|
||||
username(value) {
|
||||
if (!value) return;
|
||||
this.condition = {
|
||||
username: value
|
||||
};
|
||||
this.display_name = '';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchList() {
|
||||
this.condition.status = this.status;
|
||||
this.listQuery.condition = JSON.stringify(this.condition);
|
||||
usersList(this.listQuery).then(response => {
|
||||
const data = response.data;
|
||||
this.list = data.items;
|
||||
this.total = data.total_count;
|
||||
this.listLoading = false;
|
||||
})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.listQuery.limit = val;
|
||||
this.fetchList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.listQuery.page = val;
|
||||
this.fetchList();
|
||||
},
|
||||
handleFilter() {
|
||||
this.fetchList();
|
||||
},
|
||||
handleModifyUserStatus(status, row) {
|
||||
const msg = status === 'frozen' ? '注销' : '恢复';
|
||||
this.$confirm('是否确' + msg + '用户:' + row.display_name || row.username, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === 'confirm') {
|
||||
modifyStatus(status, [row.uid]).then(() => {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: msg + '成功',
|
||||
type: 'success',
|
||||
duration: 1600
|
||||
});
|
||||
for (const i of this.list) {
|
||||
if (i.uid === row.uid) {
|
||||
const index = this.list.indexOf(i);
|
||||
this.list.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
done();
|
||||
}).catch(() => {
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Reference in New Issue
Block a user