This commit is contained in:
2026-01-29 10:43:43 +08:00
commit 4a76df3391
426 changed files with 74975 additions and 0 deletions

200
utils/common.ts Normal file
View File

@@ -0,0 +1,200 @@
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import {FILE_SERVER} from "~/config";
const route = useRoute();
/**
* 判断是否为整数
* @param num
*/
export const isInteger = (num: any) => {
return /^-?\d+$/.test(num);
}
/**
* 判断是否是移动设备
*/
export function isMobileDevice(): boolean {
return (typeof window.orientation !== "undefined") || /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
/**
* 获取当前语言版本
*/
export const getLang = () => {
const i18n = useI18n();
if(i18n.locale.value == 'zh'){
return 'zh_CN';
}
return i18n.locale.value;
}
/**
* 从spm提取参数
* param spm?=0.1.2.3.4.5.6.7
* @param index
*/
export const getIdBySpm = (index: number) => {
if(route.query.spm){
const split = String(route.query.spm).split('.')
return split[index];
}
return undefined;
}
/**
* 获取当前网址的Path部分
* 示例 http://websoft.top/article/123.html
* getPath(1) 将返回 article
* @param index
*/
export const getPath = (index?: number) => {
const path = route.path;
const i18n = useI18n();
if(i18n.locale.value == 'en' && index){
index = index + 1;
}
if(index){
const split = path.split('/');
return split[index];
}
return path;
}
/**
* 获取当前网址的ID部分
* 示例 /article/123.html
* @return 123
*/
export const paramsId = () => {
let index;
index = route.params.id;
if(index){
try {
index = index.toString().split('.')[0];
return Number(index);
} catch (e) {
return Number(index);
}
}
return 0;
}
export const getNavIdByParamsId = (text: any) => {
if(typeof text === 'number'){
return Number(text);
}
const index = text.toString().split('.')[0];
if(typeof index){
return Number(index);
}
return 0;
}
// 打开新窗口
export function openUrl(url: string) {
window.open(url, '_blank')
}
/**
* 当前网址
*/
export function locationUrl ():string {
return `${window.location}`;
}
/**
* 导航跳转(推荐使用)
* @param d 栏目数据
* @param path 手动指定path
* @param spm 是否附加spm参数
* @return https://websoft.top/article/123.html?spm=0.1.2.3.4.5.6.7
*/
export function navTo(d?: any, path?: string,spm?: boolean){
if(!path){
path = d?.path;
}
// 是否移动设备
// if(isMobileDevice()){
// path = '/m' + path;
// }
// 国际化配置
const i18n = useI18n();
if(i18n.locale.value){
if(i18n.locale.value == 'en'){
path = '/en' + path;
}
}
// 首页
if(d?.model == 'index'){
return path;
}
// if(d?.model == 'links'){
// return openUrl(d?.path)
// }
// 是否附加spm参数
if(spm){
let uid = localStorage.getItem('UserId') || 0;
let timestamp = ref(Date.now() / 1000);
return `${path}?spm=${d?.tenantId||0}.0.${d?.parentId}.${d?.navigationId||0}.${uid}.${timestamp.value}`
}
return `${path}`;
}
/**
* 详情页跳转
* @param d
*/
export function detail(d?: any) {
// console.log(d.detail,'9999999')
return navTo(d,`/${d?.detail}/${d?.articleId}.html`);
}
/**
* 详情页跳转(手机版专用)
* @param d
*/
export function mDetail(d?: any) {
return navTo(d,`/${d?.detail}/${d?.articleId}.html`);
}
export function getViews(form: CmsArticle): number{
return Number(form?.actualViews) + Number(form?.virtualViews)
}
// 单点登录控制台
export function loginAdminByToken(): void {
const user = useUser();
const token = localStorage.getItem('token');
const tid = user.value?.tenantId;
openUrl(`https://${tid}.websoft.top/token-login?token=${token}`)
}
export function getTimeStamp(): number {
return new Date().getTime();
}
// 复制文本
export const copyText = (text: string) => {
// 模拟 输入框
const cInput = document.createElement('input');
cInput.value = text;
document.body.appendChild(cInput);
cInput.select(); // 选取文本框内容
// 执行浏览器复制命令
// 复制命令会将当前选中的内容复制到剪切板中这里就是创建的input标签
// Input要在正常的编辑状态下原生复制方法才会生效
ElMessage.success(`复制成功`);
document.execCommand('copy');
// 复制成功后再将构造的标签 移除
document.body.removeChild(cInput);
};
export const getImage = (text: any) => {
// 是否包含字符串
if(text.indexOf('/upfile') > -1){
return FILE_SERVER + text;
}
return `${text}`
}

116
utils/domain.ts Normal file
View File

@@ -0,0 +1,116 @@
// 解析域名结构
export function getHost(): any {
const host = window.location.host;
return host.split('.');
}
// 是否https
export function isHttps() {
const protocol = window.location.protocol;
if (protocol == 'https:') {
return true;
}
return false;
}
/**
* 获取原始域名
* @return http://www.domain.com
*/
export function getOriginDomain(): string {
return window.origin;
}
/**
* 从域名的第一部分解析租户ID
* 获取tenantId
* @return 10140
*/
export function getDomainPart1(): any {
const split = getHost();
if (split[0] == '127') {
return undefined;
}
if (typeof (split[0])) {
if(isInteger(split[0])){
return split[0];
}
const arr = split[0].split('-');
const tid = arr[arr.length - 1];
if(isInteger(tid)){
return tid;
}
}
return undefined;
}
/**
* 通过解析泛域名获取租户ID
* https://10140.wsdns.cn
* @return 10140
*/
export function getTenantId() {
let tenantId = localStorage.getItem('TenantId');
if(getDomainPart1()){
tenantId = getDomainPart1();
return tenantId;
}
return tenantId;
}
/**
* 获取根域名
* hostname
*/
export function getHostname(): string {
return window.location.hostname;
}
/**
* 获取域名
* @return https://www.domain.com
*/
export function getDomain(): string {
return window.location.protocol + '//www.' + getRootDomain();
}
/**
* 获取根域名
* abc.com
*/
export function getRootDomain(): string {
const split = getHost();
return split[split.length - 2] + '.' + split[split.length - 1];
}
/**
* 获取二级域名
* @return abc.com
*/
export function getSubDomainPath(): string {
const split = getHost();
if (split.length == 2) {
return '';
}
return split[split.length - 3];
}
/**
* 获取产品标识
* @return 10048
*/
export function getProductCode(): string | null {
const subDomain = getSubDomainPath();
if (subDomain == undefined) {
return null;
}
const split = subDomain.split('-');
return split[0];
}
/**
* 控制台域名
*/
export function navSubDomain(path: string): string {
return `${window.location.protocol}//${path}.${getRootDomain()}`;
}

47
utils/editor.ts Normal file
View File

@@ -0,0 +1,47 @@
import { ref } from 'vue';
import TinymceEditor from '@/components/TinymceEditor/index.vue';
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null);
// 编辑器配置信息
export function editorConfig(height?: 500): void {
const resultData = ref<any>({
height: height,
// 自定义文件上传(这里使用把选择的文件转成 blob 演示)
file_picker_callback: (callback: any, _value: any, meta: any) => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
// 设定文件可选类型
if (meta.filetype === 'image') {
input.setAttribute('accept', 'image/*');
} else if (meta.filetype === 'media') {
input.setAttribute('accept', 'video/*');
}
input.onchange = () => {
const file = input.files?.[0];
if (!file) {
return;
}
if (meta.filetype === 'media') {
if (!file.type.startsWith('video/')) {
editorRef.value?.alert({ content: '只能选择视频文件' });
return;
}
}
if (file.size / 1024 / 1024 > 20) {
editorRef.value?.alert({ content: '大小不能超过 20MB' });
return;
}
const reader = new FileReader();
reader.onload = (e) => {
if (e.target?.result != null) {
const blob = new Blob([e.target.result], { type: file.type });
callback(URL.createObjectURL(blob));
}
};
reader.readAsArrayBuffer(file);
};
input.click();
}
});
return resultData;
}

