提交 426b1046 authored 作者: pengxiaohui's avatar pengxiaohui

update

上级 bd8d3d44
module.exports = { module.exports = {
domain: 'dev.ezijing.com', domain: 'dev.ezijing.com',
url: 'https://cms-admin2.ezijing.com/api', url: 'https://certs-admin2.ezijing.com/api',
webpack: { webpack: {
externals: { externals: {
CKEDITOR: 'window.CKEDITOR', CKEDITOR: 'window.CKEDITOR',
......
...@@ -8,6 +8,12 @@ export function logout() { ...@@ -8,6 +8,12 @@ export function logout() {
export function getUser() { export function getUser() {
return httpRequest.get('/api/passport/account/get-user-info') return httpRequest.get('/api/passport/account/get-user-info')
} }
/**
* 获取登录用户的权限
*/
export function getUserPermissions() {
return httpRequest.get('/api/certs/user/get-info')
}
// 发送验证码 // 发送验证码
export function sendCode(data) { export function sendCode(data) {
return httpRequest({ return httpRequest({
...@@ -34,4 +40,21 @@ export function updateUserInfo(data) { ...@@ -34,4 +40,21 @@ export function updateUserInfo(data) {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data data
}) })
}
/**
* 文件上传
*/
export function fileUpload(formData) {
return httpRequest({
url: 'https://webapp-pub.oss-cn-beijing.aliyuncs.com',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 900000,
data: formData,
withCredentials: false
})
}
// 获取oss signature
export function getSignature() {
return httpRequest.get('/api/usercenter/aliyun/get-signature')
} }
\ No newline at end of file
import httpRequest from '@/utils/axios'
/* ------------------------------------广告------------------------------------ */
/**
* 获取广告列表
* @param {string} type_id 类型id
* @param {number} id_title id或标题
* @param {string} project_id 项目id
*/
export function getAdList(params) {
return httpRequest.get('/api/cms/admin/v1/advertisements', { params })
}
/**
* 创建广告
*/
export function createAd(data) {
return httpRequest.post('/api/cms/admin/v1/advertisement/create', data)
}
/**
* 更新广告
* @param {string} id 广告id
* @param {object} data 广告表单数据
*/
export function updateAd(id, data) {
return httpRequest.put(`/api/cms/admin/v1/advertisement/${id}/update`, data)
}
/**
* 发布广告
* @param {string} id
* @param {number} data.is_publish 发布1 取消发布0
*/
export function publishAd(id, data) {
return httpRequest.post(`/api/cms/admin/v1/advertisement/${id}/publish`, data)
}
/**
* 审核广告
* @param {string} id
* @param {number} data.audit_status 2通过 3驳回
* @param {string} data.audit_remarks 审核说明
*/
export function auditAd(id, data) {
return httpRequest.post(`/api/cms/admin/v1/advertisement/${id}/audit`, data)
}
/**
* 置顶广告
* @param {string} id
* @param {number} data.is_top 是否置顶:0否 1是
* @param {number} data.weight 权重:0-10
*/
export function topAd(id, data) {
return httpRequest.post(`/api/cms/admin/v1/advertisement/${id}/top`, data)
}
/**
* 批量删除广告
* @param {string} data.ids
*/
export function batchDeleteAd(data) {
return httpRequest.post('/api/cms/admin/v1/advertisement/batch-delete', data)
}
/* ------------------------------------文章------------------------------------ */
/**
* 获取文章列表
* @param {string} type_id 类型id
* @param {number} id_title id或标题
* @param {string} project_id 项目id
*/
export function getArticleList(params) {
return httpRequest.get('/api/cms/admin/v1/articles', { params })
}
/**
* 创建文章
*/
export function createArticle(data) {
return httpRequest.post('/api/cms/admin/v1/article/create', data)
}
/**
* 更新文章
* @param {string} id
* @param {number} data
*/
export function updateArticle(id, data) {
return httpRequest.put(`/api/cms/admin/v1/article/${id}/update`, data)
}
/**
* 审核文章
* @param {string} id
* @param {number} data.audit_status 2通过 3驳回
* @param {string} data.audit_remarks 审核说明
*/
export function auditArticle(id, data) {
return httpRequest.post(`/api/cms/admin/v1/article/${id}/audit`, data)
}
/**
* 发布文章
* @param {string} id
* @param {number} data.is_publish 发布1 取消发布0
*/
export function publishArticle(id, data) {
return httpRequest.post(`/api/cms/admin/v1/article/${id}/publish`, data)
}
/**
* 置顶文章
* @param {string} id
* @param {number} data.is_top 是否置顶:0否 1是
*/
export function topArticle(id, data) {
return httpRequest.post(`/api/cms/admin/v1/article/${id}/top`, data)
}
/**
* 推荐文章
* @param {string} id
* @param {number} data.is_recommend 是否推荐:0否 1是
*/
export function recommendArticle(id, data) {
return httpRequest.post(`/api/cms/admin/v1/article/${id}/recommend`, data)
}
/**
* 批量删除文章
* @param {string} data.ids
*/
export function batchDeleteArticle(data) {
return httpRequest.post('/api/cms/admin/v1/article/batch-delete', data)
}
/* ------------------------------------图文------------------------------------ */
/**
* 获取图文列表
* @param {string} type_id 类型id
* @param {number} id_title id或标题
* @param {string} project_id 项目id
*/
export function getImgTextList(params) {
return httpRequest.get('/api/cms/admin/v1/img-texts', { params })
}
/**
* 创建文章
*/
export function createImgText(data) {
return httpRequest.post('/api/cms/admin/v1/img-text/create', data)
}
/**
* 更新图文
* @param {string} id
* @param {number} data
*/
export function updateImgText(id, data) {
return httpRequest.put(`/api/cms/admin/v1/img-text/${id}/update`, data)
}
/**
* 审核图文
* @param {string} id
* @param {number} data.audit_status 2通过 3驳回
* @param {string} data.audit_remarks 审核说明
*/
export function auditImgText(id, data) {
return httpRequest.post(`/api/cms/admin/v1/img-text/${id}/audit`, data)
}
/**
* 发布图文
* @param {string} id
* @param {number} data.is_publish 发布1 取消发布0
*/
export function publishImgText(id, data) {
return httpRequest.post(`/api/cms/admin/v1/img-text/${id}/publish`, data)
}
/**
* 获取广告/文章/图文新建时所选项目对应的类型
* @param {number} content_type 类型 1文章 2广告 3图文
* @param {string} params.project_id 项目id
*/
export function getProjectContentTypeList(type, params) {
return httpRequest.get(`/api/cms/admin/v1/type/${type}/project`, { params })
}
/**
* 置顶图文
* @param {string} id
* @param {number} data.is_top 是否置顶:0否 1是
*/
export function topImgText(id, data) {
return httpRequest.post(`/api/cms/admin/v1/img-text/${id}/top`, data)
}
/**
* 批量删除图文
* @param {string} data.ids
*/
export function batchDeleteImgText(data) {
return httpRequest.post('/api/cms/admin/v1/img-text/batch-delete', data)
}
import httpRequest from '@/utils/axios'
/**
* 课程列表
*/
export function getCourseList(params) {
return httpRequest.get('/api/certs/v1/course/list', { params })
}
/**
* 创建课程
* @data 课程信息
*/
export function createCourse(data) {
return httpRequest.post('/api/certs/v1/course/create', data)
}
/**
* 更新课程
* @id 课程id
* @data 课程信息
*/
export function updateCourse(id, data) {
return httpRequest.post(`/api/certs/v1/course/update/${id}`, data)
}
/**
* 删除课程
* @ids 课程id,以,连接
*/
export function deleteCourse(ids) {
return httpRequest.post(`/api/certs/v1/course/delete/${ids}`)
}
/**
* 课程导入
*/
export function importCourse(formData) {
return httpRequest({
url: '/api/certs/v1/course/import',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 900000,
data: formData,
withCredentials: false
})
}
/**
* 导出课程列表
* @params.key 课程ID、课程名称
*/
export function exportCourseList(params) {
return httpRequest({
url: '/api/certs/v1/course/download',
method: 'get',
params,
responseType: 'blob'
})
}
\ No newline at end of file
import httpRequest from '@/utils/axios'
/**
* 项目列表
*/
export function getProjectList(params) {
return httpRequest.get('/api/certs/v1/project/list', { params })
}
/**
* 创建项目
* @data 项目信息
*/
export function createProject(data) {
return httpRequest.post('/api/certs/v1/project/create', data)
}
/**
* 更新项目
* @id 项目id
* @data 项目信息
*/
export function updateProject(id, data) {
return httpRequest.post(`/api/certs/v1/project/update/${id}`, data)
}
/**
* 删除项目
* @ids 项目id,以,连接
*/
export function deleteProject(ids) {
return httpRequest.post(`/api/certs/v1/project/delete/${ids}`)
}
/**
* 更新项目关联的课程
* @type 操作类型(1:新增;2:删除)
* @project_id 项目id
* @courses_id 课程ID,多个使用英文逗号分割
*/
export function updateProjectCourseRelation(data) {
return httpRequest.post('/api/certs/v1/project/update-course', data)
}
/**
* 更新项目关联的学员
* @type 操作类型(1:新增;2:删除)
* @project_id 项目id
* @students_id 学员ID,多个使用英文逗号分割
*/
export function updateProjectStudentRelation(data) {
return httpRequest.post('/api/certs/v1/project/update-student', data)
}
/**
* 导出项目列表
* @params.key 项目ID、项目名称
* @params.type 项目类型
*/
export function exportProjectList(params) {
return httpRequest({
url: '/api/certs/v1/project/download',
method: 'get',
params,
responseType: 'blob'
})
}
\ No newline at end of file
import httpRequest from '@/utils/axios'
/**
* 角色列表
*/
export function getRoleList() {
return httpRequest.get('/api/certs/management/role/index')
}
/**
* 新建/更新角色
*/
export function saveRole(data) {
return httpRequest.post('/api/certs/management/role/create-or-update', data)
}
/**
* 删除角色
*/
export function deleteRole(data) {
return httpRequest.post('/api/certs/management/role/delete', data)
}
/**
* 查询角色详情
*/
export function getRoleDetails(params) {
return httpRequest.get('/api/certs/management/role/manage', { params })
}
/**
* 查询所有权限
*/
export function getPermissionList(params) {
return httpRequest.get('/api/certs/management/permission/index', { params })
}
/**
* 配置权限
*/
export function setPermissions(data) {
return httpRequest.post('/api/certs/management/role/manage?name=' + data.name, data)
}
\ No newline at end of file
import httpRequest from '@/utils/axios'
/* ------------------------------------高级设置------------------------------------ */
/**
* 获取角色列表
*/
export function getRoleList(params) {
return httpRequest.get('/api/cms/admin/v1/roles', { params })
}
/**
* 获取权限列表
*/
export function getPermissionList(params) {
return httpRequest.get('/api/cms/admin/v1/permissions', { params })
}
/**
* 创建角色
*/
export function createRole(data) {
return httpRequest.post('/api/cms/admin/v1/role/create', data)
}
/**
* 获取角色详情
*/
export function getRoleDetails(params) {
return httpRequest.get(`/api/cms/admin/v1/role/${params.role_id}/detail`, { params })
}
/**
* 更新角色
*/
export function updateRole(data) {
return httpRequest.put(`/api/cms/admin/v1/role/${data.role_id}/update`, data)
}
/**
* 删除角色
*/
export function deleteRole(data) {
return httpRequest.delete(`/api/cms/admin/v1/role/${data.role_id}/delete`, data)
}
/* ------------------------------------员工管理------------------------------------ */
/**
* 获取员工列表
*/
export function getStaffList(params) {
return httpRequest.get('/api/cms/admin/v1/users', { params })
}
/**
* 创建员工
*/
export function createStaff(data) {
return httpRequest.post('/api/cms/admin/v1/user/create', data)
}
/**
* 更新员工
*/
export function updateStaff(data) {
return httpRequest.put(`/api/cms/admin/v1/user/${data.sso_id}/update`, data)
}
/**
* 批量删除员工
*/
export function batchDeleteStaff(data) {
return httpRequest.post('/api/cms/admin/v1/user/batch-delete', data)
}
/**
* 删除用户缓存
*/
export function clearUserCached(id) {
return httpRequest.get(`/api/cms/admin/v1/user/${id}/clear-cached`)
}
/* ------------------------------------项目管理------------------------------------ */
/**
* 获取项目列表
* @param {string} name 项目名称
* @param {string} type_id 项目id
* @param {number} status 创建的时候是否展示:0不展示,1展示
*/
export function getProjectList(params) {
return httpRequest.get('/api/cms/admin/v1/projects', { params })
}
/**
* 创建项目
*/
export function createProject(data) {
return httpRequest.post('/api/cms/admin/v1/project/create', data)
}
/**
* 更新项目
*/
export function updateProject(data) {
return httpRequest.put(`/api/cms/admin/v1/project/${data.project_id}/update`, data)
}
/**
* 删除项目
*/
export function deleteProject(data) {
return httpRequest.delete(`/api/cms/admin/v1/project/${data.project_id}/delete`, data)
}
/* ------------------------------------类型管理------------------------------------ */
/**
* 获取类型列表
* @param {number} type 0项目类型 1内容类型
*/
export function getTypeList(params) {
return httpRequest.get('/api/cms/admin/v1/types', { params })
}
/**
* 创建类型
*/
export function createType(data) {
return httpRequest.post('/api/cms/admin/v1/type/create', data)
}
/**
* 更新类型
*/
export function updateType(data) {
return httpRequest.put(`/api/cms/admin/v1/type/${data.type_id}/update`, data)
}
/**
* 获取所有的内容分类
*/
export function getContentTypesList(params) {
return httpRequest.get('/api/cms/admin/v1/type/content-types', { params })
}
/**
* 批量删除类型
*/
export function batchDeleteType(data) {
return httpRequest.post('/api/cms/admin/v1/type/batch-delete', data)
}
import httpRequest from '@/utils/axios'
/**
* 学员列表
* @params.key 学籍ID、学员姓名
* @params.email 邮箱
* @params.mobile 手机号
*/
export function getStudentList(params) {
return httpRequest.get('/api/certs/v1/student/list', { params })
}
/**
* 导出学员列表
* @params.key 学籍ID、学员姓名
* @params.email 邮箱
* @params.mobile 手机号
*/
export function exportStudentList(params) {
return httpRequest({
url: '/api/certs/v1/student/download',
method: 'get',
params,
responseType: 'blob'
})
}
/**
* 学员导入
*/
export function importStudents(formData) {
console.log(formData)
return httpRequest({
url: '/api/certs/v1/student/import',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 900000,
data: formData
})
}
/**
* 学员详情
* @id 学籍ID
*/
export function getStudentDetails(id) {
return httpRequest.get(`/api/certs/v1/student/view/${id}`)
}
/**
* 更新学员信息
* @id 学籍ID
* @data 学员信息
*/
export function updateStudentInfo(id, data) {
return httpRequest.post(`/api/certs/v1/student/update/${id}`, data)
}
/**
* 删除学员
* @ids 学籍ID,以,连接
*/
export function deleteStudent(ids) {
return httpRequest.post(`/api/certs/v1/student/delete/${ids}`)
}
/**
* 学位列表
* @id 学籍ID
*/
export function getStudentDegreeList(id) {
return httpRequest.get(`/api/certs/v1/student/degree-list/${id}`)
}
/**
* 编辑/新增学位
* @id 学籍ID
* @data 学位信息
*/
export function saveStudentDegree(data) {
let api = '/api/certs/v1/student/save-degree'
if (data.id) api = api + '/' + data.id
return httpRequest.post(api, data)
}
/**
* 删除学位
* @ids 学位ID,以,连接
*/
export function deleteStudentDegree(ids) {
return httpRequest.post(`/api/certs/v1/student/delete-degree/${ids}`)
}
/**
* 学员课程列表
* @id 学籍ID
*/
export function getStudentCourseList(id) {
return httpRequest.get(`/api/certs/v1/student/course-list/${id}`)
}
/**
* 保存学员课程列表
* @id 学籍ID
*/
export function saveStudentCourseList(id, data) {
return httpRequest.post(`/api/certs/v1/student/save-course/${id}`, data)
}
\ No newline at end of file
import httpRequest from '@/utils/axios'
/**
* 操作日志
*/
export function operateLog(params) {
const logMsg = `操作人:${params.user.nickname},操作:${params.type}`
const data = {
detail: logMsg
}
httpRequest.post('/api/live/admin/v2/system/log/write', data).then(res => {
}).catch(() => {})
}
/**
* 模糊搜索
*/
export function searchUserList(params) {
return httpRequest.get('/api/cms/admin/v1/common/user-search', { params })
}
/**
* 获取当前用户的角色和权限
*/
export function getUserRolesPermissions() {
return httpRequest.get('/api/cms/admin/v1/common/user-detail')
}
/**
* 文件上传
*/
export function fileUpload(formData) {
return httpRequest({
url: 'https://webapp-pub.oss-cn-beijing.aliyuncs.com',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 900000,
data: formData,
withCredentials: false
})
}
// 获取oss signature
export function getSignature() {
return httpRequest.get('/api/usercenter/aliyun/get-signature')
}
import httpRequest from '@/utils/axios'
/**
* 授权(用户)列表
*/
export function getAssignUserList(params) {
return httpRequest.get('/api/certs/management/assignment/index', { params })
}
/**
* 搜索用户
*/
export function searchUsers(params) {
return httpRequest.get('/api/certs/management/assignment/search', { params })
}
/**
* 用户授权
*/
export function assignUser(data) {
return httpRequest.post('/api/certs/management/assignment/create', data)
}
/**
* 删除授权用户
*/
export function deleteAssignUser(data) {
return httpRequest.post('/api/certs/management/assignment/delete', data)
}
\ No newline at end of file
...@@ -51,7 +51,7 @@ export default { ...@@ -51,7 +51,7 @@ export default {
}, },
methods: { methods: {
handlleSelect(path) { handlleSelect(path) {
console.log(path) // console.log(path)
this.$router.push(path) this.$router.push(path)
} }
} }
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
</el-form-item> </el-form-item>
</template> </template>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" @click="search">搜索</el-button> <el-button type="primary" icon="el-icon-search" @click="search">查询</el-button>
<el-button icon="el-icon-refresh-left" @click="reset">重置</el-button> <el-button icon="el-icon-refresh-left" @click="reset">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
</div> </div>
<slot></slot> <slot></slot>
<div class="table-list-bd"> <div class="table-list-bd">
<el-table :data="dataList" size="mini" v-loading="loading" v-bind="$attrs" v-on="$listeners" height="100%"> <el-table ref="table" :data="dataList" size="mini" v-loading="loading" v-bind="$attrs" v-on="$listeners" height="100%">
<template v-for="item in columns"> <template v-for="item in columns">
<el-table-column v-bind="item" :key="item.prop" v-if="visible(item)"> <el-table-column v-bind="item" :key="item.prop" v-if="visible(item)">
<template v-slot:default="scope" v-if="item.slots || item.computed"> <template v-slot:default="scope" v-if="item.slots || item.computed">
...@@ -54,9 +54,7 @@ ...@@ -54,9 +54,7 @@
</el-table> </el-table>
</div> </div>
<div class="table-list-ft"> <div class="table-list-ft">
<div> <slot name="footer"></slot>
<slot name="footer"></slot>
</div>
<el-pagination <el-pagination
class="table-list-pagination" class="table-list-pagination"
layout="total, prev, pager, next, sizes, jumper" layout="total, prev, pager, next, sizes, jumper"
...@@ -95,7 +93,8 @@ export default { ...@@ -95,7 +93,8 @@ export default {
loading: false, loading: false,
params: this.remote.params || {}, params: this.remote.params || {},
dataList: this.data, dataList: this.data,
page: { total: 0, size: this.limit, currentPage: 1 } page: { total: 0, size: this.limit, currentPage: 1 },
selection: []
} }
}, },
watch: { watch: {
...@@ -128,7 +127,7 @@ export default { ...@@ -128,7 +127,7 @@ export default {
// 翻页参数设置 // 翻页参数设置
if (this.hasPagination) { if (this.hasPagination) {
params.page = this.page.currentPage params.page = this.page.currentPage
params.limit = this.page.size params['per-page'] = this.page.size
} }
// 接口请求之前 // 接口请求之前
if (beforeRequest) { if (beforeRequest) {
...@@ -142,9 +141,9 @@ export default { ...@@ -142,9 +141,9 @@ export default {
this.loading = true this.loading = true
httpRequest(params) httpRequest(params)
.then(res => { .then(res => {
const { data = [], total = 0 } = res.data || {} const { list = [], total = 0 } = res.data || {}
this.page.total = total this.page.total = total
this.dataList = callback ? callback(data) : data this.dataList = callback ? callback(list) : list
}) })
.finally(() => { .finally(() => {
this.loading = false this.loading = false
...@@ -176,6 +175,13 @@ export default { ...@@ -176,6 +175,13 @@ export default {
}, },
visible(item) { visible(item) {
return Object.prototype.hasOwnProperty.call(item, 'visible') ? item.visible : true return Object.prototype.hasOwnProperty.call(item, 'visible') ? item.visible : true
},
tableReview(arr, list) { // 默认选中
list.forEach(item => {
if (arr.indexOf(item.id) > -1) {
this.$refs.table.toggleRowSelection(item, true)
}
})
} }
}, },
beforeMount() { beforeMount() {
...@@ -200,6 +206,7 @@ export default { ...@@ -200,6 +206,7 @@ export default {
} }
.table-list-bd { .table-list-bd {
flex: 1; flex: 1;
height: 0;
} }
.table-list-bd .el-table th:first-child .cell{ .table-list-bd .el-table th:first-child .cell{
padding-left:14px; padding-left:14px;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>紫荆内容管理系统</title> <title>紫荆证书查询管理系统</title>
<meta <meta
name="viewport" name="viewport"
id="viewport" id="viewport"
......
...@@ -2,69 +2,131 @@ ...@@ -2,69 +2,131 @@
<div class="course page_container"> <div class="course page_container">
<table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange"> <table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<template #header-aside> <template #header-aside>
<el-button type="primary" size="mini" @click="handleCreate" v-if="hasImport">新增</el-button> <el-button type="primary" size="mini" @click="drawerVisible = true" v-if="hasCreate">新增</el-button>
<el-button type="primary" size="mini" @click="handleImport" v-if="hasImport">导入</el-button> <el-button type="primary" size="mini" @click="dialogVisible = true" v-if="hasImport">导入</el-button>
</template> </template>
<!-- 项目Id --> <!-- 项目Id -->
<template v-slot:table_id="scope" <template v-slot:table_id="scope"
><span class="details-handle" @click="handleDetails(scope.row)">{{ scope.row.id }}</span></template ><span class="details-handle" @click="handleEdit(scope.row)">{{ scope.row.id }}</span></template
> >
<!-- 状态 -->
<template v-slot:status="{ row }">
<el-switch
v-model="row.status"
:active-value="1"
:inactive-value="0"
active-text="启用"
inactive-text="停用"
@change="statusChange(row)"
:disabled="!hasCreate"
></el-switch>
</template>
<template #footer> <template #footer>
已选中 {{multipleSelection.length}} <div>
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasDelete" <span v-if="hasDelete">已选中 {{multipleSelection.length}}</span>
>删除</el-button <el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasDelete">删除</el-button>
> <el-dropdown size="small" @command="handleCommand" v-if="hasExport">
<el-button type="primary" size="mini" plain>
导出<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="all">导出全部</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template> </template>
</table-list> </table-list>
<el-drawer :title="drawerTitle" :destroy-on-close="true" :visible.sync="drawerVisible" size="520px" @close="handleClose">
<div class="drawer-container">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="140px">
<el-form-item label="课程名称" prop="chinese_name">
<el-input v-model="form.chinese_name" size="small" placeholder="请输入课程名称"/>
</el-form-item>
<el-form-item label="课程名称(英)" prop="english_name">
<el-input v-model="form.english_name" size="small" placeholder="请输入课程名称(英)"/>
</el-form-item>
<el-form-item label="发布时间" prop="pubdate">
<el-date-picker v-model="form.pubdate" type="datetime" value-format="yyyy-MM-dd hh:mm:ss" placeholder="请选择发布时间" size="small" style="width:100%;"/>
</el-form-item>
</el-form>
<div class="drawer-footer" style="text-align:center;">
<el-button size="mini" @click="drawerVisible = false">取 消</el-button>
<el-button size="mini" type="primary" @click="handleSubmit" >提交</el-button>
</div>
</div>
</el-drawer>
<el-dialog title="导入学员" :visible.sync="dialogVisible" width="480px" :destroy-on-close="true" :close-on-click-modal="false" @close="handleDialogClose">
<el-upload
class="file-import"
ref="upload"
action="/"
:auto-upload="false"
:file-list="fileList"
:limit="1"
:before-upload="beforeUpload"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
>
<el-button slot="trigger" size="mini" type="primary">选取文件</el-button>
<span slot="tip" style="margin-left:10px;">只能上传excel文件</span>
</el-upload>
<div style="text-align:center;">
<el-button size="mini" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" size="mini" @click="submitUpload">确认提交</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import TableList from '@/components/TableList' import TableList from '@/components/TableList'
import { getCourseList, createCourse, updateCourse, deleteCourse, importCourse, exportCourseList } from '@/api/course'
import { splitStrLast, funDownload } from '@/utils/utils'
const defaultForm = {
chinese_name: '',
english_name: '',
pubdate: ''
}
export default { export default {
components: { TableList }, components: { TableList },
data() { data() {
return { return {
multipleSelection: [] multipleSelection: [],
drawerVisible: false,
drawerTitle: '新增课程',
form: Object.assign({}, defaultForm),
rules: {
chinese_name: { required: true, message: '请输入课程名称', trigger: 'blur' },
english_name: { required: true, message: '请输入课程名称(英)', trigger: 'blur' },
pubdate: { required: true, message: '请选择发布时间', trigger: 'change' }
},
dialogVisible: false,
fileList: []
} }
}, },
computed: { computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('v1/course/create')
},
hasImport() { hasImport() {
return true return this.permissions.includes('v1/course/import')
},
hasExport() {
return this.permissions.includes('v1/course/download')
}, },
hasDelete() { hasDelete() {
return true return this.permissions.includes('v1/course/delete')
},
hasView() {
return this.permissions.includes('v1/course/view')
},
hasUpdate() {
return this.permissions.includes('v1/course/update')
}, },
tableOptions() { tableOptions() {
return { return {
data: [
{ id: '123001', name: '高品质运营管理', en_name: 'High Quality Operations Management', publish_time: '2016-09-20 11:00:54' },
{ id: '123001', name: '酒店和旅游行业市场营销', en_name: 'Marketing for Hospitality & Tourism Industry', publish_time: '2016-09-20 11:00:54' }
],
remote: { remote: {
httpRequest: null, httpRequest: getCourseList,
params: { name: '' } params: { key: '' }
}, },
filters: [ filters: [
{ type: 'input', placeholder: '请输入课程ID/课程名称', prop: 'name' } { type: 'input', placeholder: '请输入课程ID/课程名称', prop: 'key' }
], ],
columns: [ columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete }, { type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete },
{ prop: 'id', label: '课程ID', slots: 'table_id', minWidth: '120px', fixed: 'left' }, { prop: 'id', label: '课程ID', slots: 'table_id', minWidth: '120px', fixed: 'left' },
{ prop: 'name', label: '课程名称', minWidth: '130px' }, { prop: 'chinese_name', label: '课程名称', minWidth: '130px' },
{ prop: 'en_name', label: '课程名称(英)', minWidth: '160px' }, { prop: 'english_name', label: '课程名称(英)', minWidth: '160px' },
{ prop: 'publish_time', label: '发布时间', minWidth: '120px' } { prop: 'pubdate', label: '发布时间', minWidth: '120px' }
], ],
bodyHeight: 'calc(100% - 50px)' bodyHeight: 'calc(100% - 50px)'
} }
...@@ -74,10 +136,132 @@ export default { ...@@ -74,10 +136,132 @@ export default {
handleSelectionChange(val) { handleSelectionChange(val) {
this.multipleSelection = val.map(item => item.id) this.multipleSelection = val.map(item => item.id)
}, },
handleCreate() {},
handleImport() {}, handleImport() {},
handleRemove() {}, handleRemove() {
handleDetails() {} this.$confirm('确定删除选中项?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchDeleteCourse()
}).catch(() => {});
},
handleEdit(val) {
if (this.hasUpdate) {
this.drawerTitle = '编辑课程'
this.drawerVisible = true
this.form = Object.assign({}, val)
}
},
handleClose() {
this.drawerTitle = '新增课程'
this.form = Object.assign({}, defaultForm)
this.$refs.ruleForm.clearValidate()
},
handleSubmit() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.drawerTitle === '新增课程' ? this.fetchCreateCourse() : this.fetchUpdateCourse()
}
});
},
handleDialogClose() {
this.fileList = []
},
beforeUpload(file) {
console.log(file)
const suffix = splitStrLast(file.name, '.')
if (!['xlsx', 'xls'].includes(suffix)) {
this.$message.error('只能上传excel文件')
return false
} else {
const formData = new window.FormData()
formData.append('file', file)
return new Promise((resolve, reject) => {
importCourse(formData).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success('导入数据成功')
resolve(true)
this.$refs.tabList.refetch()
window.setTimeout(() => {
this.dialogVisible = false
}, 300)
} else {
this.$message.error(res.message || '导入数据失败,请重选选取文件上传')
resolve(false)
}
})
})
}
},
submitUpload() {
this.$refs.upload.submit()
},
handleCommand(type) {
if (type === 'all') this.fetchExportAll()
},
fetchExportAll() {
exportCourseList(this.tableOptions.remote.params).then(res => {
console.log(res)
if (res && res.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
const url = URL.createObjectURL(res)
funDownload(url, `课程信息表_${Date.now()}.xlsx`)
}
})
},
fetchCreateCourse() {
createCourse(this.form).then(res => {
const msg = '新增课程'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.drawerVisible = false
this.$refs.tabList.refetch()
} else {
this.$message.error(msg + '失败')
}
})
},
fetchUpdateCourse() {
updateCourse(this.form.id, this.form).then(res => {
const msg = '编辑课程'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.drawerVisible = false
this.$refs.tabList.refetch()
} else {
this.$message.error(msg + '失败')
}
})
},
fetchDeleteCourse() {
const ids = this.multipleSelection.join()
deleteCourse(ids).then(res => {
const msg = '删除课程'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.$refs.tabList.refetch()
} else {
this.$message.error(msg + '失败')
}
})
}
} }
} }
</script> </script>
\ No newline at end of file <style scoped>
.drawer-container{
height:100%;
display:flex;
flex-direction: column;
}
.el-form{
flex:1;
padding:15px 25px 0 0;
}
.drawer-footer{
padding:15px 0;
text-align:center;
}
.file-import{
padding-bottom:15px;
}
</style>
\ No newline at end of file
<template>
<div class="form-container">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="140px" :disabled="!isEditFlag">
<el-form-item label="项目名称:" prop="chinese_name">
<el-input v-model="form.chinese_name" placeholder="请输入项目名称" size="mini"/>
</el-form-item>
<el-form-item label="项目名称(英):" prop="english_name">
<el-input v-model="form.english_name" placeholder="请输入项目英文名称" size="mini"/>
</el-form-item>
<el-form-item label="项目类型" prop="type">
<el-select v-model="form.type" placeholder="请选择项目类型" size="small" style="width:100%;">
<el-option v-for="item in typeList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="发布时间" prop="pubdate">
<el-date-picker v-model="form.pubdate" type="datetime" placeholder="请选择发布时间" size="small" value-format="yyyy-MM-dd hh:mm:ss" style="width:100%;"/>
</el-form-item>
</el-form>
<div class="foot-btn-bar" v-if="isEditFlag">
<el-button size="mini" @click="handleCancel">取消</el-button>
<el-button type="primary" size="mini" @click="handleEnsure">确认提交</el-button>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: Boolean
},
details: {
type: Object
}
},
data() {
return {
form: {
chinese_name: '',
english_name: '',
type: '',
pubdate: ''
},
rules: {
chinese_name: { required: true, message: '请输入项目名称', trigger: 'blur' },
en_name: { required: true, message: '请输入项目英文名称', trigger: 'blur' },
type: { required: true, message: '请选择项目类型', trigger: 'change' },
pubdate: { required: true, message: '请选择发布时间', trigger: 'change' }
},
typeList: [
{ id: 1, name: '长期' },
{ id: 2, name: '短期' }
]
}
},
computed: {
isEditFlag() {
return this.value
}
},
created() {
if (this.details && this.details.id) {
this.form = this.details
}
},
methods: {
handleCancel() {
this.$refs.ruleForm.clearValidate()
this.$emit('input', false)
},
handleEnsure() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.$emit('formSubmit', this.form)
}
})
}
}
}
</script>
<style scoped>
.el-form{
width:450px;
margin:15px auto 0;
}
.foot-btn-bar{
text-align:center;
}
</style>
\ No newline at end of file
<template>
<div class="project-course">
<table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<template #footer>
<div v-if="isEdit">
已选中 {{multipleSelection.length}}
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasSave">删除</el-button>
</div>
<div v-else>
已选中 {{multipleSelection.length}}
<el-button style="margin-left:15px;" size="mini" @click="handleCancel">取消</el-button>
<el-button style="margin-left:15px;" type="primary" size="mini" :disabled="!multipleSelection.length" @click="fetchCourseRelation" v-if="hasSave">确认提交</el-button>
</div>
</template>
</table-list>
</div>
</template>
<script>
import { getCourseList } from '@/api/course'
import { updateProjectCourseRelation } from '@/api/project'
import TableList from '@/components/TableList'
export default {
components: { TableList },
props: {
isEdit: {
type: Boolean,
default: false
},
id: {
type: String
}
},
data() {
return {
multipleSelection: []
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasSave() {
return this.permissions.includes('v1/project/update-course')
},
tableOptions() {
const params = { key: '' }
if (this.isEdit) params.project_id = this.id
return {
remote: {
httpRequest: getCourseList,
params: params
},
filters: [
{ type: 'input', placeholder: '请输入课程ID/课程名称', prop: 'key' }
],
columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete },
{ prop: 'id', label: '课程ID', minWidth: '120px', fixed: 'left' },
{ prop: 'chinese_name', label: '课程名称', minWidth: '130px' },
{ prop: 'english_name', label: '课程名称(英)', minWidth: '160px' },
{ prop: 'pubdate', label: '发布时间', minWidth: '120px' }
]
}
}
},
methods: {
handleSelectionChange(val) {
this.multipleSelection = val.map(item => item.id)
},
handleRemove() {
this.$confirm('确定删除选中的课程与此项目的关联?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchCourseRelation()
}).catch(() => {});
},
fetchCourseRelation() {
const msg = this.isEdit ? '删除课程' : '添加课程'
const params = {
type: this.isEdit ? 2 : 1,
project_id: this.id,
courses_id: this.multipleSelection.join()
}
updateProjectCourseRelation(params).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.$emit('tableRefetch')
if (this.isEdit) this.$refs.tabList.refetch()
else this.$emit('input', false)
} else {
this.$message.error(msg + '失败')
}
})
}
}
}
</script>
<style>
.project-course{
height:100%;
}
</style>
\ No newline at end of file
<template>
<div class="project-info">
<div class="btn-bar" v-if="!isEdit">
<el-button v-if="hasUpdate" type="primary" size="mini" @click="isEdit = true">编辑</el-button>
</div>
<info-form v-model="isEdit" :details="info" @formSubmit="fetchUpdateProject"/>
</div>
</template>
<script>
import InfoForm from './InfoForm'
import { updateProject } from '@/api/project'
export default {
components: { InfoForm },
props: {
info: {
type: Object
}
},
data() {
return {
isEdit: false
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasUpdate() {
return this.permissions.includes('v1/project/update')
}
},
created() {
console.log(this.info)
},
methods: {
fetchUpdateProject(val) {
updateProject(val.id, val).then(res => {
const msg = '编辑项目'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.createDrawerVisible = false
this.$emit('tableRefetch')
} else {
this.$message.error(msg + '失败')
}
})
}
}
}
</script>
<style scoped>
.btn-bar{
text-align:right;
margin-right:20px;
}
</style>
\ No newline at end of file
<template>
<div class="project-student">
<table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<template #footer>
<div v-if="isEdit">
已选中 {{multipleSelection.length}}
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasSave">删除</el-button>
</div>
<div v-else>
已选中 {{multipleSelection.length}}
<el-button style="margin-left:15px;" size="mini" @click="handleCancel">取消</el-button>
<el-button style="margin-left:15px;" type="primary" size="mini" :disabled="!multipleSelection.length" @click="fetchStudentRelation" v-if="hasSave">确认提交</el-button>
</div>
</template>
</table-list>
</div>
</template>
<script>
import TableList from '@/components/TableList'
import { getStudentList } from '@/api/student'
import { updateProjectStudentRelation } from '@/api/project'
const genderMap = { 0: '男', 1: '女', 2: '未知' }
export default {
components: { TableList },
props: {
isEdit: {
type: Boolean,
default: false
},
id: {
type: String
}
},
data() {
return {
multipleSelection: []
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasSave() {
return this.permissions.includes('v1/project/update-student')
},
tableOptions() {
const params = { key: '', mobile: '', email: '' }
if (this.isEdit) params.project_id = this.id
return {
remote: {
httpRequest: getStudentList,
params: params
},
filters: [
{ type: 'input', placeholder: '请输入学员ID/学员名称', prop: 'key' },
{ type: 'input', placeholder: '请输入手机号', prop: 'mobile' },
{ type: 'input', placeholder: '请输入邮箱地址', prop: 'email' }
],
columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete },
{ prop: 'id', label: '学员ID', minWidth: '120px', fixed: 'left' },
{ prop: 'chinese_name', label: '姓名', minWidth: '90px' },
{
prop: 'gender',
label: '性别',
minWidth: '60px',
computed({ row }) {
return genderMap[row.gender || 2]
}
},
{ prop: 'id_number', label: '身份证号', minWidth: '150px' },
{ prop: 'mobile', label: '电话号码', minWidth: '130px' },
{ prop: 'email', label: '邮箱地址', minWidth: '150px' },
{ prop: 'created_time', label: '创建时间', minWidth: '150px' }
]
}
}
},
methods: {
handleSelectionChange(val) {
this.multipleSelection = val.map(item => item.id)
},
handleRemove() {
this.$confirm('确定删除选中的学员与此项目的关联?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchStudentRelation()
}).catch(() => {});
},
handleCancel() {},
fetchStudentRelation () {
const msg = this.isEdit ? '删除学员' : '添加学员'
const params = {
type: this.isEdit ? 2 : 1,
project_id: this.id,
students_id: this.multipleSelection.join()
}
updateProjectStudentRelation(params).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.$emit('tableRefetch')
if (this.isEdit) this.$refs.tabList.refetch()
else this.$emit('input', false)
} else {
this.$message.error(msg + '失败')
}
})
}
}
}
</script>
<style>
.project-student{
height:100%
}
</style>
\ No newline at end of file
...@@ -2,70 +2,163 @@ ...@@ -2,70 +2,163 @@
<div class="course page_container"> <div class="course page_container">
<table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange"> <table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<template #header-aside> <template #header-aside>
<el-button type="primary" size="mini" @click="handleCreate" v-if="hasImport">新增</el-button> <el-button type="primary" size="mini" @click="handleCreate" v-if="hasCreate">新增</el-button>
<el-button type="primary" size="mini" @click="handleImport" v-if="hasImport">导入</el-button>
</template>
<!-- 项目Id -->
<template v-slot:table_id="scope">
<span class="details-handle" @click="handleDetails(scope.row)">{{ scope.row.id }}</span>
</template> </template>
<!-- 操作 --> <!-- 操作 -->
<template v-slot:table-operate="{ row }"> <template v-slot:table-operate="{ row }">
<el-button type="text" @click="handleEdit(row)" size="mini">编辑</el-button> <el-button v-if="hasDetails" type="text" @click="handleEdit(row)" size="mini" style="margin-right:12px;">编辑</el-button>
<el-button type="text" @click="handleAdd(row)" size="mini">添加</el-button> <el-dropdown size="small" @command="(type) => handleCommand(row, type)">
<span class="el-dropdown-link">
<el-button type="text" size="mini">添加</el-button>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="course">课程</el-dropdown-item>
<el-dropdown-item command="student">学员</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template> </template>
<template #footer> <template #footer>
已选中 {{multipleSelection.length}} <div>
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasDelete" 已选中 {{multipleSelection.length}}
>删除</el-button <el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasDelete">删除</el-button>
> <el-dropdown size="small" @command="handleExportCommand" v-if="hasExport">
<el-button type="primary" size="mini" plain>
导出<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="all">导出全部</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template> </template>
</table-list> </table-list>
<el-drawer
title="项目信息"
:visible.sync="drawerVisible"
:close-on-click-modal="false"
:destroy-on-close="true"
size="1100px"
@close="handleClose">
<el-tabs v-model="tabsActive" type="card">
<el-tab-pane label="基本信息" name="info">
<tab-info :info="details" @tableRefetch="tableRefetch"/>
</el-tab-pane>
<el-tab-pane label="关联学员" name="student">
<tab-student :id="details.id" v-model="drawerVisible" @tableRefetch="tableRefetch" isEdit/>
</el-tab-pane>
<el-tab-pane label="关联课程" name="course">
<tab-course :id="details.id" v-model="drawerVisible" @tableRefetch="tableRefetch" isEdit/>
</el-tab-pane>
</el-tabs>
</el-drawer>
<el-drawer
:title="createDrawerTitle"
:visible.sync="createDrawerVisible"
:close-on-click-modal="false"
:destroy-on-close="true"
size="960px"
@close="handleClose">
<info-form v-if="createType === 'project'" v-model="createDrawerVisible" @formSubmit="fetchCreateProject" />
<tab-student v-if="createType === 'student'" v-model="createDrawerVisible" :id="details.id" type="create" @tableRefetch="tableRefetch"/>
<tab-course v-if="createType === 'course'" v-model="createDrawerVisible" :id="details.id" @tableRefetch="tableRefetch"/>
</el-drawer>
</div> </div>
</template> </template>
<script> <script>
import TableList from '@/components/TableList' import TableList from '@/components/TableList'
import TabInfo from './components/info.vue'
import TabStudent from './components/student.vue'
import TabCourse from './components/course.vue'
import InfoForm from './components/InfoForm'
import { getProjectList, createProject, deleteProject, exportProjectList } from '@api/project'
import { funDownload } from '@/utils/utils'
const drawerTitleMap = {
project: '新增项目',
course: '添加课程',
student: '添加学员'
}
export default { export default {
components: { TableList }, components: { TableList, TabInfo, TabStudent, TabCourse, InfoForm },
data() { data() {
return { return {
multipleSelection: [] multipleSelection: [],
typeList: [
{ id: 1, name: '长期' },
{ id: 2, name: '短期' }
],
drawerVisible: false,
details: {},
tabsActive: 'info',
createDrawerVisible: false,
createType: 'project'
} }
}, },
computed: { computed: {
hasImport() { createDrawerTitle() {
return true return drawerTitleMap[this.createType]
},
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('v1/project/create')
},
hasUpdate() {
return this.permissions.includes('v1/project/update')
},
hasExport() {
return this.permissions.includes('v1/project/download')
}, },
hasDelete() { hasDelete() {
return true return this.permissions.includes('v1/project/delete')
},
hasDetails() {
return this.permissions.includes('v1/project/view')
}, },
tableOptions() { tableOptions() {
return { return {
data: [
{ id: '123001', name: '高品质运营管理', en_name: 'High Quality Operations Management', publish_time: '2016-09-20 11:00:54', type: '长期', editor: '刘义', update_time: '2016-09-20 11:00:54', course_count: 10, student_count: 10 },
{ id: '123001', name: '酒店和旅游行业市场营销', en_name: 'Marketing for Hospitality & Tourism Industry', publish_time: '2016-09-20 11:00:54', type: '短期', editor: '刘义', update_time: '2016-09-20 11:00:54', course_count: 10, student_count: 10 }
],
remote: { remote: {
httpRequest: null, httpRequest: getProjectList,
params: { name: '' } params: { key: '', type: '' }
}, },
filters: [ filters: [
{ type: 'input', placeholder: '请输入课程ID/课程名称', prop: 'name' } { type: 'input', placeholder: '请输入项目ID/项目名称', prop: 'key' },
{ type: 'select', placeholder: '项目类型', prop: 'type', options: this.typeList, labelKey: 'name', valueKey: 'id' }
], ],
columns: [ columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete }, { type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete },
{ prop: 'id', label: '项目ID', slots: 'table_id', minWidth: '120px', fixed: 'left' }, { prop: 'id', label: '项目ID', minWidth: '155px' },
{ prop: 'name', label: '项目名称', minWidth: '130px' }, { prop: 'chinese_name', label: '项目名称', minWidth: '130px' },
{ prop: 'en_name', label: '项目名称(英)', minWidth: '160px' }, { prop: 'english_name', label: '项目名称(英)', minWidth: '160px' },
{ prop: 'type', label: '项目类型', minWidth: '120px' }, {
{ prop: 'publish_time', label: '发布时间', minWidth: '120px' }, prop: 'type',
{ prop: 'editor', label: '修改人', minWidth: '80px' }, label: '项目类型',
{ prop: 'update_time', label: '修改时间', minWidth: '150px' }, minWidth: '80px',
{ prop: 'course_count', label: '课程数', minWidth: '70px' }, computed({ row }) {
{ prop: 'student_count', label: '学员数', minWidth: '70px' }, return row.type === 1 ? '长期' : '短期'
}
},
{ prop: 'pubdate', label: '发布时间', minWidth: '150px' },
{ prop: 'updated_operator', label: '修改人', minWidth: '100px' },
{ prop: 'updated_time', label: '修改时间', minWidth: '150px' },
{
prop: 'courses',
label: '课程数',
minWidth: '70px',
computed({ row }) {
return row.courses.length
}
},
{
prop: 'students',
label: '学员数',
minWidth: '70px',
computed({ row }) {
return row.students.length
}
},
{ prop: 'operate', label: '操作', slots: 'table-operate', minWidth: '120px', fixed: 'right' } { prop: 'operate', label: '操作', slots: 'table-operate', minWidth: '120px', fixed: 'right' }
], ]
bodyHeight: 'calc(100% - 50px)'
} }
} }
}, },
...@@ -73,12 +166,94 @@ export default { ...@@ -73,12 +166,94 @@ export default {
handleSelectionChange(val) { handleSelectionChange(val) {
this.multipleSelection = val.map(item => item.id) this.multipleSelection = val.map(item => item.id)
}, },
handleCreate() {}, handleCreate() {
this.createDrawerVisible = true
this.createType = 'project'
},
handleImport() {}, handleImport() {},
handleRemove() {}, handleRemove() {
this.$confirm('确定删除选中项?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchDeleteProject()
}).catch(() => {});
},
handleDetails() {}, handleDetails() {},
handleEdit() {}, handleEdit(val) {
handleAdd() {} this.drawerVisible = true
const details = {
chinese_name: val.chinese_name,
english_name: val.english_name,
type: val.type,
pubdate: val.pubdate,
id: val.id
}
this.details = details
},
handleClose() {},
handleCommand(val, type) {
this.createDrawerVisible = true
this.createType = type
const details = {
chinese_name: val.chinese_name,
english_name: val.english_name,
type: val.type,
pubdate: val.pubdate,
id: val.id
}
this.details = details
},
handleExportCommand(type) {
if (type === 'all') this.fetchExportAll()
},
fetchExportAll() {
exportProjectList(this.tableOptions.remote.params).then(res => {
if (res && res.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
const url = URL.createObjectURL(res)
funDownload(url, `项目信息表_${Date.now()}.xlsx`)
}
})
},
fetchCreateProject(val) {
createProject(val).then(res => {
const msg = '新增项目'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.createDrawerVisible = false
this.$refs.tabList.refetch()
} else {
this.$message.error(msg + '失败')
}
})
},
fetchDeleteProject() {
const ids = this.multipleSelection.join()
deleteProject(ids).then(res => {
const msg = '删除项目'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.tableRefetch()
} else {
this.$message.error(msg + '失败')
}
})
},
tableRefetch() {
this.$refs.tabList.refetch()
}
} }
} }
</script> </script>
\ No newline at end of file <style scoped>
.el-tabs{
padding:15px;
height:100%;
}
.el-tabs ::v-deep.el-tabs__content{
height:calc(100% - 51px);
}
.el-tab-pane{
height:100%;
}
</style>
\ No newline at end of file
<template>
<div class="role page_container">
<div class="btn-bar">
<el-button v-if="hasCreate" type="primary" size="mini" @click="dialogVisible = true">新建角色</el-button>
</div>
<el-table :data="tableData" size="mini" style="width: 100%">
<el-table-column label="角色名称" min-width="120" prop="name" />
<el-table-column label="角色描述" min-width="150" prop="description" />
<el-table-column label="操作" min-width="130">
<template slot-scope="scope" v-if="scope.row.name !== 'superAdmin'">
<el-button v-if="hasUpdate" type="text" @click="handleEdit(scope.row)">编辑角色</el-button>
<el-button v-if="hasPermit" type="text" @click="handlePermission(scope.row)">权限配置</el-button>
<el-button v-if="hasDelete" type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog :title="dialogType === 'add' ? '新建角色' : '编辑角色'" :visible.sync="dialogVisible" width="520px" @close="handleClose">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="120px">
<el-form-item label="角色名称" prop="name">
<el-input v-model="form.name" size="small" placeholder="请输入角色名称"/>
</el-form-item>
<el-form-item label="角色描述" prop="description">
<el-input v-model="form.description" size="small" placeholder="请输入角色描述"/>
</el-form-item>
<el-form-item>
<el-button size="mini" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" size="mini">提交</el-button>
</el-form-item>
</el-form>
</el-dialog>
<el-drawer title="权限配置" :append-to-body="true" :visible.sync="drawerVisible" size="520px" @close="handleDrawerClose">
<div class="drawer-container">
<el-form :model="permitForm" ref="ruleForm" label-width="140px">
<el-form-item label="全选:">
<el-checkbox :indeterminate="indeterminate" v-model="checkAll" @change="handleCheckAllChange">全选</el-checkbox>
</el-form-item>
<el-form-item :label="`${perm.description}:`" v-for="perm in permissionList" :key="perm.name">
<el-checkbox-group v-model="permitForm[perm.v_model_key]" @change="handleCheckedChange">
<el-checkbox v-for="subItem in perm.children" :label="subItem.name" :key="subItem.name">{{subItem.description}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<div class="drawer-footer" style="text-align:center;">
<el-button size="mini" @click="drawerVisible = false">取 消</el-button>
<el-button size="mini" type="primary" @click="fetchUpdatePermissions" >提交</el-button>
</div>
</div>
</el-drawer>
</div>
</template>
<script>
import { getRoleList, saveRole, deleteRole, getRoleDetails, getPermissionList, setPermissions } from '@/api/role'
function recursionData(arr) {
const leafArr = []
function recursion(target) {
let list = []
const isArr = Array.isArray(target)
if (isArr) {
list = target
} else if (Array.isArray(target.children)) {
list = target.children
}
list.reverse()
list.filter(item => {
if (Array.isArray(item.children) && item.children.length > 0) {
const _prop = item.name.replace(/\//g, '_')
item.v_model_key = _prop
recursion(item)
} else {
// 累计leaf节点
const key = isArr ? '' : target.v_model_key
item.parent_v_model_key = key || ''
leafArr.push(item)
}
})
}
recursion(arr)
return leafArr
}
const defaultForm = {
name: '',
description: ''
}
const permissionForm = {}
export default {
data() {
return {
tableData: [],
dialogType: 'add',
dialogVisible: false,
form: Object.assign({}, defaultForm),
rules: {
name: { required: true, message: '请输入角色名称', trigger: 'blur' },
description: { required: true, message: '请输入角色描述', trigger: 'blur' }
},
permissionList: [],
permissionLeafList: 0,
drawerVisible: false,
permitForm: {},
indeterminate: false,
checkAll: false,
editData: ''
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('management/role/create')
},
hasUpdate() {
return this.permissions.includes('management/role/create-or-update')
},
hasDelete() {
return this.permissions.includes('management/role/delete')
},
hasPermit() {
return this.permissions.includes('management/role/manage')
},
checkedCount() {
let checkedCount = 0
const form = this.permitForm
for (const key in form) {
if (Array.isArray(form[key])) {
checkedCount += form[key].length
}
}
return checkedCount
},
isCheckAll() {
const count = this.permissionLeafList.length
return this.checkedCount === count
},
isIndeterminate() {
const checkedCount = this.checkedCount
return checkedCount > 0 && !this.isCheckAll
}
},
created() {
this.fetchRoleList()
this.fetchPermissionList()
},
methods: {
assignForm() {
Object.keys(permissionForm).forEach(key => {
permissionForm[key] = []
})
this.permitForm = Object.assign({}, permissionForm)
},
handleCheckedChange() {},
handleCheckAllChange(val) {
if (val) {
this.setCheckAll()
} else {
this.assignForm()
}
this.indeterminate = false
},
setCheckAll() {
this.permissionLeafList.forEach(item => {
const targetArr = this.permitForm[item.parent_v_model_key]
if (!targetArr.includes(item.name)) {
targetArr.push(item.name)
}
});
},
async handlePermission(row) {
const data = await this.fetchRoleDetails(row)
this.drawerVisible = true
this.editData = row
let rolePermissions = []
if (data && data.exist_permissions) rolePermissions = data.exist_permissions
this.reviewPermission(rolePermissions)
},
reviewPermission(permissions) {
// const permissionData = []
this.permissionLeafList.forEach(item => {
const targetArr = this.permitForm[item.parent_v_model_key]
if (permissions.includes(item.name) && !targetArr.includes(item.name)) {
targetArr.push(item.name)
}
})
this.checkAll = this.isCheckAll
this.indeterminate = this.isIndeterminate
},
handleEdit(row) {
this.dialogVisible = true
this.dialogType = 'edit'
this.form.name = row.name
this.form.description = row.description
},
handleClose() {
this.dialogType = 'add'
this.form = Object.assign({}, defaultForm)
},
handleDelete(row) {
this.$confirm('执行删除将导致相关联的用户无法登录此系统, 是否继续执行删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchDeleteRole({ name: row.name })
}).catch(() => {});
},
handleSubmit() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.fetchUpdateRole()
}
});
},
handleDrawerClose() {
this.assignForm()
},
fetchUpdateRole() {
saveRole(this.form).then(res => {
const msg = this.dialogType === 'add' ? '新建角色' : '编辑角色'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.fetchRoleList()
this.dialogVisible = false
} else {
this.$message.error(msg + '失败')
}
})
},
fetchDeleteRole(data) {
deleteRole(data).then(res => {
const msg = '删除角色'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.fetchRoleList()
} else {
this.$message.error(msg + '失败')
}
})
},
fetchRoleDetails(row) {
return new Promise((resolve, reject) => {
getRoleDetails({ name: row.name }).then(res => {
if (res.code === 0 && res.data.role) {
resolve(res.data)
} else reject(res)
}).catch(err => reject(err))
})
},
fetchRoleList() {
getRoleList().then(res => {
if (res.code === 0 && Array.isArray(res.data)) {
this.tableData = res.data
}
})
},
async fetchPermissionList() {
const result = await new Promise((resolve, reject) => {
getPermissionList().then((res) => {
if (res.code === 0 && Array.isArray(res.data)) {
resolve(res.data)
} else reject()
}).catch(() => {
reject()
})
})
if (result) {
const leafNodeList = recursionData(result)
this.permissionLeafCount = leafNodeList.length
this.permissionLeafList = leafNodeList
result.forEach(item => {
const key = item.v_model_key
permissionForm[key] = []
})
this.permissionList = result
this.assignForm()
}
},
fetchUpdatePermissions() {
const permissionArr = []
Object.keys(this.permitForm).forEach(key => {
const item = this.permitForm[key]
if (Array.isArray(item)) {
item.forEach((it) => {
permissionArr.push(it)
})
}
})
const params = {
name: this.editData.name || '',
permissions: permissionArr
}
if (!params.name || params.permissions.length === 0) {
return
}
setPermissions(params).then(res => {
const msg = '权限配置'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '')
this.drawerVisible = false
} else {
this.$message.error(msg + '失败')
}
})
}
}
}
</script>
<style scoped>
.role{
box-sizing:border-box;
padding: 15px;
}
.btn-bar{
text-align:right;
padding-bottom:10px;
}
.drawer-container{
display:flex;
flex-direction: column;
height: 100%;
}
.drawer-container form{
flex: 1;
overflow-y: auto;
}
.drawer-container .el-form-item{
margin-bottom:8px;
}
.drawer-footer{
padding: 10px 0 10px;
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
}
</style>
\ No newline at end of file
<template>
<div class="student-course">
<div class="btn-bar">
<el-checkbox v-if="hasSave" v-model="isEdit" style="margin-right:15px;">编辑课程</el-checkbox>
<el-button v-if="hasSave" type="primary" size="mini" @click="handleSave" :disabled="!isEdit">保存</el-button>
<el-button v-if="hasSave" type="primary" size="mini" @click="handleAdd">添加</el-button>
</div>
<el-table :data="tableData" :row-key="getRowKey" :expand-row-keys="expands" class="program-table" style="width: 100%" :header-cell-style="{background:'#f9f9f9'}" @expand-change="expandChange">
<!-- <el-table-column type="selection" width="55"/> -->
<el-table-column type="expand">
<template slot-scope="scope">
<el-table :data="scope.row.courses" size="mini" style="width: 100%">
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="课程ID" prop="course_id" min-width="120" />
<el-table-column label="课程名称" prop="chinese_name" min-width="110" />
<el-table-column label="课程名称(英)" prop="english_name" min-width="140" />
<el-table-column label="学期" min-width="100">
<template slot-scope="subScope">
<span v-show="!isEdit">{{subScope.row.semester}}</span>
<el-input v-show="isEdit" v-model="subScope.row.semester" size="mini" placeholder="请填写" />
</template>
</el-table-column>
<el-table-column label="分数" min-width="70">
<template slot-scope="subScope">
<span v-show="!isEdit">{{subScope.row.score}}</span>
<el-input v-show="isEdit" v-model="subScope.row.score" size="mini" placeholder="请填写" />
</template>
</el-table-column>
<el-table-column label="成绩发布时间" min-width="160">
<template slot-scope="subScope">
<span v-show="!isEdit">{{subScope.row.pubdate}}</span>
<el-date-picker v-show="isEdit" v-model="subScope.row.pubdate" type="datetime" value-format="yyyy-MM-dd hh:mm:ss" placeholder="请选择" size="mini" style="width:100%;"/>
</template>
</el-table-column>
<!-- <el-table-column label="操作" min-width="50">
<template slot-scope="subScope">
<el-button type="text" @click="handleDelete(scope.row.courses, subScope.row)">删除</el-button>
</template>
</el-table-column> -->
</el-table>
</template>
</el-table-column>
<el-table-column label="项目名称" prop="project.chinese_name"/>
<el-table-column label="项目状况">
<template slot-scope="scope">
<span v-show="!isEdit">{{scope.row.graduation_status | filterStatus(statusMap)}}</span>
<el-select v-show="isEdit" v-model="scope.row.graduation_status" placeholder="请选择" size="mini">
<el-option v-for="(label, key) in statusMap" :key="key" :label="label" :value="parseInt(key)"/>
</el-select>
</template>
</el-table-column>
<el-table-column label="毕业时间">
<template slot-scope="scope">
<span v-show="!isEdit && scope.row.graduation_status !== 2">{{scope.row.graduation_time}}</span>
<el-date-picker v-show="isEdit && scope.row.graduation_status === 2" v-model="scope.row.graduation_time" type="datetime" value-format="yyyy-MM-dd hh:mm:ss" placeholder="请选择" size="mini" style="width:100%;"/>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button v-if="hasSave && isEdit" type="text" @click="handleDelete(scope.row.project_id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-drawer
title="添加项目"
size="1000px"
ref="drawer"
:append-to-body="true"
:destroy-on-close="true"
:visible.sync="drawerVisible">
<table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<template #footer>
<div>
<el-button size="mini" @click="handleCancel">取消</el-button>
<el-button type="primary" size="mini" :disabled="!multipleSelection.length" @click="handleEnsure">确认</el-button>
</div>
</template>
</table-list>
</el-drawer>
</div>
</template>
<script>
import TableList from '@/components/TableList'
import { getStudentCourseList, saveStudentCourseList } from '@/api/student'
import { getProjectList } from '@/api/project'
export default {
components: { TableList },
props: {
id: {
type: String
}
},
data() {
return {
statusMap: { 1: '在读', 2: '毕业', 3: '失联', 4: '休学', 5: '退学' },
isEdit: false,
tableData: [],
expands: [],
drawerVisible: false,
typeList: [
{ id: 1, name: '长期' },
{ id: 2, name: '短期' }
],
relationProjectIds: [],
multipleSelection: []
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasSave() {
return this.permissions.includes('v1/student/save-course')
},
tableOptions() {
return {
remote: {
httpRequest: getProjectList,
params: { key: '', type: '' },
callback: this.tableCallback
},
filters: [
{ type: 'input', placeholder: '请输入课程ID/课程名称', prop: 'key' },
{ type: 'select', placeholder: '项目类型', prop: 'type', options: this.typeList, labelKey: 'name', valueKey: 'id' }
],
columns: [
{
type: 'selection',
minWidth: '50px',
fixed: 'left',
visible: this.hasDelete,
'reserve-selection': true,
selectable: this.tableSelectable
},
{ prop: 'id', label: '项目ID', minWidth: '155px' },
{ prop: 'chinese_name', label: '项目名称', minWidth: '130px' },
{ prop: 'english_name', label: '项目名称(英)', minWidth: '160px' },
{
prop: 'type',
label: '项目类型',
minWidth: '80px',
computed({ row }) {
return row.type === 1 ? '长期' : '短期'
}
},
{ prop: 'pubdate', label: '发布时间', minWidth: '150px' },
{ prop: 'updated_time', label: '修改时间', minWidth: '150px' }
],
// hasPagination: false,
'row-key': row => { return row.id }
}
}
},
filters: {
filterStatus(val, statusMap) {
return statusMap[val]
}
},
created() {
this.fetchCourseList()
},
methods: {
tableCallback(list) {
if (Array.isArray(list) && this.relationProjectIds.length > 0) {
// 默认删除已有项
list = list.filter(item => !this.relationProjectIds.includes(item.id))
// 默认选中已有项
// const arr = []
// const listIds = list.map(item => item.id)
// this.relationProjectIds.forEach(id => {
// if (listIds.indexOf(id) > -1) {
// arr.push(id)
// }
// })
// if (arr.length) this.$refs.tabList.tableReview(arr, list)
}
return list;
},
tableSelectable(row) {
if (this.relationProjectIds.includes(row.id)) {
return false
} else {
return true
}
},
getRowKey (row) {
return row.id
},
expandChange(row, rows) {
if (rows.length) {
this.expands = [row.id]
} else this.expands = []
},
handleSave() {
for (let i = 0; i < this.tableData.length; i++) {
const item = this.tableData[i]
if (!item.status) {
this.$message.error('请选择项目状态')
this.expands = [this.tableData[i].id]
return false
}
for (let j = 0; j < item.courses.length; j++) {
const subItem = item.courses[j]
if (!subItem.semester) {
this.$message.error('学期不能为空')
this.expands = [this.tableData[i].id]
return false
}
}
}
this.fetchSave()
},
handleAdd() {
this.drawerVisible = true
},
handleDelete(id) {
console.log(this.tableData)
this.tableData = this.tableData.filter(item => item.project_id !== id)
},
handleCancel() {
this.$refs.drawer.closeDrawer()
},
handleEnsure() {
console.log(this.multipleSelection)
const arr = []
this.multipleSelection.forEach(item => {
const _item = {
project_id: item.id,
graduation_time: null,
graduation_status: null,
project: {
chinese_name: item.chinese_name,
english_name: item.english_name
},
courses: []
}
item.courses.forEach(subItem => {
_item.courses.push({
course_id: subItem.id,
semester: null,
score: null,
pubdate: null,
chinese_name: subItem.chinese_name,
english_name: subItem.english_name
})
})
arr.push(_item)
})
console.log(arr)
this.tableData = this.tableData.concat(arr)
this.handleCancel()
},
handleSelectionChange(val) {
this.multipleSelection = val
},
fetchCourseList() {
getStudentCourseList(this.id || '').then(res => {
if (res.code === 0 && res.message === 'OK') {
this.tableData = res.data
this.relationProjectIds = this.tableData.map(item => item.project_id)
}
})
},
fetchSave() {
saveStudentCourseList(this.id, { data: JSON.stringify(this.tableData) }).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success('数据保存成功')
} else {
this.$message.error('数据保存失败')
}
})
}
}
}
</script>
<style scoped>
.student-course{
min-height: 60vh;
}
.btn-bar{
text-align:right;
margin-bottom:6px;
}
::v-deep.program-table .el-table-column--selection .cell{
padding-left:10px;
padding-right:0;
}
::v-deep.program-table>.el-table__body-wrapper>table>colgroup>col:first-child{
width:30px;
}
.student-course ::v-deep.program-table .el-table__expanded-cell{
padding:15px 25px !important;
}
</style>
\ No newline at end of file
<template>
<div class="student-degree">
<div class="btn-bar">
<el-button v-if="hasSave" type="primary" size="mini" @click="handleCreate">新增</el-button>
</div>
<el-table :data="tableData" size="mini" style="width: 100%">
<el-table-column label="Major" width="120" prop="major" />
<el-table-column label="Degree Level" min-width="150" prop="level" />
<el-table-column label="Degree Type" min-width="240" prop="type" />
<el-table-column label="Program" min-width="120" prop="project.chinese_name" />
<el-table-column label="Operations" min-width="130">
<template slot-scope="scope">
<el-button v-if="hasSave" type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button v-if="hasDelete" type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="520px" append-to-body @close="handleClose">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="120px">
<el-form-item label="Major" prop="major">
<el-input v-model="form.major" size="small" placeholder="请输入课程"/>
</el-form-item>
<el-form-item label="Degree Level" prop="level">
<el-input v-model="form.level" size="small" placeholder="请输入学位"/>
</el-form-item>
<el-form-item label="Degree Type" prop="type">
<el-input v-model="form.type" size="small" placeholder="请输入专业"/>
</el-form-item>
<el-form-item label="Program" prop="project_id">
<el-select v-model="form.project_id" placeholder="请选择项目" size="small" style="width:100%;">
<el-option v-for="item in projectOptions" :label="item.project.chinese_name" :value="item.project_id" :key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button size="mini" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" size="mini">提交</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import { getStudentDegreeList, saveStudentDegree, getStudentCourseList } from '@/api/student'
const defaultForm = {
major: '',
level: '',
type: '',
project_id: ''
}
export default {
props: {
id: {
type: String
}
},
data() {
return {
tableData: [],
form: Object.assign({ student_id: this.id }, defaultForm),
rules: {
major: { required: true, message: '请输入专业', trigger: 'blur' },
level: { required: true, message: '请学位', trigger: 'blur' },
type: { required: true, message: '请学位类型', trigger: 'blur' },
project_id: { required: true, message: '请选择项目', trigger: 'blur' }
},
projectOptions: [],
dialogVisible: false,
dialogTitle: ''
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasSave() {
return this.permissions.includes('v1/student/save-degree')
},
hasDelete() {
return this.permissions.includes('v1/student/delete-degree')
}
},
created() {
this.fetchDegreeList()
},
methods: {
handleCreate() {
this.dialogTitle = '新增学位'
this.dialogVisible = true
this.fetchProjectList()
},
handleEdit(val) {
this.dialogTitle = '编辑学位'
this.dialogVisible = true
this.form = val
},
handleDelete(val) {
console.log(val)
},
handleClose() {
this.form = Object.assign({ student_id: this.id }, defaultForm)
},
handleSubmit() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.fetchSubmit()
}
})
},
fetchDegreeList() {
const params = {
page: 1,
'per-page': 100
}
getStudentDegreeList(this.id, params).then(res => {
if (res.code === 0 && res.data && Array.isArray(res.data.list)) {
this.tableData = res.data.list
}
})
},
fetchProjectList() {
getStudentCourseList(this.id || '').then(res => {
if (res.code === 0 && res.message === 'OK') {
this.projectOptions = res.data
}
})
},
fetchSubmit() {
saveStudentDegree(this.form).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success(this.dialogTitle + '成功')
this.fetchDegreeList()
this.dialogVisible = false
} else {
this.$message.error(this.dialogTitle + '失败')
}
})
}
}
}
</script>
<style scoped>
.student-degree{
min-height:60vh;
}
.btn-bar{
text-align:right;
}
</style>
\ No newline at end of file
<template>
<div class="student-info">
<div class="btn-bar" v-if="!isEdit">
<el-button type="primary" size="mini" v-if="hasUpdate" @click="isEdit = true">编辑</el-button>
</div>
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="120px" class="form-container">
<div class="left">
<el-form-item label="学籍ID:" prop="id">
<el-input v-model="form.id" placeholder="请输入学籍ID" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="姓名:" prop="chinese_name">
<el-input v-model="form.chinese_name" placeholder="请输入姓名" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="姓名(英):" prop="english_name">
<el-input v-model="form.english_name" placeholder="请输入英文名" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="form.gender" placeholder="请选择性别" size="mini" style="width:100%;" :disabled="!isEdit">
<el-option label="男" :value="0"></el-option>
<el-option label="女" :value="1"></el-option>
<el-option label="未知" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="年龄:">
<el-input v-model.number="form.age" type="number" min="0" max="100" placeholder="请输入年龄" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="身份证号:" prop="id_number">
<el-input v-model="form.id_number" placeholder="请输入身份证号" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="手机号:" prop="mobile">
<el-input v-model="form.mobile" placeholder="请输入手机号" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="邮箱地址:" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱地址" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="国家:">
<el-input v-model="form.country" placeholder="请输入国家" size="mini" :disabled="!isEdit"/>
</el-form-item>
</div>
<div class="right">
<el-form-item label="头像:" class="avatar-upload">
<el-upload
:disabled="!isEdit"
class="avatar-uploader" action="https://webapp-pub.oss-cn-beijing.aliyuncs.com" :show-file-list="false" :before-upload="val => beforeUpload(val, 'web_img')" :on-success="(res, file) => handleSuccess(res, file, 'web_img')" :data="uploadData">
<img v-if="form.head_img" :src="form.head_img" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<div class="avatar-upload-bar" v-if="form.head_img">
<div class="avatar-preview">
<i class="el-icon-zoom-in"></i>
<el-image :src="form.head_img" :preview-src-list="[form.head_img]" :z-index="9999"/>
</div>
<div>
<i class="el-icon-download" @click="handleDownload"></i>
</div>
<div v-if="isEdit">
<i class="el-icon-circle-close" @click="form.head_img = ''"></i>
</div>
</div>
</el-form-item>
<el-form-item label="省市:">
<el-input v-model="form.city" placeholder="请输入省市" size="mini" :disabled="!isEdit"/>
</el-form-item>
<el-form-item label="是否有学籍:">
<el-select v-model="form.is_student" placeholder="请选择" size="mini" style="width:100%;" disabled>
<el-option label="无" :value="0"></el-option>
<el-option label="有" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="创建人:">
<el-input v-model="form.created_operator" size="mini" disabled/>
</el-form-item>
<el-form-item label="创建时间:">
<el-input v-model="form.created_time" size="mini" disabled/>
</el-form-item>
<el-form-item label="修改人:">
<el-input v-model="form.updated_operator" size="mini" disabled/>
</el-form-item>
<el-form-item label="修改时间:">
<el-input v-model="form.updated_time" size="mini" disabled/>
</el-form-item>
</div>
</el-form>
<div class="foot-btn-bar" v-if="isEdit">
<el-button size="mini" @click="handleCancel">取消</el-button>
<el-button type="primary" size="mini" @click="handleEnsure">确认提交</el-button>
</div>
</div>
</template>
<script>
import { getSignature } from '@/api/common'
import { updateStudentInfo } from '@/api/student'
import md5 from 'blueimp-md5'
const MOBILE_REG = /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/
const EMAIL_REG = /^[A-Za-z0-9]+([_.\\-][A-Za-z0-9]+)*@[A-Za-z0-9-.]+$/
const IDCARD_REG = /(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}[0-9Xx]$)/
export default {
props: {
info: {
type: Object
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasUpdate() {
return this.permissions.includes('v1/student/update')
}
},
data() {
const checkMobile = (rule, value, callback) => {
if (value) {
if (!MOBILE_REG.test(value)) {
callback(new Error('手机号格式错误'));
} else {
callback()
}
}
}
const checkEmail = (rule, value, callback) => {
if (value) {
if (!EMAIL_REG.test(value)) {
callback(new Error('邮箱格式错误'));
} else {
callback()
}
}
}
const checkID = (rule, value, callback) => {
if (value) {
if (!IDCARD_REG.test(value)) {
callback(new Error('身份证号格式错误'));
} else {
callback()
}
}
}
return {
isEdit: false,
uploadData: {},
form: {
id: '',
chinese_name: '',
english_name: '',
gender: '',
age: '',
id_number: '',
mobile: '',
email: '',
head_img: '',
country: '',
city: '',
is_student: '',
editor: '',
update_time: '',
creator: '',
created_time: ''
},
rules: {
id: { required: true, message: '请输入学籍ID', trigger: 'blur' },
chinese_name: { required: true, message: '请输入姓名', trigger: 'blur' },
english_name: { required: true, message: '请输入英文名', trigger: 'blur' },
id_number: { validator: checkID, trigger: 'blur' },
mobile: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ validator: checkMobile, trigger: 'blur' }
],
email: [
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ validator: checkEmail, trigger: 'blur' }
]
}
}
},
created() {
if (this.info && this.info.id) {
this.form = Object.assign({}, this.info)
}
},
methods: {
handleCancel() {
this.$refs.ruleForm.clearValidate()
this.isEdit = false
},
handleEnsure() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.fetchUpdate()
}
})
},
handleSuccess(res, file, target) {
this.fileLoading = ''
const _file = file.raw
this.form.head_img = _file.src
},
async handleDownload() {
const fileUrl = await this.getBase64(this.form.head_img)
const elink = document.createElement('a')// 创建一个a标签
elink.download = fileUrl;// 设置a标签的下载属性
elink.style.display = 'none';// 将a标签设置为隐藏
elink.href = fileUrl;// 把之前处理好的地址赋给a标签的href
document.body.appendChild(elink);// 将a标签添加到body中
elink.click();// 执行a标签的点击方法
// URL.revokeObjectURL(elink.href) // 下载完成释放URL 对象
document.body.removeChild(elink)// 移除a标签
},
getBase64(url) {
return new Promise((resolve, reject) => {
var Img = new window.Image()
var dataURL = ''
Img.setAttribute('crossOrigin', 'Anonymous')
Img.src = url + '?v=' + Math.random()
Img.onload = function() {
// 要先确保图片完整获取到,这是个异步事件
var canvas = document.createElement('canvas') // 创建canvas元素
var width = Img.width // 确保canvas的尺寸和图片一样
var height = Img.height
canvas.width = width
canvas.height = height
canvas.getContext('2d').drawImage(Img, 0, 0, width, height) // 将图片绘制到canvas中
dataURL = canvas.toDataURL('image/jpeg') // 转换图片为dataURL
resolve(dataURL)
}
})
},
fetchUpdate() {
updateStudentInfo(this.form.id, this.form).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success('提交数据成功')
} else {
this.$message.error(res.message || '提交数据失败')
}
})
},
beforeUpload(file, target) {
let errorMsg = ''
if (!file.type.includes('image/')) {
errorMsg = '上传图片格式错误,只允许png/jpg格式'
}
if (errorMsg) {
this.$message.error(errorMsg)
return false
} else {
this.fileLoading = target
const fileName = file.name
const key = 'upload/cms-admin/' + md5(fileName + new Date().getTime()) + fileName.substr(fileName.lastIndexOf('.'))
return new Promise((resolve, reject) => {
getSignature()
.then(response => {
const { accessid, policy, signature, host } = response
this.uploadData = { key, OSSAccessKeyId: accessid, policy, signature, success_action_status: '200' }
file.src = `${host}/${key}`
resolve(true)
})
.catch(err => {
console.log(err)
reject(err)
})
})
}
}
}
}
</script>
<style scoped>
.btn-bar{
text-align:right;
}
.form-container{
padding-top:15px;
display:flex;
align-items: stretch;
}
.form-container.details ::-webkit-input-placeholder{
color:transparent;
}
.form-container.details :-ms-input-placeholder{
color:transparent;
}
.form-container.details ::placeholder{
color:transparent;
}
.form-container .left{
width:calc(50% - 20px);
padding-right:20px;
}
.form-container .right{
flex-shrink: 0;
flex: 1;
box-sizing:border-box;
}
.avatar-uploader, .file-uploader{
display:inline-block;
vertical-align: top;
width:160px;
}
.avatar-uploader{
height:165px;
}
.avatar-uploader ::v-deep.el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
vertical-align: middle;
}
.avatar-uploader ::v-deep.el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 155px;
height: 155px;
line-height: 155px;
text-align: center;
}
.avatar {
width: 150px;
height: 150px;
display: block;
}
.avatar-upload-bar{
position:absolute;
left:1px;
top:121px;
width:150px;
height:30px;
border-radius:0 0 4px 0;
overflow: hidden;
background:rgba(0,0,0,.3);
color:#fff;
display:none;
border-radius:0 0 6px 6px;
}
.avatar-upload .el-form-item__content:hover .avatar-upload-bar{
display:flex;
}
.avatar-upload-bar>div{
flex:1;
position:relative;
cursor:pointer;
}
.avatar-upload-bar>div>i{
font-size:20px;
width:100%;
height:100%;
text-align:center;
line-height:30px;
}
.avatar-preview{
cursor:zoom-in;
}
.avatar-preview .el-image{
position:absolute;
left:0;
top:0;
width:100%;
height:100%;
opacity:0;
z-index:1;
}
.avatar-preview ::v-deep.el-image__preview{
cursor: zoom-in;
}
.foot-btn-bar{
text-align:center;
}
</style>
\ No newline at end of file
...@@ -2,12 +2,16 @@ ...@@ -2,12 +2,16 @@
<div class="student page_container"> <div class="student page_container">
<table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange"> <table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<template #header-aside> <template #header-aside>
<el-button type="primary" size="mini" @click="handleImport" v-if="hasImport">导入</el-button> <el-button type="primary" size="mini" @click="dialogVisible = true" v-if="hasImport">导入</el-button>
</template>
<!-- 图片 -->
<template v-slot:table_img="{ row }">
<img :src="row.head_img" height="40" />
</template>
<!-- Id -->
<template v-slot:table_id="scope">
<span class="details-handle" @click="handleDetails(scope.row)">{{ scope.row.student_number }}</span>
</template> </template>
<!-- 项目Id -->
<template v-slot:table_id="scope"
><span class="details-handle" @click="handleDetails(scope.row)">{{ scope.row.id }}</span></template
>
<!-- 状态 --> <!-- 状态 -->
<template v-slot:status="{ row }"> <template v-slot:status="{ row }">
<el-switch <el-switch
...@@ -21,93 +25,171 @@ ...@@ -21,93 +25,171 @@
></el-switch> ></el-switch>
</template> </template>
<template #footer> <template #footer>
已选中 {{multipleSelection.length}} <div>
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasDelete" 已选中 {{multipleSelection.length}}
>删除</el-button <el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove" v-if="hasDelete">删除</el-button>
> <el-dropdown size="small" @command="handleCommand" v-if="hasExport">
<el-button type="primary" size="mini" plain>
导出<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="all">导出全部</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template> </template>
</table-list> </table-list>
<el-drawer
:title="drawerTitle"
:visible.sync="drawerVisible"
:close-on-click-modal="false"
:destroy-on-close="true"
size="1100px"
top="15px"
:before-close="handleBeforeClose"
@close="handleClose">
<el-tabs v-model="tabsActive" type="card">
<el-tab-pane label="基本信息" name="info" v-if="hasViewInfo">
<tab-info :info="details" />
</el-tab-pane>
<el-tab-pane label="学位信息" name="degree" v-if="hasViewDegree">
<tab-degree :id="details.id"/>
</el-tab-pane>
<el-tab-pane label="项目课程" name="course" v-if="hasViewCourse">
<tab-course :id="details.id"/>
</el-tab-pane>
</el-tabs>
</el-drawer>
<el-dialog title="导入学员" :visible.sync="dialogVisible" width="480px" :destroy-on-close="true" :close-on-click-modal="false" @close="handleDialogClose">
<div>
导入类型:
<el-select v-model="importType" placeholder="请选择" size="mini" style="margin-bottom:15px;">
<el-option v-for="item in typeList" :label="item.label" :value="item.value" :key="item.value" />
</el-select>
</div>
<el-upload
class="file-import"
ref="upload"
action="/"
:auto-upload="false"
:file-list="fileList"
:limit="1"
:before-upload="beforeUpload"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
>
<el-button slot="trigger" size="mini" type="primary">选取文件</el-button>
<span slot="tip" style="margin-left:10px;">只能上传excel文件</span>
</el-upload>
<div style="text-align:center;">
<el-button size="mini" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" size="mini" @click="submitUpload">确认提交</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
// accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
import TableList from '@/components/TableList' import TableList from '@/components/TableList'
import TabInfo from './components/info.vue'
import TabDegree from './components/degree.vue'
import TabCourse from './components/course.vue'
import { getStudentList, deleteStudent, importStudents, exportStudentList } from '@/api/student'
import { splitStrLast, funDownload } from '@/utils/utils'
const genderMap = {
0: '女',
1: '男',
2: '未知'
}
export default { export default {
components: { TableList }, components: { TableList, TabInfo, TabDegree, TabCourse },
data() { data() {
return { return {
multipleSelection: [] multipleSelection: [],
drawerVisible: false,
details: {},
tabsActive: 'info',
dialogVisible: false,
fileList: [],
typeList: [
{ label: '基本信息', value: 'info' },
{ label: '学位信息', value: 'degree' },
{ label: '课程信息', value: 'course' }
],
importType: 'info'
} }
}, },
computed: { computed: {
drawerTitle() {
let title = '学员详情'
if (this.details.chinese_name) title += ':' + this.details.chinese_name
return title
},
permissions() {
return this.$store.state.user.permissions || []
},
hasImport() { hasImport() {
return true return this.permissions.includes('v1/student/import')
},
hasExport() {
return this.permissions.includes('v1/student/download')
}, },
hasDelete() { hasDelete() {
return true return this.permissions.includes('v1/student/delete')
},
hasViewInfo() {
return this.permissions.includes('v1/student/view')
},
hasViewDegree() {
return this.permissions.includes('v1/student/degree-list')
},
hasViewCourse() {
return this.permissions.includes('v1/student/course-list')
},
hasSaveCourse() {
return this.permissions.includes('v1/student/save-course')
}, },
tableOptions() { tableOptions() {
return { return {
data: [
{
id: '123000',
country: '中国',
city: '辽宁省大连市',
name: '张三',
en_name: 'San Zhang',
sex: '女',
age: '29',
ID: '211202********4567',
mobile: '13367891234',
email: 'ahgl@163.com',
hasSchoolRoll: '是',
editor: '刘义',
update_time: '2016-09-20 11:00:54',
creator: '刘义',
created_time: '2016-09-20 11:00:54'
},
{
id: '123001',
country: '中国',
city: '河北省石家庄',
name: '李四',
en_name: 'Si Li',
sex: '男',
age: '33',
ID: '211202********4567',
mobile: '13367891234',
email: 'ahgl@163.com',
hasSchoolRoll: '是',
editor: '刘义',
update_time: '2016-09-20 11:00:54',
creator: '刘义',
created_time: '2016-09-20 11:00:54'
}
],
remote: { remote: {
httpRequest: null, httpRequest: getStudentList,
params: { name: '', mobile: '', email: '' } params: { key: '', mobile: '', email: '' }
}, },
filters: [ filters: [
{ type: 'input', placeholder: '请输入学籍ID/学员姓名', prop: 'name' }, { type: 'input', placeholder: '请输入学籍ID/学员姓名', prop: 'key' },
{ type: 'input', placeholder: '手机号码', prop: 'mobile' }, { type: 'input', placeholder: '手机号码', prop: 'mobile' },
{ type: 'input', placeholder: '邮箱地址', prop: 'email' } { type: 'input', placeholder: '邮箱地址', prop: 'email' }
], ],
columns: [ columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete }, { type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete },
{ prop: 'id', label: '学籍ID', slots: 'table_id', minWidth: '120px', fixed: 'left' }, { prop: 'head_img', label: '图像', slots: 'table_img', minWidth: '80px', fixed: 'left' },
{ prop: 'country', label: '国家', minWidth: '120px' }, { prop: 'student_number', label: '学籍ID', slots: 'table_id', minWidth: '160px', fixed: 'left' },
{ prop: 'city', label: '省市', minWidth: '120px' }, { prop: 'country', label: '国家', minWidth: '100px' },
{ prop: 'name', label: '姓名', minWidth: '90px' }, { prop: 'city', label: '省市', minWidth: '100px' },
{ prop: 'en_name', label: '姓名(英)', minWidth: '150px' }, { prop: 'chinese_name', label: '姓名', minWidth: '90px' },
{ prop: 'sex', label: '性别', minWidth: '60px' }, { prop: 'english_name', label: '姓名(英)', minWidth: '130px' },
{
prop: 'gender',
label: '性别',
minWidth: '60px',
computed({ row }) {
return genderMap[row.gender || 2]
}
},
{ prop: 'age', label: '年龄', minWidth: '60px' }, { prop: 'age', label: '年龄', minWidth: '60px' },
{ prop: 'ID', label: '身份证号', minWidth: '150px' }, { prop: 'id_number', label: '身份证号', minWidth: '150px' },
{ prop: 'mobile', label: '电话号码', minWidth: '130px' }, { prop: 'mobile', label: '电话号码', minWidth: '130px' },
{ prop: 'email', label: '邮箱地址', minWidth: '150px' }, { prop: 'email', label: '邮箱地址', minWidth: '150px' },
{ prop: 'hasSchoolRoll', label: '是否有学籍', minWidth: '80px' }, {
{ prop: 'editor', label: '修改人', minWidth: '80px' }, prop: 'is_student',
{ prop: 'update_time', label: '修改时间', minWidth: '150px' }, label: '是否有学籍',
{ prop: 'creator', label: '创建人', minWidth: '80px' }, minWidth: '80px',
computed({ row }) {
return row.is_student === 1 ? '有' : '无'
}
},
{ prop: 'updated_operator', label: '修改人', minWidth: '80px' },
{ prop: 'updated_time', label: '修改时间', minWidth: '150px' },
{ prop: 'created_operator', label: '创建人', minWidth: '80px' },
{ prop: 'created_time', label: '创建时间', minWidth: '150px' } { prop: 'created_time', label: '创建时间', minWidth: '150px' }
], ],
bodyHeight: 'calc(100% - 50px)' bodyHeight: 'calc(100% - 50px)'
...@@ -119,8 +201,96 @@ export default { ...@@ -119,8 +201,96 @@ export default {
this.multipleSelection = val.map(item => item.id) this.multipleSelection = val.map(item => item.id)
}, },
handleImport() {}, handleImport() {},
handleRemove() {}, handleRemove() {
handleDetails() {} const ids = this.multipleSelection.join()
console.log(ids)
this.fetchDeleteStudents(ids)
},
handleDetails(val) {
if (this.hasViewInfo || this.hasViewDegree || this.hasViewCourse) {
this.drawerVisible = true
this.details = val
} else {
this.$message.error('您没有权限查看详情')
}
},
handleClose() {
},
handleDialogClose() {
this.fileList = []
},
beforeUpload(file) {
console.log(file)
const suffix = splitStrLast(file.name, '.')
if (!['xlsx', 'xls'].includes(suffix)) {
this.$message.error('只能上传excel文件')
return false
} else {
const formData = new window.FormData()
formData.append('type', this.importType)
formData.append('file', file)
return new Promise((resolve, reject) => {
importStudents(formData).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success('导入数据成功')
resolve(true)
this.$refs.tabList.refetch()
window.setTimeout(() => {
this.dialogVisible = false
}, 300)
} else {
this.$message.error(res.message || '导入数据失败,请重选选取文件上传')
resolve(false)
}
})
})
}
},
submitUpload() {
this.$refs.upload.submit()
},
handleBeforeClose(done) {
if (this.hasSaveCourse) {
this.$confirm('您已确认保存好修改的数据,并关闭弹框吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
done()
})
} else {
done()
}
},
handleCommand(type) {
if (type === 'all') this.fetchExportAll()
},
fetchExportAll() {
exportStudentList(this.tableOptions.remote.params).then(res => {
console.log(res)
if (res && res.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
const url = URL.createObjectURL(res)
funDownload(url, `学生信息表_${Date.now()}.xlsx`)
}
})
},
fetchDeleteStudents(ids) {
deleteStudent(ids).then(res => {
if (res.code === 0 && res.message === 'OK') {
this.$message.success('删除学员成功')
this.$refs.tabList.refetch()
} else {
this.$message.error('删除学员失败')
}
})
}
} }
} }
</script> </script>
\ No newline at end of file <style scoped>
.el-tabs{
padding:15px;
}
.file-import{
padding-bottom:15px;
}
</style>
\ No newline at end of file
<template>
<el-select v-model="userId" v-bind="options" placeholder="输入邮箱/手机号码搜索" filterable remote :remote-method="fetchUserList" :loading="searchUsersloading" @change="handleChange">
<el-option :label="user.realname || user.nickname " :value="user.id" v-for="user in userList" :key="user.id" >
<span style="float: left">
{{ user.realname || user.nickname }}
<template v-if="user.mobile">(手机号:{{user.mobile}})</template>
</span>
<span style="float: right; color: #8492a6; font-size: 13px; margin:0 20px 0 10px;" v-if="user.email">邮箱:{{ user.email }}</span>
<span style="float: right; color: #8492a6; font-size: 13px; margin:0 20px 0 10px;" v-else>ID:{{ user.id }}</span>
</el-option>
</el-select>
</template>
<script>
import { searchUsers } from '@/api/user'
export default {
props: {
value: {
type: [Array, String]
},
defaultList: {
type: Array,
default: () => {
return []
}
},
options: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
userId: this.value,
searchUsersloading: false,
userList: []
}
},
watch: {
value: {
handler: function (nv) {
this.userId = nv
},
immediate: true,
deep: true
},
defaultList: {
handler: function (nv) {
this.userList = nv
},
immediate: true,
deep: true
}
},
methods: {
handleChange() {
const selectedUser = this.userList.find(item => item.id === this.userId)
this.$emit('input', this.userId)
this.$emit('select', selectedUser)
},
fetchUserList(val) {
this.searchUsersloading = true
searchUsers({ q: val || '' })
.then(res => {
this.searchUsersloading = false
if (res.data && Array.isArray(res.data.list)) {
this.userList = res.data.list
}
})
}
}
}
</script>
<style scoped>
.el-select{
width:100%;
}
</style>
\ No newline at end of file
<template>
<div class="user page_container">
<div class="btn-bar">
<div class="filter">
<div style="display:inline-block;width:220px;margin-right:12px;" v-if="hasSearchUser">
<user-search v-model="filter.uid" :options="{ size: 'small', clearable: true }"/>
</div>
<div style="display:inline-block;width:220px;margin-right:12px;">
<el-select v-model="filter.role" placeholder="请选择角色" size="small" style="width:100%;" clearable>
<el-option :label="role.description" :value="role.name" v-for="role in roleList" :key="role.id" ></el-option>
</el-select>
</div>
<el-button type="primary" size="small" icon="el-icon-search" @click="fetchAssignUserList">查询</el-button>
<el-button icon="el-icon-refresh-left" size="small" @click="handleReset">重置</el-button>
</div>
<el-button v-if="hasCreate" type="primary" size="mini" @click="drawerVisible = true">新增授权</el-button>
</div>
<el-table :data="tableData" size="mini" style="width: 100%">
<el-table-column label="用户ID" min-width="120" prop="userId" />
<el-table-column label="用户姓名" min-width="150" prop="userName" />
<el-table-column label="角色" min-width="150" prop="roleName" />
<el-table-column label="操作" min-width="130">
<template slot-scope="scope" v-if="scope.row.name !== 'superAdmin'">
<!-- <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button> -->
<el-button v-if="hasDelete" type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-drawer title="新增用户" :append-to-body="true" :visible.sync="drawerVisible" size="520px">
<div class="drawer-container">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="140px">
<el-form-item label="账户" prop="uid" v-if="dialogType === 'edit'">
<el-input v-model="form.uid" size="small" disabled></el-input>
</el-form-item>
<el-form-item label="账户" prop="uid" v-else style="position:relative;">
<user-search v-model="form.uid" :options="{ size: 'small' }"/>
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" placeholder="请选择角色" size="small" style="width:100%;">
<el-option :label="role.description" :value="role.name" v-for="role in roleList" :key="role.id" ></el-option>
</el-select>
</el-form-item>
</el-form>
<div class="drawer-footer">
<el-button size="mini" @click="drawerVisible = false">取 消</el-button>
<el-button size="mini" type="primary" @click="handleSubmit" >提交</el-button>
</div>
</div>
</el-drawer>
</div>
</template>
<script>
import UserSearch from './components/UserSearch'
import { getRoleList } from '@/api/role'
import { getAssignUserList, searchUsers, assignUser, deleteAssignUser } from '@/api/user'
const defaultForm = {
uid: '',
role: ''
}
export default {
components: { UserSearch },
data() {
return {
tableData: [],
drawerVisible: false,
filter: Object.assign({}, defaultForm),
form: Object.assign({}, defaultForm),
rules: {
uid: { required: true, message: '请选择用户', trigger: 'change' },
role: { required: true, message: '请选择角色', trigger: 'change' }
},
dialogType: 'create',
searchUsersloading: false,
userList: [],
roleList: []
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('management/assignment/create')
},
hasSearchUser() {
return this.permissions.includes('management/assignment/search')
},
hasDelete() {
return this.permissions.includes('management/assignment/delete')
}
},
created() {
this.fetchAssignUserList()
this.fetchRoleList()
},
methods: {
handleReset() {
this.filter = Object.assign({}, defaultForm)
this.fetchAssignUserList()
},
handleCreate() {
this.drawerVisible = true
this.dialogType = 'create'
},
handleDelete(row) {
this.$confirm('执行删除将导致此用户无法登录系统, 是否继续执行删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchDeleteAssignUser({ uid: row.userId, role: row.roleName })
}).catch(() => {});
},
handleEdit() {
this.drawerVisible = true
this.dialogType = 'edit'
},
handleSubmit() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.fetchCreateAssignUser()
}
});
},
fetchCreateAssignUser() {
assignUser(this.form).then(res => {
const msg = this.dialogType === 'create' ? '新增用户' : '编辑用户'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.fetchAssignUserList()
this.drawerVisible = false
} else {
this.$message.error(msg + '失败')
}
})
},
fetchDeleteAssignUser(data) {
deleteAssignUser(data).then(res => {
const msg = '删除用户'
if (res.code === 0 && res.message === 'OK') {
this.$message.success(msg + '成功')
this.fetchAssignUserList()
} else {
this.$message.error(msg + '失败')
}
})
},
fetchAssignUserList() {
const params = {
uid: this.filter.uid,
role: this.filter.role
}
getAssignUserList(params).then(res => {
if (res.code === 0 && Array.isArray(res.data)) {
this.tableData = res.data
}
})
},
fetchSearchUsers(val) {
this.searchUsersloading = true
searchUsers({ q: val || '' })
.then(res => {
this.searchUsersloading = false
if (res.data && Array.isArray(res.data.list)) {
this.userList = res.data.list
}
})
},
fetchRoleList() {
getRoleList().then(res => {
if (res.code === 0 && Array.isArray(res.data)) {
this.roleList = res.data
}
})
}
}
}
</script>
<style scoped>
.user{
box-sizing:border-box;
padding: 15px;
}
.btn-bar{
padding-bottom:10px;
display:flex;
}
.filter{
flex:1;
}
.drawer-container{
height:100%;
display:flex;
flex-direction: column;
}
.el-form{
flex:1;
padding:15px 25px 0 0;
}
.drawer-footer{
padding:15px 0;
text-align:center;
}
</style>
\ No newline at end of file
...@@ -10,52 +10,65 @@ export default [ ...@@ -10,52 +10,65 @@ export default [
{ {
path: '/student', path: '/student',
component: Layout, component: Layout,
meta: { onlyShowOneChild: true }, meta: { onlyShowOneChild: true, permission: 'v1/student/list' },
children: [ children: [
{ {
path: '', path: '',
name: 'Student', name: 'Student',
component: () => import('@/pages/student/index'), component: () => import('@/pages/student/index'),
meta: { title: '学员管理', icon: '', permission: 'content-menu-advert-list' } meta: { title: '学员管理', icon: '', permission: 'v1/student/list' }
} }
] ]
}, },
{ {
path: '/course', path: '/course',
component: Layout, component: Layout,
meta: { onlyShowOneChild: true }, meta: { onlyShowOneChild: true, permission: 'v1/course/list' },
children: [ children: [
{ {
path: '', path: '',
name: 'Course', name: 'Course',
component: () => import('@/pages/course/index'), component: () => import('@/pages/course/index'),
meta: { title: '课程管理', icon: '', permission: 'content-menu-advert-list' } meta: { title: '课程管理', icon: '', permission: 'v1/course/list' }
} }
] ]
}, },
{ {
path: '/project', path: '/project',
component: Layout, component: Layout,
meta: { onlyShowOneChild: true }, meta: { onlyShowOneChild: true, permission: 'v1/project/list' },
children: [ children: [
{ {
path: '', path: '',
name: 'Project', name: 'Project',
component: () => import('@/pages/project/index'), component: () => import('@/pages/project/index'),
meta: { title: '项目管理', icon: '', permission: 'content-menu-advert-list' } meta: { title: '项目管理', icon: '', permission: 'v1/project/list' }
} }
] ]
}, },
{ {
path: '/role', path: '/role',
component: Layout, component: Layout,
meta: { onlyShowOneChild: true }, meta: { onlyShowOneChild: true, permission: 'management/role/index' },
children: [ children: [
{ {
path: '', path: '',
name: 'Role', name: 'Role',
component: () => import('@/pages/role/index'), component: () => import('@/pages/role/index'),
meta: { title: '权限管理', icon: '', permission: 'content-menu-advert-list' } meta: { title: '权限管理', icon: '', permission: 'management/role/index' }
}
]
},
{
path: '/user',
component: Layout,
meta: { onlyShowOneChild: true, permission: 'management/assignment/index' },
children: [
{
path: '',
name: 'User',
component: () => import('@/pages/user/index'),
meta: { title: '授权管理', icon: '', permission: 'management/assignment/index' }
} }
] ]
}, },
......
...@@ -3,7 +3,6 @@ const getters = { ...@@ -3,7 +3,6 @@ const getters = {
roles: state => state.user.roles, roles: state => state.user.roles,
sidebar: state => state.app.sidebar, sidebar: state => state.app.sidebar,
permission_routes: state => state.permission.routes, permission_routes: state => state.permission.routes,
projects: state => state.user.projects,
permissions: state => state.user.permissions permissions: state => state.user.permissions
} }
export default getters export default getters
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import { getProjectContentTypeList } from '@/api/contentManage'
const state = { const state = {
sidebar: { sidebar: {
...@@ -18,22 +17,12 @@ const mutations = { ...@@ -18,22 +17,12 @@ const mutations = {
} else { } else {
Cookies.set('sidebarStatus', 0) Cookies.set('sidebarStatus', 0)
} }
},
SET_TYPELIST: (state, list) => {
console.log(list)
state.typeList = list
} }
} }
const actions = { const actions = {
toggleSideBar({ commit }) { toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR') commit('TOGGLE_SIDEBAR')
},
getTypeList({ commit }, type, id) {
getProjectContentTypeList(type, id).then(res => {
const data = res.data
commit('SET_TYPELIST', data)
})
} }
} }
......
import { getUser, logout } from '@/api/account' import { getUser, logout, getUserPermissions } from '@/api/common'
import { getUserRolesPermissions } from '@/api/system'
import router, { resetRouter } from '@/router' import router, { resetRouter } from '@/router'
const user = { const user = {
state: { state: {
...@@ -63,38 +62,22 @@ const user = { ...@@ -63,38 +62,22 @@ const user = {
}, },
// 检测角色权限 // 检测角色权限
async checkRolesPermissions({ commit }) { async checkRolesPermissions({ commit }) {
const obj = await getUserRolesPermissions().then(res => { const obj = await getUserPermissions().then(res => {
if (res.code === 0) { if (res.code === 0) {
const data = res.data const data = res.data
let roles = []
let permissions = [] let permissions = []
let projects = []
if (data.roles && Array.isArray(data.roles)) {
roles = data.roles
}
commit('setRoles', roles)
if (data.permissions && Array.isArray(data.permissions)) { if (data.permissions && Array.isArray(data.permissions)) {
permissions = data.permissions permissions = data.permissions
} }
commit('setPermissions', permissions) commit('setPermissions', permissions)
if (data.projects && Array.isArray(data.projects)) {
projects = data.projects
commit('setProjects', projects)
} else {
commit('setProjects', [])
}
return { return {
roles, permissions
permissions,
projects
} }
} else { } else {
return {} return {}
} }
}).catch(() => { }).catch(() => {
commit('setRoles', null)
commit('setPermissions', null) commit('setPermissions', null)
commit('setProjects', [])
return {} return {}
}) })
return obj return obj
......
...@@ -6,7 +6,7 @@ import { getNonce } from '@/utils/utils' ...@@ -6,7 +6,7 @@ import { getNonce } from '@/utils/utils'
const httpRequest = axios.create({ const httpRequest = axios.create({
timeout: 60000, timeout: 60000,
withCredentials: true, withCredentials: true,
headers: { 'Content-Type': 'application/json', apikey: 'McS1WsnMwMhsfc7cJ7Y0' } headers: { 'Content-Type': 'application/x-www-form-urlencoded', apikey: 'McS1WsnMwMhsfc7cJ7Y0' }
}) })
// 请求拦截 // 请求拦截
...@@ -22,6 +22,7 @@ httpRequest.interceptors.request.use( ...@@ -22,6 +22,7 @@ httpRequest.interceptors.request.use(
// Object.keys(defaultParams).forEach(key => { // Object.keys(defaultParams).forEach(key => {
// params.append(key, defaultParams[key]) // params.append(key, defaultParams[key])
// }) // })
console.log(config)
} else { } else {
params = Object.assign({}, defaultParams, params) params = Object.assign({}, defaultParams, params)
if (['post', 'put', 'delete'].includes(config.method)) { if (['post', 'put', 'delete'].includes(config.method)) {
...@@ -50,7 +51,7 @@ httpRequest.interceptors.response.use( ...@@ -50,7 +51,7 @@ httpRequest.interceptors.response.use(
return Promise.reject(data) return Promise.reject(data)
} }
if (data.code === 403) { if (data.code === 403) {
window.location.href = `${webConf.others.loginUrl}?rd=${encodeURIComponent(window.location.href)}` // window.location.href = `${webConf.others.loginUrl}?rd=${encodeURIComponent(window.location.href)}`
} }
return data return data
}, },
...@@ -59,7 +60,7 @@ httpRequest.interceptors.response.use( ...@@ -59,7 +60,7 @@ httpRequest.interceptors.response.use(
const { status, message, code } = error.response.data const { status, message, code } = error.response.data
// 未登录 // 未登录
if (status === 403) { if (status === 403) {
window.location.href = `${webConf.others.loginUrl}?rd=${encodeURIComponent(window.location.href)}` // window.location.href = `${webConf.others.loginUrl}?rd=${encodeURIComponent(window.location.href)}`
} else if (status === 400 && code === 401) { } else if (status === 400 && code === 401) {
// router.push('/role') // router.push('/role')
} else { } else {
......
...@@ -17,6 +17,7 @@ export default class BeforeEnter { ...@@ -17,6 +17,7 @@ export default class BeforeEnter {
if (to.path.includes('error-page')) { if (to.path.includes('error-page')) {
next() next()
} else { } else {
console.log(to)
if (!permissions.includes(to.meta.permission)) { if (!permissions.includes(to.meta.permission)) {
router.push('/error-page/401') router.push('/error-page/401')
} }
......
...@@ -76,3 +76,32 @@ export function dateFormat(time, cFormat) { ...@@ -76,3 +76,32 @@ export function dateFormat(time, cFormat) {
}) })
return timeStr return timeStr
} }
/**
* 文件下载
* @param {string} fileUrl 文件下载地址
* @param {string} fileName 文件名
* @returns {null}
*/
export function funDownload(fileUrl, fileName) {
// console.log(fileUrl)
const elink = document.createElement('a')// 创建一个a标签
elink.download = fileName;// 设置a标签的下载属性
elink.style.display = 'none';// 将a标签设置为隐藏
elink.href = fileUrl;// 把之前处理好的地址赋给a标签的href
document.body.appendChild(elink);// 将a标签添加到body中
elink.click();// 执行a标签的点击方法
// URL.revokeObjectURL(elink.href) // 下载完成释放URL 对象
document.body.removeChild(elink)// 移除a标签
}
/**
* 分割字符串,取得尾部
* @param {string} str 字符串
* @param {string} split 分割符
* @returns {string}
*/
export function splitStrLast(str, split) {
const fileNameArr = str.split(split)
const last = fileNameArr[fileNameArr.length - 1]
return last
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论