mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-24 07:49:47 +08:00
refactor(projects): 精简版+动态路由权限初步
This commit is contained in:
@ -1,9 +0,0 @@
|
||||
import useAntv from './useAntv';
|
||||
import useAntvTool from './useAntvTool';
|
||||
import useCountDown from './useCountDown';
|
||||
import useSmsCode from './useSmsCode';
|
||||
import useImageVerify from './useImageVerify';
|
||||
import useAgreement from './useAgreement';
|
||||
import useVirtualList from './useVirtualList';
|
||||
|
||||
export { useAntv, useAntvTool, useCountDown, useSmsCode, useImageVerify, useAgreement, useVirtualList };
|
@ -1,20 +0,0 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
/** 使用勾选协议 */
|
||||
export default function useAgreement(text = '请勾选 "我已经仔细阅读并接受《用户协议》《隐私权政策》"') {
|
||||
const agreement = ref(true);
|
||||
|
||||
function isAgree() {
|
||||
let agree = true;
|
||||
if (!agreement.value) {
|
||||
agree = false;
|
||||
window.$message?.error(text);
|
||||
}
|
||||
return agree;
|
||||
}
|
||||
|
||||
return {
|
||||
agreement,
|
||||
isAgree
|
||||
};
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import type { ComputedRef } from 'vue';
|
||||
import type { Plot } from '@antv/g2plot';
|
||||
import { useBoolean } from '@/hooks';
|
||||
|
||||
interface AntvFn<T, O> {
|
||||
new (dom: HTMLElement, options: O): T;
|
||||
}
|
||||
|
||||
export default function useAntv<GraphOption, GraphType extends Plot<GraphOption>>(
|
||||
GraphFn: AntvFn<GraphType, GraphOption>,
|
||||
graphOptions: ComputedRef<GraphOption>
|
||||
) {
|
||||
/** 图表dom容器 */
|
||||
const domRef = ref<HTMLElement>();
|
||||
|
||||
/** 图表实例 */
|
||||
const graph = ref<GraphType>();
|
||||
|
||||
/** 是否可以开始渲染图表 */
|
||||
const { bool: canRender, setTrue: setCanRender } = useBoolean();
|
||||
|
||||
/** 是否是在onMouted第一次渲染图表 */
|
||||
const { bool: isFirstRender, setTrue: setIsFirstRender, setFalse: setIsNotFirstRender } = useBoolean();
|
||||
|
||||
/** 渲染图表 */
|
||||
function renderGraph(options: GraphOption) {
|
||||
if (!domRef.value) return;
|
||||
if (!graph.value) {
|
||||
graph.value = new GraphFn(domRef.value, options);
|
||||
graph.value.render();
|
||||
} else {
|
||||
graph.value.update(options);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setCanRender();
|
||||
setIsFirstRender();
|
||||
renderGraph(graphOptions.value);
|
||||
setIsNotFirstRender();
|
||||
});
|
||||
|
||||
watch(graphOptions, newValue => {
|
||||
if (!canRender.value || isFirstRender.value) return;
|
||||
renderGraph(newValue);
|
||||
});
|
||||
|
||||
return {
|
||||
domRef,
|
||||
graph
|
||||
};
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
export default function useAntvTool() {
|
||||
/**
|
||||
* antv滑动调属性
|
||||
*/
|
||||
function getSlider(columns: number, length: number, sliderColor: string) {
|
||||
return {
|
||||
start: 1 - columns / length,
|
||||
end: 1,
|
||||
foregroundStyle: { fill: sliderColor }
|
||||
};
|
||||
}
|
||||
|
||||
function getFormatter(unit: string) {
|
||||
const EMPTY = ' ';
|
||||
function formatter(v: number | null) {
|
||||
return v === null ? EMPTY : v + unit;
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
|
||||
function formatLabelWithUnit(value: number | null, unit: string) {
|
||||
const EMPTY = ' ';
|
||||
return value === null ? EMPTY : value + unit;
|
||||
}
|
||||
|
||||
return {
|
||||
getSlider,
|
||||
getFormatter,
|
||||
formatLabelWithUnit
|
||||
};
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import { useBoolean } from '../common';
|
||||
|
||||
/**
|
||||
* 倒计时
|
||||
* @param second - 倒计时的时间(s)
|
||||
*/
|
||||
export default function useCountDown(second: number) {
|
||||
if (second <= 0 && second % 1 !== 0) {
|
||||
throw Error('倒计时的时间应该为一个正整数!');
|
||||
}
|
||||
const { bool: isComplete, setTrue, setFalse } = useBoolean(false);
|
||||
|
||||
const counts = ref(0);
|
||||
const isCounting = computed(() => Boolean(counts.value));
|
||||
|
||||
let intervalId: any;
|
||||
|
||||
/**
|
||||
* 开始计时
|
||||
* @param updateSecond - 更改初时传入的倒计时时间
|
||||
*/
|
||||
function start(updateSecond: number = second) {
|
||||
if (!counts.value) {
|
||||
setFalse();
|
||||
counts.value = updateSecond;
|
||||
intervalId = setInterval(() => {
|
||||
counts.value -= 1;
|
||||
if (counts.value <= 0) {
|
||||
clearInterval(intervalId);
|
||||
setTrue();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止计时
|
||||
*/
|
||||
function stop() {
|
||||
intervalId = clearInterval(intervalId);
|
||||
counts.value = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
counts,
|
||||
isCounting,
|
||||
start,
|
||||
stop,
|
||||
isComplete
|
||||
};
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
/**
|
||||
* 绘制图形验证码
|
||||
* @param width - 图形宽度
|
||||
* @param height - 图形高度
|
||||
*/
|
||||
export default function useImageVerify(width = 152, height = 40) {
|
||||
const domRef = ref<HTMLCanvasElement>();
|
||||
const imgCode = ref('');
|
||||
|
||||
function setImgCode(code: string) {
|
||||
imgCode.value = code;
|
||||
}
|
||||
|
||||
function getImgCode() {
|
||||
if (!domRef.value) return;
|
||||
imgCode.value = draw(domRef.value, width, height);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getImgCode();
|
||||
});
|
||||
|
||||
return {
|
||||
domRef,
|
||||
imgCode,
|
||||
setImgCode,
|
||||
getImgCode
|
||||
};
|
||||
}
|
||||
|
||||
function randomNum(min: number, max: number) {
|
||||
const num = Math.floor(Math.random() * (max - min) + min);
|
||||
return num;
|
||||
}
|
||||
|
||||
function randomColor(min: number, max: number) {
|
||||
const r = randomNum(min, max);
|
||||
const g = randomNum(min, max);
|
||||
const b = randomNum(min, max);
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
|
||||
function draw(dom: HTMLCanvasElement, width: number, height: number) {
|
||||
let imgCode = '';
|
||||
|
||||
const NUMBER_STRING = '0123456789';
|
||||
|
||||
const ctx = dom.getContext('2d');
|
||||
if (!ctx) return imgCode;
|
||||
|
||||
ctx.fillStyle = randomColor(180, 230);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)];
|
||||
imgCode += text;
|
||||
const fontSize = randomNum(18, 41);
|
||||
const deg = randomNum(-30, 30);
|
||||
ctx.font = `${fontSize}px Simhei`;
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillStyle = randomColor(80, 150);
|
||||
ctx.save();
|
||||
ctx.translate(30 * i + 23, 15);
|
||||
ctx.rotate((deg * Math.PI) / 180);
|
||||
ctx.fillText(text, -15 + 5, -15);
|
||||
ctx.restore();
|
||||
}
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(randomNum(0, width), randomNum(0, height));
|
||||
ctx.lineTo(randomNum(0, width), randomNum(0, height));
|
||||
ctx.strokeStyle = randomColor(180, 230);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
for (let i = 0; i < 41; i += 1) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = randomColor(150, 200);
|
||||
ctx.fill();
|
||||
}
|
||||
return imgCode;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import { computed } from 'vue';
|
||||
import { REGEXP_PHONE } from '@/config';
|
||||
import useCountDown from './useCountDown';
|
||||
|
||||
export default function useSmsCode() {
|
||||
const { counts, start, isCounting } = useCountDown(60);
|
||||
const initLabel = '获取验证码';
|
||||
const countingLabel = (second: number) => `${second}秒后重新获取`;
|
||||
const label = computed(() => (isCounting.value ? countingLabel(counts.value) : initLabel));
|
||||
|
||||
/** 判断手机号码格式是否正确 */
|
||||
function isPhoneValid(phone: string) {
|
||||
let valid = true;
|
||||
if (phone.trim() === '') {
|
||||
window.$message?.error('手机号码不能为空!');
|
||||
valid = false;
|
||||
} else if (!REGEXP_PHONE.test(phone)) {
|
||||
window.$message?.error('手机号码格式错误!');
|
||||
valid = false;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取短信验证码
|
||||
* @param phone - 手机号
|
||||
*/
|
||||
async function getSmsCode(phone: string) {
|
||||
const valid = isPhoneValid(phone);
|
||||
if (!valid) return;
|
||||
// 该处调用验证码接口
|
||||
window.$message?.success('验证码发送成功!');
|
||||
start();
|
||||
}
|
||||
|
||||
return {
|
||||
label,
|
||||
start,
|
||||
isCounting,
|
||||
getSmsCode
|
||||
};
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
import { ref, watch, computed, nextTick } from 'vue';
|
||||
import type { Ref, ComputedRef } from 'vue';
|
||||
|
||||
interface VirtualListConfig {
|
||||
/** 容器的高度 */
|
||||
containerHeight: number;
|
||||
/** 渲染的个数 */
|
||||
renderNums: number;
|
||||
/** 触发的高度距离 */
|
||||
triggerHeight: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 虚拟列表
|
||||
* @param list - 列表数据源
|
||||
* @param config - 虚拟列表配置
|
||||
*/
|
||||
export default function useVirtualList<T extends { [key: string]: any }[]>(
|
||||
list: Ref<T>,
|
||||
config: VirtualListConfig = {
|
||||
containerHeight: 200,
|
||||
renderNums: 10,
|
||||
triggerHeight: 24
|
||||
}
|
||||
) {
|
||||
const { containerHeight, renderNums, triggerHeight } = config;
|
||||
|
||||
const renderIndex = ref(1);
|
||||
function setRenderIndex(index: number) {
|
||||
renderIndex.value = index;
|
||||
}
|
||||
function resetRenderIndex() {
|
||||
setRenderIndex(1);
|
||||
}
|
||||
|
||||
const dataSource = computed(() => {
|
||||
const endIndex = renderIndex.value * renderNums;
|
||||
return list.value.slice(0, endIndex);
|
||||
}) as ComputedRef<T>;
|
||||
|
||||
function handleScroll(e: Event) {
|
||||
const target = e.target as HTMLElement;
|
||||
const needRender = target.scrollHeight - (target.scrollTop + containerHeight) < triggerHeight;
|
||||
if (needRender) {
|
||||
nextTick(() => {
|
||||
setRenderIndex(renderIndex.value + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(list, () => {
|
||||
resetRenderIndex();
|
||||
});
|
||||
|
||||
return {
|
||||
containerHeight,
|
||||
dataSource,
|
||||
handleScroll
|
||||
};
|
||||
}
|
@ -2,5 +2,6 @@ import useContext from './useContext';
|
||||
import useBoolean from './useBoolean';
|
||||
import useLoading from './useLoading';
|
||||
import useLoadingEmpty from './useLoadingEmpty';
|
||||
import useReload from './useReload';
|
||||
|
||||
export { useContext, useBoolean, useLoading, useLoadingEmpty };
|
||||
export { useContext, useBoolean, useLoading, useLoadingEmpty, useReload };
|
||||
|
29
src/hooks/common/useReload.ts
Normal file
29
src/hooks/common/useReload.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { nextTick } from 'vue';
|
||||
import useBoolean from './useBoolean';
|
||||
|
||||
/** 重载 */
|
||||
export default function useReload() {
|
||||
// 重载的标志
|
||||
const { bool: reloadFlag, setTrue, setFalse } = useBoolean(true);
|
||||
|
||||
/**
|
||||
* 触发重载
|
||||
* @param duration - 延迟时间(ms)
|
||||
*/
|
||||
async function handleReload(duration = 0) {
|
||||
setFalse();
|
||||
await nextTick();
|
||||
if (duration) {
|
||||
setTimeout(() => {
|
||||
setTrue();
|
||||
}, duration);
|
||||
} else {
|
||||
setTrue();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
reloadFlag,
|
||||
handleReload
|
||||
};
|
||||
}
|
@ -1,2 +1 @@
|
||||
export * from './common';
|
||||
export * from './business';
|
||||
|
Reference in New Issue
Block a user