新版本官网优化完成

This commit is contained in:
2025-02-12 16:37:07 +08:00
parent 43a2e17a80
commit 3efdbfc662
547 changed files with 23001 additions and 28169 deletions

View File

@@ -1,60 +1,5 @@
import { v4 as uuidv4 } from 'uuid';
import type {Company} from "~/api/system/company/model";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
const route = useRoute();
/**需要进行持久化的数据:把需要持久化的数据放在下面这个对象中,才会持久化,不需要持久化的数据就不用放到这里了。 */
const enduring: { [key: string]: () => Ref<any> } = {
useToken, useConfigInfo
}
//下面的俩函数在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);
}
}
}
};
/**
* 判断是否为整数
* @param num
@@ -64,154 +9,163 @@ export const isInteger = (num: any) => {
}
/**
* 提取传参中的ID
* param 12334.html
* return 1234
* @param num
* 判断是否是移动设备
*/
export const getIdByParam = () => {
const split = String(route.params.id).split('.')
return split[0];
export function isMobileDevice(): boolean {
return (typeof window.orientation !== "undefined") || /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
/**
* 提取传参中的ID
* param 12334.html
* return 1234
* @param num
* 获取当前语言版本
*/
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) => {
console.log('split',route.query)
const split = String(route.query.spm).split('.')
return split[index];
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 = () => {
return route.path;
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;
}
/**
* 跳转页面函数
* 携带用于统计用户行为的参数
* @param path /product/detail.html
* @param id 128
* @param d 项目数据
* @param isOpen 是否新窗口打开
* @param isToken 是否登录控制台
* 拼接规则: {域名}{path}?spm=c.{用户ID}.{租户ID}.{栏目ID}.{商品ID}.{timestamp}&token={token}
* @return https:///websoft.top/product/detail/128.html?spm=c.5.3057.10005.undefined&token=DDkr1PpE9DouIVMjLEMt9733QsgG7oNV
* 获取当前网址的ID部分
* 示例 /article/123.html
* @return 123
*/
export function openSpmUrl(p: string, d?: any, id = 0, isOpen?: boolean, isToken?: boolean): void {
const config = useWebsite();
const itemId = ref<number>(0);
const path = ref<string>('');
const spm = ref<string>('');
const model = ref<string>('c');
const tid = d?.tenantId || localStorage.getItem('TID_ADMIN');
const mid = config.value.loginUser?.merchantId || 0;
const pid = d?.parentId || 0;
const cid = d?.categoryId || 0;
const uid = localStorage.getItem('UserId') || 0;
const timestamp = ref(Date.now() / 1000);
let token = '';
// TODO 登录控制台
if(isToken){
token = `&token=${localStorage.getItem('token')}`;
}
// TODO 判断模型
if(d?.articleId){
model.value = 'a';
}
if(d?.goodsId){
model.value = 'g';
}
if(d?.productId){
model.value = 'p';
}
if(d?.appId){
model.value = 'app';
}
if(d?.plugId){
model.value = 'plug';
}
if(d?.itemId > 0){
path.value = d.path;
itemId.value = d.itemId;
}else {
path.value = p;
itemId.value = id;
}
if(d?.model === 'links'){
window.open(d.path,'_blank');
return;
}
// TODO 封装spm
spm.value = `?spm=${model.value}.${tid}.${mid}.${pid}.${cid}.${itemId.value}.${uid}.${timestamp.value}${token}`;
// TODO 账号密码
if(d?.account || d?.password){
spm.value += `&account=${d.account}&password=${d.password}`;
}
// TODO 新窗口打开
if(isOpen){
window.open(`${path.value}${spm.value}`,'_blank');
return;
}else {
window.open(`${path.value}${spm.value}`, '_top');
return;
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) {
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 uid = user.value?.userId;
const token = localStorage.getItem('token');
const tid = user.value?.tenantId;
openSpmUrl(`https://console.websoft.top/token-login`,undefined, uid, true,true)
openUrl(`https://${tid}.websoft.top/token-login?token=${token}`)
}
// 单点登录开发者中心
export function loginDeveloperCenterByToken(item: Company): void {
const user = useUser();
const tenantId = Number(user.value?.tenantId);
const token = localStorage.getItem('token');
if(!token){
window.open(`https://${item.domain}`, '_blank')
return;
}
openSpmUrl(`https://${item.domain}/token-login`,undefined, tenantId, true,true)
}
// 单点登录应用控制台
export function loginByToken(item: Company): void {
const user = useUser();
const tenantId = Number(user.value?.tenantId);
const token = localStorage.getItem('token');
window.open(`https://${item.domain}`, '_blank')
}
export function getSpmUrl(path: string, d?: any, id = 0): string {
const config = useWebsite();
const spm = ref<string>('');
const model = ref<string>('c');
const tid = config.value.tenantId || 0;
const mid = config.value.loginUser?.merchantId || 0;
const pid = d?.parentId || 0;
const cid = d?.categoryId || 0;
const uid = config.value.loginUser?.userId || 0;
const timestamp = ref(Date.now() / 1000);
let token = uuidv4();
// TODO 封装spm
spm.value = `?spm=${model.value}.${tid}.${mid}.${pid}.${cid}.${id}.${uid}.${timestamp.value}&token=${token}`;
return `${path}${spm.value}`
}
export function openUrl(url: string) {
window.open(url, '_blank')
export function getTimeStamp(): number {
return new Date().getTime();
}

View File

@@ -22,7 +22,7 @@ export function getOriginDomain(): string {
}
/**
* 域名的第一部分
* 域名的第一部分解析租户ID
* 获取tenantId
* @return 10140
*/
@@ -32,7 +32,14 @@ export function getDomainPart1(): any {
return undefined;
}
if (typeof (split[0])) {
return 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;
}
@@ -43,7 +50,12 @@ export function getDomainPart1(): any {
* @return 10140
*/
export function getTenantId() {
return getDomainPart1();
let tenantId = localStorage.getItem('TenantId');
if(getDomainPart1()){
tenantId = getDomainPart1();
return tenantId;
}
return tenantId;
}
/**

View File

@@ -1,88 +1,108 @@
/**
* axios 实例
*/
import axios from 'axios';
import type { AxiosResponse } from 'axios';
import {API_BASE_URL, TOKEN_HEADER_NAME, LAYOUT_PATH, TOKEN_STORE_NAME} from '@/config';
import type { ApiResult } from '@/api';
import { getHostname, getTenantId } from '@/utils/domain';
import type { UseFetchOptions } from '#app';
import {useToken} from "~/composables/configState";
import {getTenantId} from "~/utils/domain";
const service = axios.create({
baseURL: API_BASE_URL
});
export const request = <T>(url:string, options?: UseFetchOptions<T, unknown>) => {
const nuxtApp = useNuxtApp()
const config = useRuntimeConfig()
const TenantId = localStorage.getItem('TenantId') || `${config.public.tenantId}`;
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)
}
/**
* 添加请求拦截器
*/
service.interceptors.request.use(
(config) => {
const TENANT_ID = localStorage.getItem('TenantId') || 5;
const token = localStorage.getItem(TOKEN_STORE_NAME);
// 添加 token 到 header
if (token && config.headers) {
config.headers.common[TOKEN_HEADER_NAME] = token;
}
// 获取租户ID
if (config.headers) {
// 附加企业ID
const companyId = localStorage.getItem('CompanyId');
if (companyId) {
config.headers.common['CompanyId'] = companyId;
}
// 通过网站域名获取租户ID
if (getHostname()) {
config.headers.common['Domain'] = getHostname();
}
// 解析二级域名获取租户ID
if (getTenantId()) {
config.headers.common['TenantId'] = getTenantId();
return config;
}
if (TENANT_ID) {
config.headers.common['TenantId'] = TENANT_ID;
return config;
}
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
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)
})
})
}
/**
* 添加响应拦截器
*/
service.interceptors.response.use(
(res: AxiosResponse<ApiResult<unknown>>) => {
// 登录过期处理
if (res.data?.code === 401) {
// const currentPath = unref(router.currentRoute).path;
// if (currentPath == LAYOUT_PATH) {
// logout(true);
// } else {
// Modal.destroyAll();
// Modal.info({
// title: '系统提示',
// content: '登录状态已过期, 请退出重新登录!',
// okText: '重新登录',
// onOk: () => {
// logout(false, currentPath);
// }
// });
// }
// return Promise.reject(new Error(res.data.message));
}
// token 自动续期
const token = res.headers[TOKEN_HEADER_NAME.toLowerCase()];
if (token) {
localStorage.setItem(TOKEN_STORE_NAME, token);
}
return res;
},
(error) => {
return Promise.reject(error);
}
);
export default service;
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);
}
}
}
};