68
utils/loadScript.ts Normal file
View File

@@ -0,0 +1,68 @@
const callbacks: any = {};
/**
* 加载一个远程脚本
* @param {String} src 一个远程脚本
* @param {Function} callback 回调
*/
function loadScript(src: string, callback: { (): void; (): void }) {
const existingScript = document.getElementById(src);
const cb = callback || (() => {});
if (!existingScript) {
callbacks[src] = [];
const $script: HTMLScriptElement = document.createElement('script');
$script.src = src;
$script.id = src;
$script.async = Boolean(1);
document.body.appendChild($script);
const onEnd = 'onload' in $script ? stdOnEnd.bind($script) : ieOnEnd.bind($script);
// @ts-ignore
onEnd($script);
}
callbacks[src].push(cb);
function stdOnEnd(script: { onload?: () => void; onerror: () => void }) {
script.onload = () => {
// @ts-ignore
this.onerror = this.onload = null;
callbacks[src].forEach(
(item: (arg0: null, arg1: { onload?: (() => void) | undefined; onerror: () => void }) => void) => {
item(null, script);
}
);
delete callbacks[src];
};
script.onerror = () => {
// @ts-ignore
this.onerror = this.onload = null;
// @ts-ignore
cb(new Error(`Failed to load ${src}`), script);
};
}
function ieOnEnd(script: { onreadystatechange: () => void }) {
script.onreadystatechange = () => {
// @ts-ignore
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return;
// @ts-ignore
this.onreadystatechange = null;
callbacks[src].forEach((item: (arg0: null, arg1: { onreadystatechange: () => void }) => void) => {
item(null, script);
});
delete callbacks[src];
};
}
}
/**
* 顺序加载一组远程脚本
* @param {Array} list 一组远程脚本
* @param {Function} cb 回调
*/
export function loadScriptQueue(list: any[], cb: { (): void; (): void }) {
const first = list.shift();
list.length ? loadScript(first, () => loadScriptQueue(list, cb)) : loadScript(first, cb);
}
export default loadScript;

