From e15e35dd8a2940d86b923c5849414690f165262e Mon Sep 17 00:00:00 2001 From: 龚焕茏 <2842157468@qq.com> Date: 星期四, 05 九月 2024 16:36:30 +0800 Subject: [PATCH] 学员导出 --- src/utils/request.js | 203 +++++++++++++++------- src/views/home/index.vue | 2 src/utils/cache.js | 77 ++++++++ src/main.js | 2 src/utils/ruoyi.js | 233 +++++++++++++++++++++++++ package.json | 3 src/utils/errorCode.js | 6 src/views/student/index.vue | 8 8 files changed, 463 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index 8ce7d9a..46130e3 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "cos-js-sdk-v5": "^1.8.3", "echarts": "^5.5.1", "element-ui": "2.13.2", - "graphql": "^16.9.0", + "file-saver": "^2.0.5", + "graphql": "^15.0.0", "graphql-tag": "^2.12.6", "js-cookie": "2.2.0", "normalize.css": "7.0.0", diff --git a/src/main.js b/src/main.js index 09153ea..1e5df94 100644 --- a/src/main.js +++ b/src/main.js @@ -11,10 +11,12 @@ import App from "./App"; import store from "./store"; import router from "./router"; +import { download } from '@/utils/request' import "@/icons"; // icon import "@/permission"; // permission control +Vue.prototype.download = download // const cors = require('cors'); // // 澶勭悊璺ㄥ煙璇锋眰 // App.use(cors()); diff --git a/src/utils/cache.js b/src/utils/cache.js new file mode 100644 index 0000000..6b5c00b --- /dev/null +++ b/src/utils/cache.js @@ -0,0 +1,77 @@ +const sessionCache = { + set (key, value) { + if (!sessionStorage) { + return + } + if (key != null && value != null) { + sessionStorage.setItem(key, value) + } + }, + get (key) { + if (!sessionStorage) { + return null + } + if (key == null) { + return null + } + return sessionStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + }, + remove (key) { + sessionStorage.removeItem(key); + } +} +const localCache = { + set (key, value) { + if (!localStorage) { + return + } + if (key != null && value != null) { + localStorage.setItem(key, value) + } + }, + get (key) { + if (!localStorage) { + return null + } + if (key == null) { + return null + } + return localStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + }, + remove (key) { + localStorage.removeItem(key); + } +} + +export default { + /** + * 浼氳瘽绾х紦瀛� + */ + session: sessionCache, + /** + * 鏈湴缂撳瓨 + */ + local: localCache +} diff --git a/src/utils/errorCode.js b/src/utils/errorCode.js new file mode 100644 index 0000000..d2111ee --- /dev/null +++ b/src/utils/errorCode.js @@ -0,0 +1,6 @@ +export default { + '401': '璁よ瘉澶辫触锛屾棤娉曡闂郴缁熻祫婧�', + '403': '褰撳墠鎿嶄綔娌℃湁鏉冮檺', + '404': '璁块棶璧勬簮涓嶅瓨鍦�', + 'default': '绯荤粺鏈煡閿欒锛岃鍙嶉缁欑鐞嗗憳' +} diff --git a/src/utils/request.js b/src/utils/request.js index ee21946..521876f 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -1,84 +1,153 @@ import axios from 'axios' -import { MessageBox, Message } from 'element-ui' +import { Notification, MessageBox, Message, Loading } from 'element-ui' import store from '@/store' import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { tansParams, blobValidate } from "@/utils/ruoyi"; +import cache from '@/utils/cache' +import { saveAs } from 'file-saver' -// create an axios instance +let downloadLoadingInstance; +// 鏄惁鏄剧ず閲嶆柊鐧诲綍 +export let isRelogin = { show: false }; + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' +// 鍒涘缓axios瀹炰緥 const service = axios.create({ - baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url - // withCredentials: true, // send cookies when cross-domain requests - timeout: 5000 // request timeout + // axios涓姹傞厤缃湁baseURL閫夐」锛岃〃绀鸿姹俇RL鍏叡閮ㄥ垎 + baseURL: process.env.VUE_APP_BASE_API, + // 瓒呮椂 + //榛樿10绉� 淇敼涓�20绉� + timeout: 20000 }) -// request interceptor -service.interceptors.request.use( - config => { - // do something before request is sent - - if (store.getters.token) { - // let each request carry token - // ['X-Token'] is a custom headers key - // please modify it according to the actual situation - config.headers['X-Token'] = getToken() - } - return config - }, - error => { - // do something with request error - console.log(error) // for debug - return Promise.reject(error) +// request鎷︽埅鍣� +service.interceptors.request.use(config => { + // 鏄惁闇�瑕佽缃� token + const isToken = (config.headers || {}).isToken === false + // 鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜� + const isRepeatSubmit = (config.headers || {}).repeatSubmit === false + if (getToken() && !isToken) { + config.headers['Authorization'] = 'Bearer ' + getToken() // 璁╂瘡涓姹傛惡甯﹁嚜瀹氫箟token 璇锋牴鎹疄闄呮儏鍐佃嚜琛屼慨鏀� } -) - -// response interceptor -service.interceptors.response.use( - /** - * If you want to get http information such as headers or status - * Please return response => response - */ - - /** - * Determine the request status by custom code - * Here is just an example - * You can also judge the status by HTTP Status Code - */ - response => { - const res = response - // if the custom code is not 20000, it is judged as an error. - if (res.status !== 200) { - Message({ - message: res.message || 'Error', - type: 'error', - duration: 5 * 1000 - }) - - // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; - if (res.status === 50008 || res.status === 50012 || res.status === 50014) { - // to re-login - MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { - confirmButtonText: 'Re-Login', - cancelButtonText: 'Cancel', - type: 'warning' - }).then(() => { - store.dispatch('user/resetToken').then(() => { - location.reload() - }) - }) - } - return Promise.reject(new Error(res.message || 'Error')) + // get璇锋眰鏄犲皠params鍙傛暟 + if (config.method === 'get' && config.params) { + let url = config.url + '?' + tansParams(config.params); + url = url.slice(0, -1); + config.params = {}; + config.url = url; + } + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + } + const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 璇锋眰鏁版嵁澶у皬 + const limitSize = 5 * 1024 * 1024; // 闄愬埗瀛樻斁鏁版嵁5M + if (requestSize >= limitSize) { + console.warn(`[${config.url}]: ` + '璇锋眰鏁版嵁澶у皬瓒呭嚭鍏佽鐨�5M闄愬埗锛屾棤娉曡繘琛岄槻閲嶅鎻愪氦楠岃瘉銆�') + return config; + } + const sessionObj = cache.session.getJSON('sessionObj') + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj) } else { - return res + const s_url = sessionObj.url; // 璇锋眰鍦板潃 + const s_data = sessionObj.data; // 璇锋眰鏁版嵁 + const s_time = sessionObj.time; // 璇锋眰鏃堕棿 + const interval = 1000; // 闂撮殧鏃堕棿(ms)锛屽皬浜庢鏃堕棿瑙嗕负閲嶅鎻愪氦 + if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { + const message = '鏁版嵁姝e湪澶勭悊锛岃鍕块噸澶嶆彁浜�'; + console.warn(`[${s_url}]: ` + message) + return Promise.reject(new Error(message)) + } else { + cache.session.setJSON('sessionObj', requestObj) + } + } + } + return config +}, error => { + console.log(error) + Promise.reject(error) +}) + +// 鍝嶅簲鎷︽埅鍣� +service.interceptors.response.use(res => { + // 鏈缃姸鎬佺爜鍒欓粯璁ゆ垚鍔熺姸鎬� + const code = res.data.code || 200; + // 鑾峰彇閿欒淇℃伅 + const msg = errorCode[code] || res.data.msg || errorCode['default'] + // 浜岃繘鍒舵暟鎹垯鐩存帴杩斿洖 + if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { + return res.data + } + if (code === 401) { + if (!isRelogin.show) { + isRelogin.show = true; + MessageBox.confirm('鐧诲綍鐘舵�佸凡杩囨湡锛屾偍鍙互缁х画鐣欏湪璇ラ〉闈紝鎴栬�呴噸鏂扮櫥褰�', '绯荤粺鎻愮ず', { confirmButtonText: '閲嶆柊鐧诲綍', cancelButtonText: '鍙栨秷', type: 'warning' }).then(() => { + isRelogin.show = false; + store.dispatch('LogOut').then(() => { + location.href = '/index'; + }) + }).catch(() => { + isRelogin.show = false; + }); + } + return Promise.reject('鏃犳晥鐨勪細璇濓紝鎴栬�呬細璇濆凡杩囨湡锛岃閲嶆柊鐧诲綍銆�') + } else if (code === 500) { + Message({ message: msg, type: 'error' }) + return Promise.reject(new Error(msg)) + } else if (code === 601) { + Message({ message: msg, type: 'warning' }) + return Promise.reject('error') + } else if (code !== 200) { + Notification.error({ title: msg }) + return Promise.reject('error') + } else { + return res.data } }, error => { - console.log('err' + error) // for debug - Message({ - message: error.message, - type: 'error', - duration: 5 * 1000 - }) + console.log('err' + error) + let { message } = error; + if (message == "Network Error") { + message = "鍚庣鎺ュ彛杩炴帴寮傚父"; + } else if (message.includes("timeout")) { + message = "绯荤粺鎺ュ彛璇锋眰瓒呮椂"; + } else if (message.includes("Request failed with status code")) { + message = "绯荤粺鎺ュ彛" + message.substr(message.length - 3) + "寮傚父"; + } + Message({ message: message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) +// 閫氱敤涓嬭浇鏂规硶 +export function download(url, params, filename, config) { + downloadLoadingInstance = Loading.service({ text: "姝e湪涓嬭浇鏁版嵁锛岃绋嶅��", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) + return service.post(url, params, { + transformRequest: [(params) => { return tansParams(params) }], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob', + ...config + }).then(async (data) => { + const isBlob = blobValidate(data); + if (isBlob) { + const blob = new Blob([data]) + saveAs(blob, filename) + } else { + const resText = await data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + Message.error(errMsg); + } + downloadLoadingInstance.close(); + }).catch((r) => { + console.error(r) + Message.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒') + downloadLoadingInstance.close(); + }) +} + export default service diff --git a/src/utils/ruoyi.js b/src/utils/ruoyi.js new file mode 100644 index 0000000..44bf9c4 --- /dev/null +++ b/src/utils/ruoyi.js @@ -0,0 +1,233 @@ + + +/** + * 閫氱敤js鏂规硶灏佽澶勭悊 + * Copyright (c) 2019 ruoyi + */ + +// 鏃ユ湡鏍煎紡鍖� +export function parseTime(time, pattern) { + if (arguments.length === 0 || !time) { + return null + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } else if (typeof time === 'string') { + time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), ''); + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['鏃�', '涓�', '浜�', '涓�', '鍥�', '浜�', '鍏�'][value] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +// 琛ㄥ崟閲嶇疆 +export function resetForm(refName) { + if (this.$refs[refName]) { + this.$refs[refName].resetFields(); + } +} + +// 娣诲姞鏃ユ湡鑼冨洿 +export function addDateRange(params, dateRange, propName) { + let search = params; + search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}; + dateRange = Array.isArray(dateRange) ? dateRange : []; + if (typeof (propName) === 'undefined') { + search.params['beginTime'] = dateRange[0]; + search.params['endTime'] = dateRange[1]; + } else { + search.params['begin' + propName] = dateRange[0]; + search.params['end' + propName] = dateRange[1]; + } + return search; +} + +// 鍥炴樉鏁版嵁瀛楀吀 +export function selectDictLabel(datas, value) { + if (value === undefined) { + return ""; + } + var actions = []; + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + value)) { + actions.push(datas[key].label); + return true; + } + }) + if (actions.length === 0) { + actions.push(value); + } + return actions.join(''); +} + +// 鍥炴樉鏁版嵁瀛楀吀锛堝瓧绗︿覆銆佹暟缁勶級 +export function selectDictLabels(datas, value, separator) { + if (value === undefined || value.length ===0) { + return ""; + } + if (Array.isArray(value)) { + value = value.join(","); + } + var actions = []; + var currentSeparator = undefined === separator ? "," : separator; + var temp = value.split(currentSeparator); + Object.keys(value.split(currentSeparator)).some((val) => { + var match = false; + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + temp[val])) { + actions.push(datas[key].label + currentSeparator); + match = true; + } + }) + if (!match) { + actions.push(temp[val] + currentSeparator); + } + }) + return actions.join('').substring(0, actions.join('').length - 1); +} + +// 瀛楃涓叉牸寮忓寲(%s ) +export function sprintf(str) { + var args = arguments, flag = true, i = 1; + str = str.replace(/%s/g, function () { + var arg = args[i++]; + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; +} + +// 杞崲瀛楃涓诧紝undefined,null绛夎浆鍖栦负"" +export function parseStrEmpty(str) { + if (!str || str == "undefined" || str == "null") { + return ""; + } + return str; +} + +// 鏁版嵁鍚堝苟 +export function mergeRecursive(source, target) { + for (var p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]); + } else { + source[p] = target[p]; + } + } catch (e) { + source[p] = target[p]; + } + } + return source; +}; + +/** + * 鏋勯�犳爲鍨嬬粨鏋勬暟鎹� + * @param {*} data 鏁版嵁婧� + * @param {*} id id瀛楁 榛樿 'id' + * @param {*} parentId 鐖惰妭鐐瑰瓧娈� 榛樿 'parentId' + * @param {*} children 瀛╁瓙鑺傜偣瀛楁 榛樿 'children' + */ +export function handleTree(data, id, parentId, children) { + let config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + }; + + var childrenListMap = {}; + var nodeIds = {}; + var tree = []; + + for (let d of data) { + let parentId = d[config.parentId]; + if (childrenListMap[parentId] == null) { + childrenListMap[parentId] = []; + } + nodeIds[d[config.id]] = d; + childrenListMap[parentId].push(d); + } + + for (let d of data) { + let parentId = d[config.parentId]; + if (nodeIds[parentId] == null) { + tree.push(d); + } + } + + for (let t of tree) { + adaptToChildrenList(t); + } + + function adaptToChildrenList(o) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]]; + } + if (o[config.childrenList]) { + for (let c of o[config.childrenList]) { + adaptToChildrenList(c); + } + } + } + return tree; +} + +/** +* 鍙傛暟澶勭悊 +* @param {*} params 鍙傛暟 +*/ +export function tansParams(params) { + let result = '' + for (const propName of Object.keys(params)) { + const value = params[propName]; + var part = encodeURIComponent(propName) + "="; + if (value !== null && value !== "" && typeof (value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { + let params = propName + '[' + key + ']'; + var subPart = encodeURIComponent(params) + "="; + result += subPart + encodeURIComponent(value[key]) + "&"; + } + } + } else { + result += part + encodeURIComponent(value) + "&"; + } + } + } + return result +} + +// 楠岃瘉鏄惁涓篵lob鏍煎紡 +export function blobValidate(data) { + return data.type !== 'application/json' +} diff --git a/src/views/home/index.vue b/src/views/home/index.vue index c9254e9..4268322 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -48,7 +48,7 @@ methods: { getUserDate() { login({ - code: "0913JF000eW6BS1rZ3100FQTUM03JF05", + code: "021dbA000NZ9LS1gKd400jYDAX1dbA0L", }).then((res) => { localStorage.setItem("user", JSON.stringify(res.data)); }); diff --git a/src/views/student/index.vue b/src/views/student/index.vue index 3a28c1b..c9f5af7 100644 --- a/src/views/student/index.vue +++ b/src/views/student/index.vue @@ -8,7 +8,7 @@ </el-tabs> <div style="display: flex; justify-content: space-between"> <!-- <el-button type="primary" size="small" @click="showCreate">鏂板缓瀛﹀憳</el-button> --> - <el-button type="primary" size="small">瀵煎嚭瀛﹀憳</el-button> + <el-button type="primary" size="small" @click="handleExport">瀵煎嚭瀛﹀憳</el-button> <div style="width: 300px"> <el-input placeholder="鎸夊鍚嶆悳绱�" @@ -112,7 +112,7 @@ </template> <script> -import { getData, getRenew, getExpire, getDeleted } from "@/api/student"; +import { getData, getRenew, getExpire, getDeleted, handleExport } from "@/api/student"; import StudentCreate from "./components/create.vue"; export default { @@ -154,6 +154,10 @@ this.fetchData(); }, methods: { + handleExport() { + this.download('exportPlayer', { + }, `瀵煎嚭_${new Date().getTime()}.xlsx`) + }, fetchData() { this.listLoading = true; getData(this.data).then((response) => { -- Gitblit v1.8.0