提交 9c1e3c3f authored 作者: pengxiaohui's avatar pengxiaohui

update

上级 d60adcac
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_SHARE_URL=https://marketing.ezijing.com
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LOGIN_URL=https://login2.ezijing.com/auth/login/index
VITE_SHARE_URL=https://marketing2.ezijing.com
\ No newline at end of file
VITE_LOGIN_URL=https://login2.ezijing.com/auth/login/index
VITE_SHARE_URL=https://marketing2.ezijing.com
\ No newline at end of file
......@@ -11,13 +11,15 @@
},
"dependencies": {
"axios": "^0.21.1",
"blueimp-md5": "^2.18.0",
"clipboard": "^2.0.8",
"element-ui": "^2.15.5",
"qrcode.vue": "^1.7.0",
"query-string": "^7.0.1",
"vue": "^2.6.14",
"vue-router": "^3.5.2",
"vuex": "^3.6.2"
"vuex": "^3.6.2",
"xlsx": "^0.17.0"
},
"devDependencies": {
"ali-oss": "^6.16.0",
......
......@@ -2,3 +2,33 @@
margin: 0;
padding: 0;
}
/* element-ui input,textarea font-family reset */
.el-input__inner, .el-textarea__inner{
font-family: 'PingFang SC', 'PingFangSC-Regular', 'Source Han Sans CN', -apple-system, 'Microsoft YaHei', 'Helvetica', 'Arial', Verdana,
'Hiragino Sans GB', 'Wenquanyi Micro Hei', sans-serif;
}
/* element-ui drawer reset */
.el-drawer__header{
padding:14px 16px;
margin:0;
border-bottom:1px solid #DCDFE6;
}
.el-drawer__header>h5{
font-size:16px;
color:#666;
font-weight:600;
}
.el-drawer__body{
height:calc(100% - 82px);
overflow-y: auto;
}
/* element-ui dialog reset */
.el-dialog__header{
padding:20px 16px 10px;
}
.el-dialog__body{
padding:10px 20px;
}
.el-dialog__footer{
text-align: center;
}
......@@ -32,8 +32,8 @@
</el-form-item>
</template>
<el-form-item class="filter-buttons">
<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 type="primary" size="small" icon="el-icon-search" @click="search">搜索</el-button>
<el-button size="small" icon="el-icon-refresh-left" @click="reset">重置</el-button>
</el-form-item>
</el-form>
</div>
......@@ -140,24 +140,24 @@ export default {
let params = this.params
// 翻页参数设置
if (this.hasPagination) {
params.page = (this.page.currentPage - 1).toString()
params.page_size = this.page.size.toString()
params.page = this.page.currentPage.toString()
params.limit = this.page.size.toString()
}
// 接口请求之前
if (beforeRequest) {
params = beforeRequest(params, isReset)
}
// for (const key in params) {
// if (params[key] === '' || params[key] === undefined || params[key] === undefined) {
// delete params[key]
// }
// }
for (const key in params) {
if (params[key] === '' || params[key] === undefined || params[key] === null) {
delete params[key]
}
}
this.loading = true
httpRequest(params)
.then(res => {
const { data = [], page_info: pageInfo = {} } = res || {}
this.page.total = parseInt(pageInfo.total_number || '')
this.dataList = callback ? callback(data) : data
const { data = {} } = res || {}
this.page.total = parseInt(data.total || 0)
this.dataList = callback ? callback(data.data) : data.data
})
.catch(() => {
this.page.total = 0
......
......@@ -24,16 +24,22 @@ export default {
data() {
return {
menuList: [
{
name: '概览',
path: '/dashboard',
icon: 'el-icon-s-home'
},
// {
// name: '概览',
// path: '/dashboard',
// icon: 'el-icon-s-home'
// },
{
name: '营销工具',
path: '/tools/sign-in',
icon: 'el-icon-s-grid',
children: [{ name: '打卡签到', path: '/tools/sign-in' }]
},
{
name: '系统管理',
path: '/system/user',
icon: 'el-icon-s-tools',
children: [{ name: '用户管理', path: '/system/user' }]
}
]
}
......
......@@ -5,9 +5,6 @@ import store from './store'
import modules from './modules'
import beforeEnter from './utils/beforeEnter'
// 公共css
import '@/assets/css/base.css'
// 注册模块
modules({ router, store })
......@@ -16,6 +13,8 @@ import '@/assets/theme/index.css'
import ElementUI from 'element-ui'
Vue.use(ElementUI)
// 公共css
import '@/assets/css/base.css'
// 路由钩子函数
router.beforeEach(beforeEnter)
......
<template>
<div>dashboard</div>
<div>
概览
<!-- <button @click="handleClick">显示弹框</button> -->
<app-popup :visible.sync="popupVisible">
<a href="http://www.baidu.com">链接</a>
</app-popup>
</div>
</template>
<script>
export default {}
import AppPopup from './components/AppPopup.vue'
export default {
components: { AppPopup },
data() {
return {
popupVisible: false
}
},
methods: {
handleClick() {
this.popupVisible = true
}
}
}
</script>
<style>
......
<template>
<div class="popup" v-show="visible">
<div class="popup-container" :class="signStatus === 1 ? 'success':'warn'">
<p v-if="signStatus === 1" class="success">签到成功!</p>
<p v-if="signStatus === 2" class="warn">
您已迟到。<span>请于课后补看错过视频。</span>
</p>
<div class="content">
<slot></slot>
</div>
<i class="el-icon-circle-close" @click="handleClose"></i>
</div>
<div class="overlay"></div>
</div>
</template>
<script>
export default {
props: {
visible: {
type: Boolean,
default: false
},
signStatus: {
type: Number,
default: 1
}
},
data() {
return {}
},
methods: {
handleClose() {
this.$emit('update:visible', false)
}
}
}
</script>
<style scoped>
.overlay{
position:fixed;
left:0;
top:0;
right:0;
bottom:0;
z-index:1999;
background:rgba(0, 0, 0, .5);
}
.popup-container{
position:fixed;
left:50%;
top:50%;
transform:translate(-50%, -50%);
z-index:2000;
width:313px;
height:211px;
padding-top:150px;
border-radius:5px;
box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
background-image: url('https://webapp-pub.ezijing.com/upload/marketing-admin/popup_bg1.png');
background-size:313px;
}
.popup-container.warn{
background-image: url('https://webapp-pub.ezijing.com/upload/marketing-admin/popup_bg2.png');
}
.popup-container p{
color:#1B6EBB;
font-size:28px;
text-align:center;
margin:0;
}
.popup-container p.warn{
color:#D51E2A;
}
.popup-container p.warn span{
color:#959595;
font-size:18px;
display:block;
}
.popup-container>i{
position:absolute;
left:50%;
bottom:-60px;
transform:translateX(-50%);
padding:5px;
cursor:pointer;
font-size:30px;
color:#fff;
}
.popup-container>i:hover{
color: #e3e3e3;
}
.content{
text-align: center;
padding-top:40px;
}
.content button{
width:180px;
font-size:16px;
background:#1B6EBB;
border-color:#1B6EBB;
}
.popup-container.warn .content button{
background:#D51E2A;
border-color:#D51E2A;
}
</style>
import user from './user'
import routes from './route'
export default function (options) {
// 路由
if (options.router && routes.length) {
routes.forEach(route => {
options.router.addRoute(route)
})
}
user(options)
}
\ No newline at end of file
import AppLayout from '@/components/layout/Index.vue'
const routes = [
{
name: 'system',
path: '/system',
component: AppLayout
}
]
export default routes
\ No newline at end of file
import httpRequest from '@/utils/axios'
/**
* 搜索统一用户
*/
export function search(params) {
return httpRequest.get('/api/marketing/admin/v1/system/search-user', { params })
}
/**
* 获取统一用户
*/
export function getUserList(params) {
return httpRequest.get('/api/marketing/admin/v1/users', { params })
}
/**
* 授权用户
*/
export function addUser(data) {
return httpRequest.post('/api/marketing/admin/v1/user', data)
}
/**
* 批量删除授权用户
*/
export function batchDeleteUser(data) {
return httpRequest.post('/api/marketing/admin/v1/user/batch-delete', data)
}
import routes from './route'
export default function (options = {}) {
// 路由
if (options.router && routes.length) {
routes.forEach(route => {
options.router.addRoute('system', route)
})
}
}
export default [
{
path: 'user',
component: () => import('./views/List.vue')
}
]
<template>
<app-card>
<app-list v-bind="tableOptions" ref="appList" @selection-change="handleSelectionChange">
<!-- 筛选 -->
<template v-slot:filter-user="{ params }">
<user-search v-model="params.sso_id" :options="{ size: 'small' }"/>
</template>
<template #header-aside>
<el-button type="primary" size="small" style="margin-top:5px;" @click="handleCreate">添加用户</el-button>
</template>
<template #footer>
<div class="selection_bar">
已选中 {{multipleSelection.length}}
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove">删除</el-button>
</div>
</template>
</app-list>
<el-dialog title="添加用户" :visible.sync="dialogVisible" width="480px" :destroy-on-close="true" :close-on-click-modal="false" @close="handleDialogClose">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="60px">
<el-form-item label="用户" prop="sso_id" style="position:relative;">
<user-search v-model="form.sso_id" :options="{ size: 'small' }"/>
</el-form-item>
</el-form>
<div style="text-align:center;">
<el-button size="mini" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" size="mini" @click="handleSubmit">提交</el-button>
</div>
</el-dialog>
</app-card>
</template>
<script>
// 引入组件
import AppList from '@/components/base/AppList.vue'
import AppCard from '@/components/base/AppCard.vue'
import UserSearch from './components/UserSearch.vue'
// api
import { getUserList, addUser, batchDeleteUser } from '../api'
export default {
components: { AppCard, AppList, UserSearch },
data() {
return {
multipleSelection: [],
dialogVisible: false,
form: {
sso_id: ''
},
rules: {
sso_id: { required: true, message: '请选择用户', trigger: 'change' }
}
}
},
computed: {
tableOptions() {
return {
remote: {
httpRequest: getUserList,
params: { sso_id: '' }
},
filters: [
{ prop: 'sso_id', slots: 'filter-user' }
],
columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left' },
{ prop: 'id', label: '用户ID', minWidth: '160px' },
{ prop: 'sso_user.realname', label: '用户姓名', minWidth: '120px' },
{ prop: 'sso_user.nickname', label: '用户昵称', minWidth: '120px' },
{ prop: 'sso_user.email', label: '邮箱地址', minWidth: '160px' }
]
}
}
},
methods: {
handleSelectionChange(val) {
this.multipleSelection = val
},
handleCreate() {
this.dialogVisible = true
},
handleDialogClose() {
this.dialogVisible = false
this.form.sso_id = ''
},
handleSubmit() {
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.fetchAddUser()
} else {
return false
}
this.handleDialogClose()
})
},
handleRemove() {
this.$confirm('执行操作则选中的用户将无法登录,确定删除??', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchBatchDeleteUsers()
}).catch(() => {})
},
fetchAddUser() {
const params = {
sso_id: this.form.sso_id
}
addUser(params).then(res => {
if (res.code === 0) {
this.$message.success('添加用户成功')
this.$refs.appList.refetch(true)
} else {
this.$message.error('添加用户失败')
}
})
},
fetchBatchDeleteUsers() {
const params = {
sso_ids: this.multipleSelection.map(item => item.sso_id)
}
batchDeleteUser(params).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('删除用户成功')
this.$refs.appList.refetch(true)
} else {
this.$message.error('删除用户失败')
}
})
}
}
}
</script>
<style>
.selection_bar{
font-size:14px;
color:#626266;
}
</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 { search } from '../../api'
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{4,8}$/
const EMAIL_REG = /^[A-Za-z0-9]+([_.\\-][A-Za-z0-9]+)*@[A-Za-z0-9-.]+$/
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) {
let searchType = 'username'
if (EMAIL_REG.test(val)) {
searchType = 'email'
} else if (MOBILE_REG.test(val)) {
searchType = 'mobile'
}
if (!val) return false
else {
const params = {
[searchType]: val
}
this.searchUsersloading = true
search(params)
.then(res => {
this.searchUsersloading = false
if (res.data && Array.isArray(res.data.items)) {
this.userList = res.data.items
}
})
.catch(() => {})
}
}
}
}
</script>
<style scoped>
.el-select{
width:100%;
}
</style>
\ No newline at end of file
......@@ -3,6 +3,72 @@ import httpRequest from '@/utils/axios'
/**
* 获取商品列表
*/
export function getGoodsList(data) {
return httpRequest.post('/api/shop/commodity/spu/search', data).then({})
export function getActivityList(params) {
return httpRequest.get('/api/marketing/admin/v1/activities', { params })
}
/**
* 创建活动
*/
export function createActivity(data) {
return httpRequest.post('/api/marketing/admin/v1/activity', data)
}
/**
* 获取商品列表
*/
export function getActivityDetails(id) {
return httpRequest.get(`/api/marketing/admin/v1/${id}/activity`)
}
/**
* 更新活动
*/
export function updateActivity(id, data) {
return httpRequest.put(`/api/marketing/admin/v1/${id}/activity`, data)
}
/**
* 批量删除活动
*/
export function batchDeleteActivity(data) {
return httpRequest.post('/api/marketing/admin/v1/activity/batch-delete', data)
}
/**
* 导入学员
*/
export function importStudents(id, formData) {
return httpRequest({
url: `/api/marketing/admin/v1/activity/${id}/student/batch-import`,
method: 'post',
// headers: { 'Content-Type': 'multipart/form-data' },
timeout: 900000,
data: formData,
withCredentials: false
})
}
/**
* 导出学员列表
*/
export function exportStudents(id, data) {
return httpRequest({
url: `/api/marketing/admin/v1/activity/${id}/student/batch-export`,
method: 'post',
data,
responseType: 'blob'
})
}
/**
* 获取学员列表
*/
export function getStudentList(params) {
return httpRequest.get(`/api/marketing/admin/v1/activity/${params.id}/students`, { params })
}
/**
* 批量删除学员
*/
export function batchDeleteStudents(data) {
return httpRequest.post('/api/marketing/admin/v1/activity/student/batch-delete', data)
}
/**
* 批量标记签到/取消签到
*/
export function batchSignin(data) {
return httpRequest.post('/api/marketing/admin/v1/activity/students/sign-in', data)
}
\ No newline at end of file
......@@ -2,5 +2,9 @@ export default [
{
path: 'sign-in',
component: () => import('./views/List.vue')
},
{
path: 'preview',
component: () => import('./views/Preview.vue')
}
]
<template>
<div class="preview">
<div class="mobile">
<div class="content">
<!-- <img v-if="details.market_background_img" :src="details.market_background_img"> -->
<h5>签到成功</h5>
<p>此处显示签到备注文字</p>
</div>
</div>
<div class="bottom-bar">
<el-button size="small" @click="goBack">上一步</el-button>
<el-button size="small">完成</el-button>
</div>
</div>
</template>
<script>
import { getActivityDetails } from '../api'
export default {
data() {
return {
details: {}
}
},
computed: {
id() {
const { id = '' } = this.$route.query
return id
}
},
created() {
this.fetchActivityDetails()
},
methods: {
fetchActivityDetails() {
getActivityDetails(this.id).then(res => {
if (res.code === 0) {
this.details = res.data
}
})
},
goBack() {
this.$router.go(-1)
}
}
}
</script>
<style scoped>
.preview{
height:800px;
padding:30px 0;
background:#fff;
border-radius:6px;
}
.mobile{
width:320px;
height:600px;
background:#eee;
margin:0 auto;
border-radius:6px;
padding-top:100px;
}
.content{
margin:0 20px;
background:#fff;
height:300px;
border-radius:4px;
}
.content h5{
font-size:20px;
line-height:80px;
text-align:center;
}
.content p{
font-size:16px;
line-height:40px;
text-align:center;
margin-top:40px;
}
.bottom-bar{
margin-top:40px;
padding-top:20px;
text-align:center;
border-top:1px solid #e3e3e3;
}
</style>
\ No newline at end of file
<template>
<div class="person-list">
<app-list v-bind="tableOptions" ref="appList" @selection-change="handleSelectionChange">
<template #header-aside>
<el-button type="primary" size="small" style="margin-top:5px;" @click="handleImport">导入</el-button>
</template>
<!-- 筛选 -->
<template v-slot:filter-type="{ params }">
<el-select v-model="params.type" placeholder="请选择类型" size="small">
<el-option label="姓名" value="username"></el-option>
<el-option label="手机号码" value="mobile"></el-option>
<el-option label="邮箱" value="email"></el-option>
</el-select>
</template>
<template #footer>
<div style="font-size:14px;">
已选中 {{multipleSelection.length}}
<el-button style="margin:0 15px;" size="mini" :disabled="!multipleSelection.length" @click="handleRemove">删除</el-button>
<!-- <el-button size="mini" :disabled="!multipleSelection.length" @click="exportSelected">导出</el-button> -->
<el-dropdown size="small" @command="handleCommand">
<el-button type="primary" size="mini">
导出<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-item command="selected" :disabled="!multipleSelection.length">导出选中项</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="fetchBatchSignin(1)">标记为已签到</el-button>
<el-button style="margin-left:15px;" size="mini" :disabled="!multipleSelection.length" @click="fetchBatchSignin(0)">取消签到</el-button>
</div>
</template>
</app-list>
<el-dialog title="导入学员" :visible.sync="dialogVisible" width="480px" append-to-body :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"
:http-request="fetchFileUpload"
accept=".xls,.xlsx"
>
<el-button slot="trigger" size="mini" type="primary">选取文件</el-button>
<span slot="tip" style="margin-left:10px;">只能上传excel文件</span>
</el-upload>
<div style="margin-bottom:10px;">
导入模板下载:<a href="https://webapp-pub.ezijing.com/upload/marketing-admin/student_import.xlsx" download="课程模板"><el-button type="text" >student_import.xlsx</el-button></a>
</div>
<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>
</template>
<script>
import AppList from '@/components/base/AppList.vue'
import { getStudentList, batchDeleteStudents, batchSignin, exportStudents, importStudents } from '../../api'
import { splitStrLast, funDownload } from '@/utils/util'
import XLSX from 'xlsx'
export default {
props: {
id: {
type: String,
default: ''
}
},
components: { AppList },
data() {
return {
multipleSelection: [],
dialogVisible: false,
fileList: [],
importDisabled: false
}
},
computed: {
tableOptions() {
return {
remote: {
httpRequest: getStudentList,
beforeRequest: this.beforeRequest,
params: { id: this.id, type: 'username', key: '' }
},
filters: [
{ prop: 'type', slots: 'filter-type' },
{ type: 'input', placeholder: '请输入', prop: 'key', size: 'small' }
],
columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left' },
{ prop: 'username', label: '姓名', minWidth: '80px', fixed: 'left' },
{ prop: 'phone', label: '手机号码', minWidth: '100px' },
{ prop: 'email', label: '邮箱地址', minWidth: '130px' },
{ prop: 'company_name', label: '公司名称', minWidth: '140px' },
{ prop: 'position', label: '职务', minWidth: '80px' },
{ prop: 'age', label: '年龄', minWidth: '50px' },
{ prop: 'remark', label: '备注', minWidth: '160px', 'show-overflow-tooltip': true },
{
prop: 'sign_in_status',
label: '签到状态',
minWidth: '80px',
computed({ row }) {
return row.sign_in_status === 0 ? '未签到' : '已签到'
}
},
{ prop: 'sign_in_time', label: '签到时间', minWidth: '130px' }
]
}
}
},
methods: {
beforeRequest(params) {
const _params = Object.assign({}, params)
if (_params.key) {
_params[_params.type] = _params.key
}
delete _params.type
delete _params.key
return _params
},
handleSelectionChange(val) {
this.multipleSelection = val
},
handleCommand(type) {
if (type === 'all') this.fetchExportStudentList()
if (type === 'selected') this.exportSelected()
},
handleRemove() {
this.$confirm('执行操作将删除选中的学员,确定删除??', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchBatchDeleteStudents()
}).catch(() => {})
},
handleImport() {
this.dialogVisible = true
},
handleDialogClose() {
this.fileList = []
},
beforeUpload(file) {
const suffix = splitStrLast(file.name, '.')
if (!['xlsx', 'xls'].includes(suffix)) {
this.$message.error('只能上传excel文件')
return false
} else {
return true
}
},
submitUpload() {
if (!this.importDisabled) {
this.$refs.upload.submit()
}
},
fetchFileUpload(data) {
const formData = new window.FormData()
formData.append('file', data.file)
importStudents(this.id, formData).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('导入数据成功')
this.$refs.appList.refetch(true)
window.setTimeout(() => {
this.dialogVisible = false
}, 300)
} else {
this.$message.error(res.message || '导入数据失败,请重选选取文件上传')
}
})
},
fetchBatchDeleteStudents() {
const params = {
ids: this.multipleSelection.map(item => item.id)
}
batchDeleteStudents(params).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('删除学员成功')
this.$refs.appList.refetch(true)
} else {
this.$message.error('删除学员失败')
}
})
},
fetchBatchSignin(status) {
const params = {
status,
ids: this.multipleSelection.map(item => item.id)
}
batchSignin(params).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('更改签到状态成功')
this.$refs.appList.refetch(true)
} else {
this.$message.error('更改签到状态失败')
}
})
},
exportSelected() {
const list = this.tableOptions.columns.filter(item => {
return item.prop && item.prop !== 'head_img'
})
const headList = list.map(item => item.label)
const propList = list.map(item => item.prop)
const excelList = []
excelList.push(headList)
this.multipleSelection.forEach(item => {
const rowValArr = []
propList.forEach(key => {
let val = item[key]
if (key === 'sign_in_status') val = val === 1 ? '已签到' : '未签到'
rowValArr.push(val)
})
excelList.push(rowValArr)
})
const ws = XLSX.utils.aoa_to_sheet(excelList)
ws['!cols'] = [
{ wpx: 120 },
{ wpx: 120 },
{ wpx: 160 },
{ wpx: 180 },
{ wpx: 120 },
{ wpx: 80 },
{ wpx: 200 },
{ wpx: 120 },
{ wpx: 120 }
]
const wb = XLSX.utils.book_new()
wb.SheetNames.push('Worksheet')
wb.Sheets.Worksheet = ws
const wopts = { bookType: 'xlsx', bookSST: false, type: 'array' }
const wbout = XLSX.write(wb, wopts)
const url = URL.createObjectURL(new window.Blob([wbout], { type: 'application/octet-stream' }))
funDownload(url, `学员列表_${Date.now()}.xlsx`)
},
fetchExportStudentList() {
const params = {}
if (this.tableOptions.remote.params.key) {
params[this.tableOptions.remote.params.type] = this.tableOptions.remote.params.key
}
exportStudents(this.id, params).then(res => {
if (res && res.type === 'text/xlsx') {
const url = URL.createObjectURL(res)
funDownload(url, `学员列表_${Date.now()}.xlsx`)
}
})
}
}
}
</script>
<style scoped>
.person-list{
padding:15px 20px;
}
</style>
\ No newline at end of file
/**
* 文件下载
* @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
}
\ No newline at end of file
......@@ -14,7 +14,7 @@ export default defineConfig({
cert: fs.readFileSync(path.join(__dirname, './certs/dev.ezijing.com.pem'))
},
proxy: {
'/api': 'https://shop-admin.ezijing.com'
'/api': 'https://marketing-admin2.ezijing.com'
}
},
resolve: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论