114
utils/request.ts Normal file
View File

@@ -0,0 +1,114 @@
import type { UseFetchOptions } from '#app';
import {useToken} from "~/composables/configState";
export const request = <T>(url:string, options?: UseFetchOptions<T, unknown>) => {
const nuxtApp = useNuxtApp()
const config = useRuntimeConfig()
let TenantId = localStorage.getItem('TenantId') || `${config.public.tenantId}`;
const body = options?.body;
// @ts-ignore
const tenantIdByOptions = body?.tenantId;
if(tenantIdByOptions){
// 自定义传TenantId
TenantId = tenantIdByOptions;
}
return useFetch<T>(url,{
baseURL: config.public.apiBaseUrl,
onRequest({ options }: any){
let token = ''
if(import.meta.client){
token = `${useToken().value}`
}
options.headers = {
TenantId,
'Authorization': `${token}`,
...options.headers
}
},
onResponse({ response }: any){
// 200xx 以内错误
if(response.status >= 200 && response.status < 300){
if(response._data.code !== 0){
console.log('>>>>200xx 以内错误')
if(import.meta.client){
ElMessage.error(response._data.message)
nuxtApp.runWithContext(() => {
// navigateTo({
// path: '/404',
// query: {
// code: response.status,
// message: response._data.message
// }
// })
})
}
}
}
},
onResponseError({ response }: any){
// 200xx 以外错误
if(import.meta.client){
console.log('200xx 以外错误',response)
ElMessage.error(isArray(response._data.data.message) ? response._data.data.message[0] : response._data.data.message);
}
},
...options
} as any)
}
export const get = <T>(url: string,options?: UseFetchOptions<T, unknown>) => {
return <T>new Promise((resolve,reject) => {
request(url,{
method: 'get',
...options
}).then(res => {
resolve(res.data.value)
}).catch(err => {
reject(err)
})
})
}
export const post = <T>(url:string,data?: any) => {
return <T>new Promise((resolve,reject) => {
request(url,{
method: 'post',
body: data
}).then(res => {
resolve(res.data.value)
}).catch(err => {
reject(err)
})
})
}
export const put = <T>(url:string,data?: any) => {
return <T>new Promise((resolve,reject) => {
request(url,{
method: 'put',
body: data
}).then(res => {
resolve(res.data.value)
}).catch(err => {
reject(err)
})
})
}
export const del = <T>(url:string,data?: any) => {
return <T>new Promise((resolve,reject) => {
request(url,{
method: 'delete',
...data
}).then(res => {
resolve(res.data.value)
}).catch(err => {
reject(err)
})
})
}
export default {
request,
get,
post,
put,
del
}

