This commit is contained in:
Sun
2023-11-08 21:53:07 +08:00
commit 211c3071dc
245 changed files with 39293 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
import moment from 'moment'
import { h } from 'vue'
import type { NotificationReactive } from 'naive-ui'
import { NButton, createDiscreteApi } from 'naive-ui'
import { useNoticeStore, useUserStore } from '@/store'
import { getInfo as getUserInfo } from '@/api/system/user'
import { getListByDisplayType as getListByDisplayTypeApi } from '@/api/notice'
const noticeStore = useNoticeStore()
const userStore = useUserStore()
const { notification } = createDiscreteApi(['notification'])
/**
* 生成指定时间格式
* @param format 时间格式 默认:'YYYY-MM-DD HH:mm:ss'
* @returns string
*/
export function buildTimeString(format?: string): string {
if (!format)
format = 'YYYY-MM-DD HH:mm:ss'
return moment().format(format)
}
export function timeFormat(timeString?: string) {
return moment(timeString).format('YYYY-MM-DD HH:mm:ss')
}
/**
* 创建新的公告
* @param timeString
*/
export function noticeCreate(info: Notice.NoticeInfo) {
const option: any = {
title: info.title,
content: info.content,
meta: info.createTime ? timeFormat(info.createTime) : '',
}
const btns: any = []
let n: NotificationReactive
// 链接按钮
if (info.url !== '') {
btns.push(
h(
NButton,
{
text: true,
type: 'info',
onClick: () => {
window.open(info.url, '_blank')
n.destroy()
},
},
{
default: () => '打开链接',
},
),
)
}
if (info.oneRead === 1) {
btns.push(
h(
NButton,
{
text: true,
type: 'primary',
style: { marginLeft: '20px' },
onClick: () => {
if (info.id) {
if (info.isLogin === 1 && userStore.userInfo.username) {
noticeStore.setReadByUsername(userStore.userInfo.username, info.id)
console.log('设置用户已读', info.id)
}
else {
noticeStore.setReadByGlobal(info.id)
console.log('设置全局已读', info.id)
}
}
n.destroy()
},
},
{
default: () => '不再提醒',
},
),
)
}
option.action = () => btns
n = notification.create(option)
}
export function setTitle(titile: string) {
document.title = titile
}
export function getTitle(titile: string) {
document.title = titile
}
//
export async function updateLocalUserInfo() {
const { data } = await getUserInfo<User.Info>()
userStore.updateUserInfo({ headImage: data.headImage, name: data.name })
}
export async function getNotice(displayType: number | number[]) {
let param: number[]
if (typeof displayType === 'number')
param = [displayType]
else
param = displayType
const { data } = await getListByDisplayTypeApi<Common.ListResponse<Notice.NoticeInfo[]>>(param)
for (let i = 0; i < data.list.length; i++) {
const element = data.list[i]
if (element.id && !noticeStore.getReadByNoticeId(element.id, userStore.userInfo.username))
noticeCreate(element)
}
}
/**
* @description: 获取随机码
* @param {number} size
* @param {array} seed ["a","b"m"c]
* @return {string}
*/
export function randomCode(size: number, seed?: Array<string>) {
seed = seed || ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'p', 'Q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'2', '3', '4', '5', '6', '7', '8', '9',
]// 数组
const seedlength = seed.length// 数组长度
let createPassword = ''
for (let i = 0; i < size; i++) {
const j = Math.floor(Math.random() * seedlength)
createPassword += seed[j]
}
return createPassword
}
+18
View File
@@ -0,0 +1,18 @@
import CryptoJS from 'crypto-js'
const CryptoSecret = '__CRYPTO_SECRET__'
export function enCrypto(data: any) {
const str = JSON.stringify(data)
return CryptoJS.AES.encrypt(str, CryptoSecret).toString()
}
export function deCrypto(data: string) {
const bytes = CryptoJS.AES.decrypt(data, CryptoSecret)
const str = bytes.toString(CryptoJS.enc.Utf8)
if (str)
return JSON.parse(str)
return null
}
+44
View File
@@ -0,0 +1,44 @@
/**
* 转义 HTML 字符
* @param source
*/
export function encodeHTML(source: string) {
return source
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
/**
* 判断是否为代码块
* @param text
*/
export function includeCode(text: string | null | undefined) {
const regexp = /^(?:\s{4}|\t).+/gm
return !!(text?.includes(' = ') || text?.match(regexp))
}
/**
* 复制文本
* @param options
*/
export function copyText(options: { text: string; origin?: boolean }) {
const props = { origin: true, ...options }
let input: HTMLInputElement | HTMLTextAreaElement
if (props.origin)
input = document.createElement('textarea')
else
input = document.createElement('input')
input.setAttribute('readonly', 'readonly')
input.value = props.text
document.body.appendChild(input)
input.select()
if (document.execCommand('copy'))
document.execCommand('copy')
document.body.removeChild(input)
}
+18
View File
@@ -0,0 +1,18 @@
type CallbackFunc<T extends unknown[]> = (...args: T) => void
export function debounce<T extends unknown[]>(
func: CallbackFunc<T>,
wait: number,
): (...args: T) => void {
let timeoutId: ReturnType<typeof setTimeout> | undefined
return (...args: T) => {
const later = () => {
clearTimeout(timeoutId)
func(...args)
}
clearTimeout(timeoutId)
timeoutId = setTimeout(later, wait)
}
}
+7
View File
@@ -0,0 +1,7 @@
export function getCurrentDate() {
const date = new Date()
const day = date.getDate()
const month = date.getMonth() + 1
const year = date.getFullYear()
return `${year}-${month}-${day}`
}
+55
View File
@@ -0,0 +1,55 @@
export function isNumber<T extends number>(value: T | unknown): value is number {
return Object.prototype.toString.call(value) === '[object Number]'
}
export function isString<T extends string>(value: T | unknown): value is string {
return Object.prototype.toString.call(value) === '[object String]'
}
export function isBoolean<T extends boolean>(value: T | unknown): value is boolean {
return Object.prototype.toString.call(value) === '[object Boolean]'
}
export function isNull<T extends null>(value: T | unknown): value is null {
return Object.prototype.toString.call(value) === '[object Null]'
}
export function isUndefined<T extends undefined>(value: T | unknown): value is undefined {
return Object.prototype.toString.call(value) === '[object Undefined]'
}
export function isObject<T extends object>(value: T | unknown): value is object {
return Object.prototype.toString.call(value) === '[object Object]'
}
export function isArray<T extends any[]>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Array]'
}
export function isFunction<T extends (...args: any[]) => any | void | never>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Function]'
}
export function isDate<T extends Date>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Date]'
}
export function isRegExp<T extends RegExp>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object RegExp]'
}
export function isPromise<T extends Promise<any>>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Promise]'
}
export function isSet<T extends Set<any>>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Set]'
}
export function isMap<T extends Map<any, any>>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Map]'
}
export function isFile<T extends File>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object File]'
}
+32
View File
@@ -0,0 +1,32 @@
import axios, { type AxiosResponse } from 'axios'
import { useAuthStore } from '@/store'
const service = axios.create({
baseURL: import.meta.env.VITE_GLOB_API_URL,
})
service.interceptors.request.use(
(config) => {
const token = useAuthStore().token
if (token)
config.headers.Authorization = `Bearer ${token}`
return config
},
(error) => {
return Promise.reject(error.response)
},
)
service.interceptors.response.use(
(response: AxiosResponse): AxiosResponse => {
if (response.status === 200)
return response
throw new Error(response.status.toString())
},
(error) => {
return Promise.reject(error)
},
)
export default service
+120
View File
@@ -0,0 +1,120 @@
import type { AxiosProgressEvent, AxiosResponse, GenericAbortSignal } from 'axios'
import { createDiscreteApi } from 'naive-ui'
import request from './axios'
import { useAuthStore } from '@/store'
import { router } from '@/router'
const { message } = createDiscreteApi(['message'])
export interface HttpOption {
url: string
data?: any
method?: string
headers?: any
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void
signal?: GenericAbortSignal
beforeRequest?: () => void
afterRequest?: () => void
}
export interface Response<T = any> {
data: T
// message: string | null
// status: string
msg: string
code: number
}
function http<T = any>(
{ url, data, method, headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
) {
const authStore = useAuthStore()
const successHandler = (res: AxiosResponse<Response<T>>) => {
if (res.data.code === 0 || typeof res.data === 'string')
return res.data
if (res.data.code === 1001) {
message.warning('登录过期,请重新登录')
router.push({ path: '/login' })
authStore.removeToken()
return res.data
}
if (res.data.code === 1000) {
router.push({ path: '/login' })
authStore.removeToken()
return res.data
}
if (res.data.code === 1005) {
message.warning(res.data.msg)
return res.data
}
if (res.data.code === -1) {
message.warning(res.data.msg)
// router.push({ path: '/login' })
// authStore.removeToken()
return res.data
}
// 验证码相关错误
if (res.data.code > 1100 && res.data.code < 1200)
return res.data
return Promise.reject(res.data)
}
const failHandler = (error: Response<Error>) => {
afterRequest?.()
// message.error('网络错误,请稍后重试', {
// duration: 50000,
// closable: true,
// })
throw new Error(error?.msg || 'Error')
}
beforeRequest?.()
method = method || 'GET'
const params = Object.assign(typeof data === 'function' ? data() : data ?? {}, {})
if (!headers)
headers = {}
headers.token = authStore.token
return method === 'GET'
? request.get(url, { params, signal, onDownloadProgress }).then(successHandler, failHandler)
: request.post(url, params, { headers, signal, onDownloadProgress }).then(successHandler, failHandler)
}
export function get<T = any>(
{ url, data, method = 'GET', onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
return http<T>({
url,
method,
data,
onDownloadProgress,
signal,
beforeRequest,
afterRequest,
})
}
export function post<T = any>(
{ url, data, method = 'POST', headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
return http<T>({
url,
method,
data,
headers,
onDownloadProgress,
signal,
beforeRequest,
afterRequest,
})
}
export default post
+1
View File
@@ -0,0 +1 @@
export * from './local'
+70
View File
@@ -0,0 +1,70 @@
import { deCrypto, enCrypto } from '../crypto'
interface StorageData<T = any> {
data: T
expire: number | null
}
export function createLocalStorage(options?: { expire?: number | null; crypto?: boolean }) {
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
const { expire, crypto } = Object.assign(
{
expire: DEFAULT_CACHE_TIME,
crypto: true,
},
options,
)
function set<T = any>(key: string, data: T) {
const storageData: StorageData<T> = {
data,
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
}
const json = crypto ? enCrypto(storageData) : JSON.stringify(storageData)
window.localStorage.setItem(key, json)
}
function get(key: string) {
const json = window.localStorage.getItem(key)
if (json) {
let storageData: StorageData | null = null
try {
storageData = crypto ? deCrypto(json) : JSON.parse(json)
}
catch {
// Prevent failure
}
if (storageData) {
const { data, expire } = storageData
if (expire === null || expire >= Date.now())
return data
}
remove(key)
return null
}
}
function remove(key: string) {
window.localStorage.removeItem(key)
}
function clear() {
window.localStorage.clear()
}
return {
set,
get,
remove,
clear,
}
}
export const ls = createLocalStorage()
export const ss = createLocalStorage({ expire: null, crypto: false })