54
utils/state.ts Normal file
View File

@@ -0,0 +1,54 @@
import {useCart, useCompany, useLayout, useMenu, useSubMenu} from "~/composables/configState";
/**需要进行持久化的数据:把需要持久化的数据放在下面这个对象中,才会持久化,不需要持久化的数据就不用放到这里了。 */
const enduring: { [key: string]: () => Ref<any> } = {
useToken, useConfigInfo, useWebsite, useUser, useLayout, useSubMenu, useMenu, useCompany, useCart
}
//下面的俩函数在app.vue的onMounted中统一调用或者在其它情况挂载后单独调用。
/**把所有指定数据保存到本地存储
* @param key 要保存的数据名。不填的话就是保存全部(一般不填,统一在页面关闭时保存。如果是特别重要的数据,就时不时单独保存一下即可。)
*/
export const setLocal = (key?: string) => {
if (key) {
console.log('只保存', key);
const useKey = 'use' + key.slice(0, 1).toUpperCase() + key.slice(1).toLowerCase(); //首字母大写,其它全部转小写
const func = enduring[useKey];
if (!func) {
console.log('没有找到', useKey, '对应的函数');
return;
}
localStorage.setItem(key, JSON.stringify(func().value));
} else {
console.log('正在保存全部数据');
for (const key in enduring) {
if (Object.prototype.hasOwnProperty.call(enduring, key)) {
const element = enduring[key];
const setKey = key.toLowerCase().substring(3); //去掉前面的use ,其它全部转小写
try {
localStorage.setItem(setKey, JSON.stringify(element().value));
} catch (error) {
console.log(`在设置${setKey}的数据时出现错误`, error);
}
}
}
}
};
/**从本地存储获取数据到state中 */
export const getLoacl = () => {
for (const key in enduring) {
if (Object.prototype.hasOwnProperty.call(enduring, key)) {
const element = enduring[key];
const setKey = key.toLowerCase().substring(3); //去掉前面的use ,其它全部转小写
try {
const localData = localStorage.getItem(setKey) || '';
if (localData) {
element().value = JSON.parse(localData);
console.log(setKey, '的本地存储数据获取成功', element().value);
}
} catch (error) {
console.log(`在获取${setKey}的数据时出现错误`, error);
}
}
}
};

25
utils/tool.ts Normal file
View File

@@ -0,0 +1,25 @@
export function escapeHtml(str: string) {
let temp = '';
if (str.length === 0) return '';
temp = str.replace(/&amp;/g, '&');
temp = temp.replace(/&lt;/g, '<');
temp = temp.replace(/&gt;/g, '>');
temp = temp.replace(/&nbsp;/g, ' ');
temp = temp.replace(/&#39;/g, "'");
temp = temp.replace(/&quot;/g, '"');
return temp;
}
export function isArray(str: unknown) {
return Object.prototype.toString.call(str) === '[object Array]';
}
// 配置服务器接口
export function getBaseUrl() {
console.log('process:',process.server)
if (process.server) {
return "https://modules.gxwebsoft.com/api"
} else {
return "https://modules.gxwebsoft.com/api"
}
}

29
utils/use-form-data.ts Normal file
View File

@@ -0,0 +1,29 @@
import { reactive } from 'vue';
/**
* 表单数据 hook
* @param initValue 默认值
*/
export default function <T extends object>(initValue?: T) {
const form = reactive<T>({ ...initValue } as T);
const resetFields = () => {
Object.keys(form).forEach((key) => {
form[key] = initValue ? initValue[key] : void 0;
});
};
const assignFields = (data: object) => {
Object.keys(form).forEach((key) => {
form[key] = data[key];
});
};
return {
form,
// 重置为初始值
resetFields,
// 赋值不改变字段
assignFields
};
}