first commit

This commit is contained in:
muzi
2025-06-16 14:42:26 +08:00
commit 6f06721506
149 changed files with 56883 additions and 0 deletions

25
src/App.vue Normal file
View File

@ -0,0 +1,25 @@
<template>
<a-config-provider :locale="zhCN" size="small" :theme="redTheme">
<LayoutBasic>
<router-view />
</LayoutBasic>
</a-config-provider>
</template>
<script setup lang="ts">
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
onMounted(() => {
// 监听全局未处理错误
window.addEventListener('unhandledrejection', (event) => {
event.preventDefault();
console.log(event);
console.error(`发现catch报错${event.reason}`);
});
});
const redTheme = {
token: {
colorPrimary: '#6d4cfe', // 主色
colorLink: '#f5222d', // 链接色
},
};
</script>

111
src/api/all/index.ts Normal file
View File

@ -0,0 +1,111 @@
import Http from '@/api';
// 导出一个函数,用于获取行业树
export const fetchIndustriesTree = (params = {}) => {
// 发送GET请求获取行业树
return Http.get('/v1/industries/tree', params);
};
// 导出一个函数,用于获取菜单树
export const fetchMenusTree = (params = {}) => {
// 使用Http.get方法发送GET请求获取菜单树
return Http.get('/v1/menus/tree', params);
};
// 导出一个函数,用于获取行业话题列表
export const fetchIndustryTopics = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/industry-topics/list', params);
};
// 导出一个函数,用于获取行业话题详情
export const fetchIndustryTopicDetail = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/industry-topics/' + params, {});
};
export const fetchindustryTerms = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/industry-terms/list', params);
};
// 导出一个函数fetchKeywordTrendsList用于获取行业话题列表
export const fetchKeywordTrendsList = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/industry-keyword-trends/list', params);
};
// 导出一个函数,用于获取行业话题列表
export const fetchIndustryEmotions = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/industry-emotions', params);
};
export const fetchNewKeywordList = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/industry-new-keywords/list', params);
};
// 导出一个函数fetchUserPainPointsList用于获取用户痛点列表
export const fetchUserPainPointsList = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/user-pain-points/list', params);
};
// 导出一个函数fetchUserPainPointsDetail用于获取用户痛点详情
export const fetchUserPainPointsDetail = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/user-pain-points/' + params, {});
};
// 导出一个函数fetchFocusBrandsList用于获取行业话题列表
export const fetchFocusBrandsList = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/focus-brands/list', params);
};
// 导出一个函数fetchFocusBrandsList用于获取行业话题列表
// 导出一个函数,用于获取行业话题列表
export const fetchEventDynamicsList = (params: any) => {
// 使用Http.get方法发送GET请求获取行业话题列表
return Http.get('/v1/event-dynamics/list', params);
};
// 导出一个函数,用于获取年龄分布列表
export const fetchAgeDistributionsList = (params: any) => {
// 使用Http.get方法发送GET请求获取年龄分布列表
return Http.get('/v1/age-distributions/list', params);
};
// 导出一个函数,用于获取地理分布列表
export const fetchGeoDistributionsList = (params: any) => {
// 使用Http.get方法发送GET请求获取地理分布列表
return Http.get('/v1/geo-distributions/list', params);
};
// 导出一个函数,用于获取性别分布列表
export const fetchGenderDistributionsList = (params: any) => {
// 使用Http.get方法发送GET请求获取性别分布列表
return Http.get('/v1/gender-distributions/list', params);
};
// 导出一个函数,用于获取产品列表
export const fetchProductList = () => {
// 使用Http.get方法发送GET请求获取产品列表
return Http.get('/v1/products/list', {}, { headers: { 'enterprise-id': 1 } });
};
// 导出一个函数,用于获取成功案例列表
export const fetchSuccessCaseList = () => {
// 使用Http.get方法发送GET请求获取成功案例列表
return Http.get('/v1/success-cases/list');
};
// 试用产品
export const trialProduct = (id: number) => {
return Http.post(`/v1/products/${id}/try`, {}, { headers: { 'enterprise-id': 1 } });
};

36
src/api/apiCodes.ts Normal file
View File

@ -0,0 +1,36 @@
/*
* @Author: 田鑫
* @Date: 2023-02-17 11:15:23
* @LastEditors: 田鑫
* @LastEditTime: 2023-02-21 16:25:32
* @Description: API状态码
*/
export enum API_STATUS_CODE {
/** 正常响应 */
SUCCESS = 200,
/** 会话失效,请重新登录 */
SESSION_FAILUIRE = 1000,
/** 认证失败,无法访问系统资源 */
AUTH_FAILED = 1001,
/** 未授权,无法访问系统资源 */
RESOURCE_FORBIDDEN = 1002,
/** 黑名单用户 */
BLACK_LIST_USER = 1003,
/** 非法IP地址 */
ILLEGAL_IP = 1004,
/** 网关访问受限 */
GATEWAY_FORBIDDEN = 1005,
/** 网关访问异常,请稍后重试 */
GATEWAY_ERROR = 1006,
/** 流控异常 */
TRAFFIC_CONTROL_ERROR = 1007,
/** 资源找不到服务 */
RESOURCE_NOT_FOUND = 1008,
/** 资源请求超时 */
FETCH_TIMEOUT = 1009,
/** 资源响应异常 */
RESOURCE_RESPONSE_ERROR = 1010,
/** 请求路径不符合规范 */
ILLEGAL_PATH = 1011,
}

32
src/api/axiosHandler.ts Normal file
View File

@ -0,0 +1,32 @@
/*
* @Author: 田鑫
* @Date: 2023-02-21 16:34:47
* @LastEditors: 田鑫
* @LastEditTime: 2023-02-21 16:59:10
* @Description:
*/
import router from '@/router';
import { clearToken } from '@/utils/auth';
import { Message } from '@arco-design/web-vue';
/**
* 处理业务逻辑定义的错误code
* @param errStatus
*/
export const handleCodeError = (error: any) => {
let errMessage = '未知错误';
console.log(error);
errMessage = error.msg;
switch (error.code) {
case 1000:
router.replace({
name: '/login/password',
});
clearToken();
break;
default:
errMessage = error.msg || `未知错误-${error.code}`;
}
Message.error(errMessage);
};

24
src/api/example/index.ts Normal file
View File

@ -0,0 +1,24 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 18:42:52
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 19:06:13
* @Description: 示例api
*/
import Http from '@/api';
export declare namespace IExample {
interface ITableResponse {
id: string;
column1: string;
column2: string;
column3: string;
column4: string;
column5: string;
}
}
export const fetchTableData = (params = {}) => {
return Http.get<IExample.ITableResponse[]>('/api/example-table', params);
};

83
src/api/index.ts Normal file
View File

@ -0,0 +1,83 @@
/*
* @Author: 田鑫
* @Date: 2023-02-17 11:58:44
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 19:17:29
* @Description:
*/
import axios from 'axios';
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
//* 导出Request类可以用来自定义传递配置来创建实例
export class Request {
//* axios 实例
private instance: AxiosInstance;
//* 基础配置
private baseConfig: AxiosRequestConfig = { baseURL: import.meta.env.EO_API_URL, timeout: 60000 };
public constructor(config: AxiosRequestConfig) {
this.instance = axios.create(Object.assign(this.baseConfig, config));
this.instance.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = localStorage.getItem('token') as string;
if (token) {
config.headers!.Authorization = token;
} else {
config.headers!.satoken = '123';
}
return config;
},
(err: any) => {
return Promise.reject(err);
},
);
this.instance.interceptors.response.use(
(res: AxiosResponse) => {
//* http请求成功
//* 存入通用response header
let tenanttype = localStorage.getItem('tenanttype') as string;
if (!tenanttype) {
tenanttype = res.headers!.tenanttype;
localStorage.setItem('tenanttype', tenanttype);
}
if (res.data.code === 200) {
return res.data.data;
}
AMessage.error(res.data!.msg);
return Promise.reject(res.data);
},
(err: any) => {
// AMessage.error(err.message);
// 这里用来处理http常见错误进行全局提示
return Promise.reject(err.response);
},
);
}
//* 定义请求方法
public request<T = any>(config: AxiosRequestConfig): Promise<T> {
return this.instance.request(config);
}
public get<T = any>(url: string, params?: any, config?: AxiosRequestConfig): Promise<T> {
return this.instance.get(url, { params, ...config });
}
public post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.instance.post(url, data, config);
}
public put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.instance.put(url, data, config);
}
public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.instance.delete(url, config);
}
}
//* 默认导出Request实例
export default new Request({});

19
src/api/types.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
/*
* @Author: 田鑫
* @Date: 2023-02-19 21:52:10
* @LastEditors: 田鑫
* @LastEditTime: 2023-02-21 16:59:27
* @Description:
*/
import 'axios';
import { API_STATUS_CODE } from './apiCodes';
declare module 'axios' {
export interface IWygResponse<T> {
success: boolean;
msg: string;
code: API_STATUS_CODE;
data: T;
timestamp: number;
}
}

3
src/assets/LOGO.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="148" height="32" viewBox="0 0 148 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.082 15.38L13.342 13.17H28.188L28.396 11.428H14.694L15.006 9.244H28.682L28.916 7.528H14.278L14.538 5.292H29.28C30.06 5.292 30.6407 5.49133 31.022 5.89C31.4033 6.27133 31.5507 6.83467 31.464 7.58L30.502 15.38H13.082ZM30.71 27.262C28.6647 26.3433 27.0007 25.4333 25.718 24.532C24.2273 23.492 22.91 22.2787 21.766 20.892C20.778 22.3653 19.3913 23.6307 17.606 24.688C16.15 25.5547 14.304 26.4127 12.068 27.262L10.274 25.442C11.9727 24.7487 13.316 24.1767 14.304 23.726C15.292 23.258 16.202 22.738 17.034 22.166C18.0047 21.5073 18.802 20.7707 19.426 19.956C20.0673 19.124 20.5093 18.1707 20.752 17.096L20.934 15.978H23.56L23.326 17.148C23.1873 17.7373 23.04 18.2313 22.884 18.63C24.1147 20.242 25.4927 21.542 27.018 22.53C27.7633 23.0153 28.5867 23.466 29.488 23.882C30.4067 24.2807 31.49 24.7053 32.738 25.156L30.71 27.262ZM28.942 16.732H31.49L28.63 20.944H25.978L28.942 16.732ZM17.684 20.944H15.11L12.77 16.914H15.24L17.684 20.944ZM40.98 15.926C39.9747 17.8327 38.9347 19.4793 37.86 20.866L35.884 19.722C37.8253 17.2607 39.5847 14.3833 41.162 11.09H37.808L38.068 8.802H41.864L42.41 4.46H44.828L44.282 8.802H47.09L46.856 11.09H43.996L43.918 11.688C44.802 13.1267 45.738 14.912 46.726 17.044L44.828 18.214C44.412 17.174 43.97 16.1427 43.502 15.12L42.046 26.82H39.628L40.98 15.926ZM55.046 7.996H50.99L49.976 16.342C49.404 21.1433 48.1647 24.714 46.258 27.054L43.866 26.144C44.8887 24.8267 45.686 23.3793 46.258 21.802C46.83 20.2073 47.246 18.37 47.506 16.29L48.546 7.554C48.6327 6.89533 48.8147 6.41867 49.092 6.124C49.3867 5.812 49.8373 5.656 50.444 5.656H55.774C56.3633 5.656 56.814 5.80333 57.126 6.098C57.438 6.39267 57.594 6.81733 57.594 7.372C57.594 7.56267 57.5853 7.71 57.568 7.814L55.748 23.622C55.7307 23.674 55.722 23.752 55.722 23.856C55.722 24.012 55.7653 24.116 55.852 24.168C55.9387 24.22 56.086 24.246 56.294 24.246C57.1953 24.246 58.114 23.9947 59.05 23.492L58.764 26.118C58.0533 26.4647 57.1607 26.638 56.086 26.638H55.124C53.824 26.638 53.174 26.0053 53.174 24.74C53.174 24.532 53.1827 24.3673 53.2 24.246L55.046 7.996ZM73.922 6.41C74.8927 6.41 75.456 6.878 75.612 7.814L78.576 25.936H75.326L74.468 20.294H66.07L63.808 25.936H60.454L67.942 7.606C68.2713 6.80867 68.878 6.41 69.762 6.41H73.922ZM70.516 9.27L67.214 17.434H74.026L72.778 9.27H70.516ZM83.2706 6.41H86.3386L83.9466 25.936H80.8786L83.2706 6.41ZM103.385 4.486H105.855L105.621 5.994H111.133L110.821 8.23H105.309L105.153 9.426H102.735L102.891 8.23H97.7169L97.5609 9.426H95.1169L95.2729 8.23H89.7869L90.0989 5.994H95.5589L95.7669 4.486H98.2369L98.0289 5.994H103.177L103.385 4.486ZM107.961 9.92C108.793 9.92 109.382 10.0587 109.729 10.336C110.093 10.6133 110.275 11.038 110.275 11.61C110.275 11.818 110.266 11.9827 110.249 12.104L109.989 14.626H107.493L107.805 11.974H92.5689L92.2569 14.626H89.7089L90.0209 12.052C90.1249 11.3067 90.3589 10.7693 90.7229 10.44C91.0869 10.0933 91.6589 9.92 92.4389 9.92H107.961ZM106.479 18.942H92.6209L93.0369 15.068C93.1235 14.3573 93.3229 13.8807 93.6349 13.638C93.9642 13.3953 94.5275 13.274 95.3249 13.274H104.919C105.664 13.274 106.202 13.456 106.531 13.82C106.878 14.1667 107.016 14.6173 106.947 15.172L106.479 18.942ZM95.3769 16.966H104.295L104.529 15.25H95.5329L95.3769 16.966ZM107.519 26.664H90.3849L90.9309 22.27C91.1215 20.8313 91.9882 20.112 93.5309 20.112H105.959C107.519 20.112 108.221 20.8227 108.065 22.244L107.519 26.664ZM93.2709 24.506H105.205L105.491 22.244H93.5569L93.2709 24.506ZM132.583 4.564L131.829 10.7H134.481C135.989 10.7 136.674 11.4453 136.535 12.936L135.157 25.13C135.07 25.6673 134.854 26.0833 134.507 26.378C134.178 26.6727 133.762 26.82 133.259 26.82C132.167 26.82 130.884 26.6727 129.411 26.378L129.645 24.168C130.616 24.376 131.56 24.4973 132.479 24.532H132.609C132.73 24.532 132.808 24.506 132.843 24.454C132.878 24.402 132.904 24.2893 132.921 24.116L133.207 21.62H125.927L125.329 26.716H123.015L124.627 12.806C124.696 12.1473 124.922 11.636 125.303 11.272C125.702 10.8907 126.239 10.7 126.915 10.7H129.437L130.165 4.564H132.583ZM124.211 6.826L123.769 9.088H117.373C116.801 9.088 116.515 8.88 116.515 8.464C116.515 8.308 116.558 8.11733 116.645 7.892L117.893 4.616H120.441L119.661 6.826H124.211ZM127.409 5.474L128.475 9.686H126.057L125.043 5.474H127.409ZM137.211 5.552L135.625 9.712H133.233L134.819 5.552H137.211ZM123.067 16.628L122.807 18.89H119.791L119.297 22.894V22.972C119.297 23.2147 119.375 23.336 119.531 23.336L119.739 23.284C120.779 22.92 121.689 22.556 122.469 22.192L122.183 24.766C120.952 25.286 119.73 25.702 118.517 26.014C118.344 26.066 118.17 26.092 117.997 26.092C117.564 26.092 117.226 25.9273 116.983 25.598C116.758 25.286 116.68 24.8527 116.749 24.298L117.399 18.89H114.851L115.111 16.628H117.685L118.023 13.846H115.631L115.891 11.61H123.223L122.963 13.846H120.415L120.051 16.628H123.067ZM134.195 12.884H126.967L126.707 15.068H133.935L134.195 12.884ZM133.701 17.2H126.447L126.187 19.488H133.441L133.701 17.2Z" fill="#6D4CFE"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,3 @@
<svg width="74" height="80" viewBox="0 0 74 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Vector" d="M45.3172 2.32014L65.8207 14.7368C68.1655 16.1566 70.1036 18.1528 71.4485 20.5334C72.7934 22.9139 73.4999 25.5987 73.5 28.3295V51.4893C73.4999 54.2201 72.7934 56.9049 71.4485 59.2854C70.1036 61.666 68.1655 63.6622 65.8207 65.0821L45.0173 77.6802C42.5864 79.1521 39.8048 79.9521 36.9594 79.9979C34.114 80.0437 31.3079 79.3336 28.8303 77.9407L8.62522 66.5803C6.15918 65.1936 4.1075 63.1804 2.67992 60.7464C1.25234 58.3125 0.500067 55.545 0.5 52.727V28.3295C0.500098 25.5987 1.20658 22.9139 2.55149 20.5334C3.8964 18.1528 5.83448 16.1566 8.17928 14.7368L28.6828 2.32014C31.1888 0.802647 34.0662 0 37 0C39.9338 0 42.8112 0.802647 45.3172 2.32014ZM28.6304 15.1772L28.5066 15.0446C27.7623 14.3026 24.9772 12.184 23.641 14.1021C21.8842 16.6202 25.7564 19.8044 28.1051 20.2512L28.2924 20.2828L28.3876 20.3017V23.2587C28.1448 23.5018 27.9893 24.0165 28.1226 24.6006C20.7242 29.0289 16.3807 34.7281 16.6044 43.2736C16.8758 54.4447 25.8834 61.9562 36.8445 62.0335C48.1738 62.1062 57.4035 54.2694 57.4035 42.7448C57.4035 34.586 52.6204 29.0084 45.9869 24.8832L45.5315 24.6037L45.5585 19.5029L45.7299 19.6292C46.4979 20.1549 48.2261 20.8274 48.7498 20.9095L49.2465 20.9837C50.459 21.14 52.2776 21.189 52.5712 19.6545C52.8521 18.1736 51.2223 17.2043 50.0384 16.8917C48.7165 16.5444 47.3247 16.1971 46.052 16.3739C44.0175 16.6565 43.7192 18.2857 43.6319 19.4177L43.6208 19.5755C43.6113 19.7255 43.6097 20.2449 43.6113 20.9222V23.5113L42.3671 22.8672L41.6339 22.5056C40.3215 21.8757 38.9631 20.9111 38.1267 20.2765L37.3158 19.6324C37.1336 19.4574 36.895 19.3523 36.6423 19.3355C36.3896 19.3188 36.1391 19.3915 35.9352 19.5408L35.4115 20.0049C34.6577 20.6506 32.9818 22.0257 31.6123 22.7045L30.9934 23.014L30.3951 23.3266L30.3983 20.1975H30.3919V18.9788L30.3792 18.7641L30.3475 18.3789C30.2412 17.4427 29.9301 16.6691 28.6304 15.1772ZM38.8679 36.692V41.0556H44.5015V43.1442H38.8679V49.4054H46.3773V51.2083H26.7975V49.4054H29.6587V39.9173H32.4312V49.4054H35.9177V36.692H38.8679ZM39.1535 30.1592L39.4185 30.6486C41.061 33.5551 43.6557 35.901 47.2025 37.6866L47.8024 37.9786V39.9583C43.3907 38.5059 39.9486 36.2957 37.4777 33.3277C35.1369 36.1394 31.9249 38.2707 27.8433 39.7215L27.1545 39.9583V37.9786L27.7544 37.6866C31.3012 35.9026 33.8959 33.5551 35.5368 30.6486L35.8018 30.1592H39.1551H39.1535Z" fill="#ABABAB"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,3 @@
<svg width="74" height="80" viewBox="0 0 74 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Vector" d="M45.3172 2.32014L65.8207 14.7368C68.1655 16.1566 70.1036 18.1528 71.4485 20.5334C72.7934 22.9139 73.4999 25.5987 73.5 28.3295V51.4893C73.4999 54.2201 72.7934 56.9049 71.4485 59.2854C70.1036 61.666 68.1655 63.6622 65.8207 65.0821L45.0173 77.6802C42.5864 79.1521 39.8048 79.9521 36.9594 79.9979C34.114 80.0437 31.3079 79.3336 28.8303 77.9407L8.62522 66.5803C6.15918 65.1936 4.1075 63.1804 2.67992 60.7464C1.25234 58.3125 0.500067 55.545 0.5 52.727V28.3295C0.500098 25.5987 1.20658 22.9139 2.55149 20.5334C3.8964 18.1528 5.83448 16.1566 8.17928 14.7368L28.6828 2.32014C31.1888 0.802647 34.0662 0 37 0C39.9338 0 42.8112 0.802647 45.3172 2.32014ZM28.6304 15.1772L28.5066 15.0446C27.7623 14.3026 24.9772 12.184 23.641 14.1021C21.8842 16.6202 25.7564 19.8044 28.1051 20.2512L28.2924 20.2828L28.3876 20.3017V23.2587C28.1448 23.5018 27.9893 24.0165 28.1226 24.6006C20.7242 29.0289 16.3807 34.7281 16.6044 43.2736C16.8758 54.4447 25.8834 61.9562 36.8445 62.0335C48.1738 62.1062 57.4035 54.2694 57.4035 42.7448C57.4035 34.586 52.6204 29.0084 45.9869 24.8832L45.5315 24.6037L45.5585 19.5029L45.7299 19.6292C46.4979 20.1549 48.2261 20.8274 48.7498 20.9095L49.2465 20.9837C50.459 21.14 52.2776 21.189 52.5712 19.6545C52.8521 18.1736 51.2223 17.2043 50.0384 16.8917C48.7165 16.5444 47.3247 16.1971 46.052 16.3739C44.0175 16.6565 43.7192 18.2857 43.6319 19.4177L43.6208 19.5755C43.6113 19.7255 43.6097 20.2449 43.6113 20.9222V23.5113L42.3671 22.8672L41.6339 22.5056C40.3215 21.8757 38.9631 20.9111 38.1267 20.2765L37.3158 19.6324C37.1336 19.4574 36.895 19.3523 36.6423 19.3355C36.3896 19.3188 36.1391 19.3915 35.9352 19.5408L35.4115 20.0049C34.6577 20.6506 32.9818 22.0257 31.6123 22.7045L30.9934 23.014L30.3951 23.3266L30.3983 20.1975H30.3919V18.9788L30.3792 18.7641L30.3475 18.3789C30.2412 17.4427 29.9301 16.6691 28.6304 15.1772ZM38.8679 36.692V41.0556H44.5015V43.1442H38.8679V49.4054H46.3773V51.2083H26.7975V49.4054H29.6587V39.9173H32.4312V49.4054H35.9177V36.692H38.8679ZM39.1535 30.1592L39.4185 30.6486C41.061 33.5551 43.6557 35.901 47.2025 37.6866L47.8024 37.9786V39.9583C43.3907 38.5059 39.9486 36.2957 37.4777 33.3277C35.1369 36.1394 31.9249 38.2707 27.8433 39.7215L27.1545 39.9583V37.9786L27.7544 37.6866C31.3012 35.9026 33.8959 33.5551 35.5368 30.6486L35.8018 30.1592H39.1551H39.1535Z" fill="#ABABAB"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,365 @@
<svg width="26" height="18" viewBox="0 0 26 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.084 1.77441L20.1682 5.51069L20.7939 8.63189C24.6015 5.94241 25.0839 4.43137 25.084 1.77441Z" fill="url(#paint0_linear_256_676)" stroke="url(#paint1_linear_256_676)" stroke-width="0.05"/>
<path d="M25.1108 1.74219L19.5271 4.19773L20.126 7.28362C23.4547 6.06964 24.9557 2.90886 25.1108 1.74219Z" fill="url(#paint2_linear_256_676)" stroke="url(#paint3_linear_256_676)" stroke-width="0.05"/>
<path d="M25.0747 6.11915L19.1994 8.01789L18.7569 11.1703C23.24 9.89256 24.1953 8.62633 25.0747 6.11915Z" fill="url(#paint4_linear_256_676)" stroke="url(#paint5_linear_256_676)" stroke-width="0.05"/>
<path d="M25.1108 6.09705L19.0291 6.56618L18.5729 9.67637C22.1158 9.63249 24.5783 7.14664 25.1108 6.09705Z" fill="url(#paint6_linear_256_676)" stroke="url(#paint7_linear_256_676)" stroke-width="0.05"/>
<path d="M25.072 9.44796L18.9882 10.5026L18.1072 13.5616C22.7254 12.9262 23.849 11.8067 25.072 9.44796Z" fill="url(#paint8_linear_256_676)" stroke="url(#paint9_linear_256_676)" stroke-width="0.05"/>
<path d="M25.1107 9.43114L19.0234 9.04135L18.1349 12.0566C21.6488 12.5108 24.4361 10.3955 25.1107 9.43114Z" fill="url(#paint10_linear_256_676)" stroke="url(#paint11_linear_256_676)" stroke-width="0.05"/>
<path d="M23.53 12.4864L17.3933 11.8043L15.695 14.4966C20.3072 15.1732 21.6983 14.4111 23.53 12.4864Z" fill="url(#paint12_linear_256_676)" stroke="url(#paint13_linear_256_676)" stroke-width="0.05"/>
<path d="M23.572 12.481L17.8343 10.4107L16.1409 13.059C19.3891 14.4742 22.6554 13.2193 23.572 12.481Z" fill="url(#paint14_linear_256_676)" stroke="url(#paint15_linear_256_676)" stroke-width="0.05"/>
<path d="M0.916275 1.77441L5.83203 5.51069L5.20633 8.63189C1.39875 5.94241 0.91639 4.43137 0.916275 1.77441Z" fill="url(#paint16_linear_256_676)" stroke="url(#paint17_linear_256_676)" stroke-width="0.05"/>
<path d="M0.889464 1.74219L6.47314 4.19773L5.87423 7.28362C2.54558 6.06964 1.04452 2.90886 0.889464 1.74219Z" fill="url(#paint18_linear_256_676)" stroke="url(#paint19_linear_256_676)" stroke-width="0.05"/>
<path d="M0.92574 6.11915L6.80104 8.01789L7.24362 11.1703C2.7605 9.89256 1.80522 8.62633 0.92574 6.11915Z" fill="url(#paint20_linear_256_676)" stroke="url(#paint21_linear_256_676)" stroke-width="0.05"/>
<path d="M0.889697 6.09705L6.9714 6.56618L7.42757 9.67637C3.88472 9.63249 1.42215 7.14664 0.889697 6.09705Z" fill="url(#paint22_linear_256_676)" stroke="url(#paint23_linear_256_676)" stroke-width="0.05"/>
<path d="M0.928288 9.44796L7.01205 10.5026L7.89304 13.5616C3.27489 12.9262 2.15122 11.8067 0.928288 9.44796Z" fill="url(#paint24_linear_256_676)" stroke="url(#paint25_linear_256_676)" stroke-width="0.05"/>
<path d="M0.889507 9.43114L6.97681 9.04135L7.86533 12.0566C4.35145 12.5108 1.56411 10.3955 0.889507 9.43114Z" fill="url(#paint26_linear_256_676)" stroke="url(#paint27_linear_256_676)" stroke-width="0.05"/>
<path d="M2.47021 12.4864L8.60691 11.8043L10.3053 14.4966C5.693 15.1732 4.30191 14.4111 2.47021 12.4864Z" fill="url(#paint28_linear_256_676)" stroke="url(#paint29_linear_256_676)" stroke-width="0.05"/>
<path d="M2.42825 12.481L8.16592 10.4107L9.85936 13.059C6.61116 14.4742 3.34484 13.2193 2.42825 12.481Z" fill="url(#paint30_linear_256_676)" stroke="url(#paint31_linear_256_676)" stroke-width="0.05"/>
<circle cx="13.0463" cy="8.63179" r="8.10445" fill="#FCBD35"/>
<circle cx="13.0463" cy="8.63171" r="7.74538" fill="url(#paint32_linear_256_676)" stroke="url(#paint33_linear_256_676)" stroke-width="0.05"/>
<circle cx="13.0463" cy="8.63225" r="6.15764" fill="#FBD371"/>
<circle cx="13.0463" cy="8.63225" r="6.10764" stroke="#FFFCED" stroke-opacity="0.8" stroke-width="0.1"/>
<g filter="url(#filter0_d_256_676)">
<circle cx="13.0463" cy="8.63174" r="5.34952" fill="#FFF8D5"/>
<circle cx="13.0463" cy="8.63174" r="5.29952" stroke="#FFFCED" stroke-opacity="0.8" stroke-width="0.1"/>
</g>
<g filter="url(#filter1_d_256_676)">
<circle cx="13.0461" cy="1.70335" r="0.44749" fill="#FFF3CE"/>
</g>
<circle cx="13.0465" cy="1.70351" r="0.364642" fill="url(#paint34_linear_256_676)"/>
<g filter="url(#filter2_d_256_676)">
<circle cx="13.0466" cy="15.5598" r="0.44749" fill="#FFF3CE"/>
</g>
<circle cx="13.0468" cy="15.56" r="0.364642" fill="url(#paint35_linear_256_676)"/>
<circle cx="13.0468" cy="15.56" r="0.364642" fill="url(#paint36_linear_256_676)"/>
<g filter="url(#filter3_d_256_676)">
<circle cx="17.9453" cy="3.73246" r="0.44749" transform="rotate(45 17.9453 3.73246)" fill="#FFF3CE"/>
</g>
<circle cx="17.9453" cy="3.73248" r="0.364642" transform="rotate(45 17.9453 3.73248)" fill="url(#paint37_linear_256_676)"/>
<g filter="url(#filter4_d_256_676)">
<circle cx="8.14746" cy="13.5313" r="0.44749" transform="rotate(45 8.14746 13.5313)" fill="#FFF3CE"/>
</g>
<circle cx="8.14746" cy="13.5313" r="0.364642" transform="rotate(45 8.14746 13.5313)" fill="url(#paint38_linear_256_676)"/>
<circle cx="8.14746" cy="13.5313" r="0.364642" transform="rotate(45 8.14746 13.5313)" fill="url(#paint39_linear_256_676)"/>
<g filter="url(#filter5_d_256_676)">
<circle cx="19.9749" cy="8.63108" r="0.44749" transform="rotate(90 19.9749 8.63108)" fill="#FFF3CE"/>
</g>
<circle cx="19.9745" cy="8.63124" r="0.364642" transform="rotate(90 19.9745 8.63124)" fill="url(#paint40_linear_256_676)"/>
<g filter="url(#filter6_d_256_676)">
<circle cx="6.11794" cy="8.63108" r="0.44749" transform="rotate(90 6.11794 8.63108)" fill="#FFF3CE"/>
</g>
<circle cx="6.11778" cy="8.63124" r="0.364642" transform="rotate(90 6.11778 8.63124)" fill="url(#paint41_linear_256_676)"/>
<circle cx="6.11778" cy="8.63124" r="0.364642" transform="rotate(90 6.11778 8.63124)" fill="url(#paint42_linear_256_676)"/>
<g filter="url(#filter7_d_256_676)">
<circle cx="17.9458" cy="13.5303" r="0.44749" transform="rotate(135 17.9458 13.5303)" fill="#FFF3CE"/>
</g>
<circle cx="17.9455" cy="13.5303" r="0.364642" transform="rotate(135 17.9455 13.5303)" fill="url(#paint43_linear_256_676)"/>
<g filter="url(#filter8_d_256_676)">
<circle cx="8.14743" cy="3.73242" r="0.44749" transform="rotate(135 8.14743 3.73242)" fill="#FFF3CE"/>
</g>
<circle cx="8.14716" cy="3.73242" r="0.364642" transform="rotate(135 8.14716 3.73242)" fill="url(#paint44_linear_256_676)"/>
<circle cx="8.14716" cy="3.73242" r="0.364642" transform="rotate(135 8.14716 3.73242)" fill="url(#paint45_linear_256_676)"/>
<g filter="url(#filter9_d_256_676)">
<path d="M12.2692 5.33496C11.6318 5.97235 10.9153 6.17047 10.6367 6.18985V8.23681C11.3389 8.03308 11.7011 7.82274 12.1267 7.2652V10.3614H10.6367V11.9289H15.4561V10.3614H14.2772V5.33496H12.2692Z" fill="#FFA028"/>
<path d="M14.2275 5.38477V10.4111H15.4062V11.8789H10.6865V10.4111H12.1768V7.11719L12.0869 7.23535C11.8768 7.51066 11.6841 7.69813 11.459 7.8418C11.2474 7.97684 11.0038 8.07275 10.6865 8.16797V6.23242C10.9999 6.19558 11.679 5.98727 12.2891 5.38477H14.2275Z" stroke="#FFFEFA" stroke-opacity="0.7" stroke-width="0.1"/>
</g>
<defs>
<filter id="filter0_d_256_676" x="7.49678" y="3.08223" width="11.099" height="11.0992" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.992157 0 0 0 0 0.631373 0 0 0 0 0.0862745 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter1_d_256_676" x="12.5086" y="1.22586" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter2_d_256_676" x="12.5091" y="15.0823" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter3_d_256_676" x="17.4078" y="3.25516" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter4_d_256_676" x="7.60995" y="13.054" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter5_d_256_676" x="19.4373" y="8.15359" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter6_d_256_676" x="5.58041" y="8.15359" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter7_d_256_676" x="17.4083" y="13.053" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter8_d_256_676" x="7.60995" y="3.25516" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.920673 0 0 0 0 0.659342 0 0 0 0 0.13668 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<filter id="filter9_d_256_676" x="10.4367" y="5.13496" width="5.21934" height="6.99375" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.992157 0 0 0 0 0.631373 0 0 0 0 0.0862745 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_676"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_676" result="shape"/>
</filter>
<linearGradient id="paint0_linear_256_676" x1="24.4895" y1="2.15758" x2="21.4798" y2="5.8581" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint1_linear_256_676" x1="23.5584" y1="2.26682" x2="13.706" y2="7.62766" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint2_linear_256_676" x1="24.4141" y1="2.055" x2="22.3051" y2="6.79212" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint3_linear_256_676" x1="23.3464" y1="2.13492" x2="16.9053" y2="9.9954" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint4_linear_256_676" x1="24.387" y1="6.28398" x2="19.2304" y2="10.1602" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint5_linear_256_676" x1="23.4722" y1="6.07887" x2="12.4007" y2="7.87675" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint6_linear_256_676" x1="24.3499" y1="6.16166" x2="20.7919" y2="9.93379" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint7_linear_256_676" x1="23.3158" y1="5.88369" x2="14.6362" y2="11.1694" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint8_linear_256_676" x1="24.3679" y1="9.51454" x2="18.718" y2="12.628" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint9_linear_256_676" x1="23.4909" y1="9.18297" x2="12.2767" y2="9.40788" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint10_linear_256_676" x1="24.3483" y1="9.38823" x2="20.2957" y2="12.6232" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint11_linear_256_676" x1="23.3636" y1="8.96777" x2="14.0275" y2="12.9819" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint12_linear_256_676" x1="22.8353" y1="12.3542" x2="16.5416" y2="13.7702" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint13_linear_256_676" x1="22.0854" y1="11.7914" x2="11.2526" y2="8.88297" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint14_linear_256_676" x1="22.8517" y1="12.2274" x2="18.0583" y2="14.2051" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint15_linear_256_676" x1="22.0231" y1="11.5492" x2="11.9384" y2="12.8032" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint16_linear_256_676" x1="1.51073" y1="2.15758" x2="4.52046" y2="5.8581" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint17_linear_256_676" x1="2.44188" y1="2.26682" x2="12.2943" y2="7.62766" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint18_linear_256_676" x1="1.58611" y1="2.055" x2="3.69514" y2="6.79212" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint19_linear_256_676" x1="2.65386" y1="2.13492" x2="9.09494" y2="9.9954" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint20_linear_256_676" x1="1.61351" y1="6.28398" x2="6.77006" y2="10.1602" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint21_linear_256_676" x1="2.52833" y1="6.07887" x2="13.5997" y2="7.87675" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint22_linear_256_676" x1="1.65061" y1="6.16166" x2="5.20862" y2="9.93379" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint23_linear_256_676" x1="2.68464" y1="5.88369" x2="11.3643" y2="11.1694" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint24_linear_256_676" x1="1.63239" y1="9.51454" x2="7.28229" y2="12.628" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint25_linear_256_676" x1="2.50933" y1="9.18297" x2="13.7235" y2="9.40788" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint26_linear_256_676" x1="1.65195" y1="9.38823" x2="5.70454" y2="12.6232" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint27_linear_256_676" x1="2.63669" y1="8.96777" x2="11.9727" y2="12.9819" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint28_linear_256_676" x1="3.16499" y1="12.3542" x2="9.45862" y2="13.7702" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCA36"/>
<stop offset="1" stop-color="#FF8E14"/>
</linearGradient>
<linearGradient id="paint29_linear_256_676" x1="3.91482" y1="11.7914" x2="14.7476" y2="8.88297" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint30_linear_256_676" x1="3.14855" y1="12.2274" x2="7.94198" y2="14.2051" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFF1BC"/>
<stop offset="1" stop-color="#FFCA3F"/>
</linearGradient>
<linearGradient id="paint31_linear_256_676" x1="3.97715" y1="11.5492" x2="14.0619" y2="12.8032" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFBF28"/>
</linearGradient>
<linearGradient id="paint32_linear_256_676" x1="5.27588" y1="0.861328" x2="15.9246" y2="16.4021" gradientUnits="userSpaceOnUse">
<stop offset="0.242116" stop-color="#FFF0C9"/>
<stop offset="0.557692" stop-color="#FFE6AD"/>
<stop offset="1" stop-color="#FDA118"/>
</linearGradient>
<linearGradient id="paint33_linear_256_676" x1="13.0463" y1="0.861328" x2="13.0463" y2="16.4021" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white" stop-opacity="0.6"/>
</linearGradient>
<linearGradient id="paint34_linear_256_676" x1="13.0465" y1="1.33887" x2="13.0465" y2="2.06815" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
<linearGradient id="paint35_linear_256_676" x1="13.0468" y1="15.1953" x2="13.0468" y2="15.9246" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#E97F33"/>
</linearGradient>
<linearGradient id="paint36_linear_256_676" x1="13.0468" y1="15.1953" x2="13.0468" y2="15.9246" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
<linearGradient id="paint37_linear_256_676" x1="17.9453" y1="3.36784" x2="17.9453" y2="4.09712" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
<linearGradient id="paint38_linear_256_676" x1="8.14746" y1="13.1667" x2="8.14746" y2="13.8959" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#E97F33"/>
</linearGradient>
<linearGradient id="paint39_linear_256_676" x1="8.14746" y1="13.1667" x2="8.14746" y2="13.8959" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
<linearGradient id="paint40_linear_256_676" x1="19.9745" y1="8.2666" x2="19.9745" y2="8.99588" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
<linearGradient id="paint41_linear_256_676" x1="6.11778" y1="8.2666" x2="6.11778" y2="8.99588" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#E97F33"/>
</linearGradient>
<linearGradient id="paint42_linear_256_676" x1="6.11778" y1="8.2666" x2="6.11778" y2="8.99588" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
<linearGradient id="paint43_linear_256_676" x1="17.9455" y1="13.1656" x2="17.9455" y2="13.8949" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
<linearGradient id="paint44_linear_256_676" x1="8.14716" y1="3.36778" x2="8.14716" y2="4.09706" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#E97F33"/>
</linearGradient>
<linearGradient id="paint45_linear_256_676" x1="8.14716" y1="3.36778" x2="8.14716" y2="4.09706" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE07D"/>
<stop offset="0.688858" stop-color="#FFB749"/>
<stop offset="0.985577" stop-color="#FB944B"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,327 @@
<svg width="26" height="18" viewBox="0 0 26 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.084 1.77441L20.1682 5.51069L20.7939 8.63189C24.6015 5.94241 25.0839 4.43137 25.084 1.77441Z" fill="url(#paint0_linear_256_740)" stroke="url(#paint1_linear_256_740)" stroke-width="0.05"/>
<path d="M25.1108 1.74219L19.5271 4.19773L20.126 7.28362C23.4547 6.06964 24.9557 2.90886 25.1108 1.74219Z" fill="url(#paint2_linear_256_740)" stroke="url(#paint3_linear_256_740)" stroke-width="0.05"/>
<path d="M25.0747 6.11915L19.1994 8.01789L18.7569 11.1703C23.24 9.89256 24.1953 8.62633 25.0747 6.11915Z" fill="url(#paint4_linear_256_740)" stroke="url(#paint5_linear_256_740)" stroke-width="0.05"/>
<path d="M25.1108 6.09705L19.0291 6.56618L18.5729 9.67637C22.1158 9.63249 24.5783 7.14664 25.1108 6.09705Z" fill="url(#paint6_linear_256_740)" stroke="url(#paint7_linear_256_740)" stroke-width="0.05"/>
<path d="M25.072 9.44796L18.9882 10.5026L18.1072 13.5616C22.7254 12.9262 23.849 11.8067 25.072 9.44796Z" fill="url(#paint8_linear_256_740)" stroke="url(#paint9_linear_256_740)" stroke-width="0.05"/>
<path d="M25.1107 9.43114L19.0234 9.04135L18.1349 12.0566C21.6488 12.5108 24.4361 10.3955 25.1107 9.43114Z" fill="url(#paint10_linear_256_740)" stroke="url(#paint11_linear_256_740)" stroke-width="0.05"/>
<path d="M23.53 12.4864L17.3933 11.8043L15.695 14.4966C20.3072 15.1732 21.6983 14.4111 23.53 12.4864Z" fill="url(#paint12_linear_256_740)" stroke="url(#paint13_linear_256_740)" stroke-width="0.05"/>
<path d="M23.572 12.481L17.8343 10.4107L16.1409 13.059C19.3891 14.4742 22.6554 13.2193 23.572 12.481Z" fill="url(#paint14_linear_256_740)" stroke="url(#paint15_linear_256_740)" stroke-width="0.05"/>
<path d="M0.916275 1.77441L5.83203 5.51069L5.20633 8.63189C1.39875 5.94241 0.91639 4.43137 0.916275 1.77441Z" fill="url(#paint16_linear_256_740)" stroke="url(#paint17_linear_256_740)" stroke-width="0.05"/>
<path d="M0.889464 1.74219L6.47314 4.19773L5.87423 7.28362C2.54558 6.06964 1.04452 2.90886 0.889464 1.74219Z" fill="url(#paint18_linear_256_740)" stroke="url(#paint19_linear_256_740)" stroke-width="0.05"/>
<path d="M0.925496 6.11915L6.8008 8.01789L7.24337 11.1703C2.76025 9.89256 1.80497 8.62633 0.925496 6.11915Z" fill="url(#paint20_linear_256_740)" stroke="url(#paint21_linear_256_740)" stroke-width="0.05"/>
<path d="M0.889453 6.09705L6.97116 6.56618L7.42732 9.67637C3.88448 9.63249 1.4219 7.14664 0.889453 6.09705Z" fill="url(#paint22_linear_256_740)" stroke="url(#paint23_linear_256_740)" stroke-width="0.05"/>
<path d="M0.928288 9.44796L7.01205 10.5026L7.89304 13.5616C3.27489 12.9262 2.15122 11.8067 0.928288 9.44796Z" fill="url(#paint24_linear_256_740)" stroke="url(#paint25_linear_256_740)" stroke-width="0.05"/>
<path d="M0.889507 9.43114L6.97681 9.04135L7.86533 12.0566C4.35145 12.5108 1.56411 10.3955 0.889507 9.43114Z" fill="url(#paint26_linear_256_740)" stroke="url(#paint27_linear_256_740)" stroke-width="0.05"/>
<path d="M2.47021 12.4864L8.60691 11.8043L10.3053 14.4966C5.693 15.1732 4.30191 14.4111 2.47021 12.4864Z" fill="url(#paint28_linear_256_740)" stroke="url(#paint29_linear_256_740)" stroke-width="0.05"/>
<path d="M2.42825 12.481L8.16592 10.4107L9.85936 13.059C6.61116 14.4742 3.34484 13.2193 2.42825 12.481Z" fill="url(#paint30_linear_256_740)" stroke="url(#paint31_linear_256_740)" stroke-width="0.05"/>
<circle cx="13.0463" cy="8.63179" r="8.10445" fill="#A2B9CB"/>
<circle cx="13.0463" cy="8.63171" r="7.74538" fill="url(#paint32_linear_256_740)" stroke="url(#paint33_linear_256_740)" stroke-width="0.05"/>
<circle cx="13.0463" cy="8.63225" r="6.10764" fill="#C2D0DD" stroke="#DFECF6" stroke-width="0.1"/>
<g filter="url(#filter0_d_256_740)">
<circle cx="13.0463" cy="8.63174" r="5.34952" fill="#E3ECF8"/>
<circle cx="13.0463" cy="8.63174" r="5.29952" stroke="#B9C9D9" stroke-width="0.1"/>
</g>
<g filter="url(#filter1_d_256_740)">
<circle cx="13.0466" cy="1.70335" r="0.44749" fill="#F3F6F8"/>
</g>
<circle cx="13.047" cy="1.70351" r="0.364642" fill="url(#paint34_linear_256_740)"/>
<g filter="url(#filter2_d_256_740)">
<circle cx="13.0466" cy="15.5598" r="0.44749" fill="#F3F6F8"/>
</g>
<circle cx="13.047" cy="15.56" r="0.364642" fill="url(#paint35_linear_256_740)"/>
<g filter="url(#filter3_d_256_740)">
<circle cx="17.9463" cy="3.73246" r="0.44749" transform="rotate(45 17.9463 3.73246)" fill="#F3F6F8"/>
</g>
<circle cx="17.9465" cy="3.73248" r="0.364642" transform="rotate(45 17.9465 3.73248)" fill="url(#paint36_linear_256_740)"/>
<g filter="url(#filter4_d_256_740)">
<circle cx="8.14795" cy="13.5313" r="0.44749" transform="rotate(45 8.14795 13.5313)" fill="#F3F6F8"/>
</g>
<circle cx="8.14819" cy="13.5313" r="0.364642" transform="rotate(45 8.14819 13.5313)" fill="url(#paint37_linear_256_740)"/>
<g filter="url(#filter5_d_256_740)">
<circle cx="19.9749" cy="8.63206" r="0.44749" transform="rotate(90 19.9749 8.63206)" fill="#F3F6F8"/>
</g>
<circle cx="19.9747" cy="8.63222" r="0.364642" transform="rotate(90 19.9747 8.63222)" fill="url(#paint38_linear_256_740)"/>
<g filter="url(#filter6_d_256_740)">
<circle cx="6.11818" cy="8.63206" r="0.44749" transform="rotate(90 6.11818 8.63206)" fill="#F3F6F8"/>
</g>
<circle cx="6.11802" cy="8.63222" r="0.364642" transform="rotate(90 6.11802 8.63222)" fill="url(#paint39_linear_256_740)"/>
<g filter="url(#filter7_d_256_740)">
<circle cx="17.9455" cy="13.5312" r="0.44749" transform="rotate(135 17.9455 13.5312)" fill="#F3F6F8"/>
</g>
<circle cx="17.9453" cy="13.5312" r="0.364642" transform="rotate(135 17.9453 13.5312)" fill="url(#paint40_linear_256_740)"/>
<g filter="url(#filter8_d_256_740)">
<circle cx="8.14743" cy="3.7334" r="0.44749" transform="rotate(135 8.14743 3.7334)" fill="#F3F6F8"/>
</g>
<circle cx="8.14716" cy="3.7334" r="0.364642" transform="rotate(135 8.14716 3.7334)" fill="url(#paint41_linear_256_740)"/>
<path d="M13.3781 5.14498C10.8453 4.9868 10.0373 6.70557 9.94983 7.58472L11.9541 7.73321C11.6425 6.52148 13.3781 6.23297 13.7775 6.68304C14.4699 7.50241 13.5327 7.58472 11.6643 8.61799C10.1696 9.44461 9.89851 10.7063 9.94983 11.2337H15.9286C15.8935 10.5128 16.0605 9.7317 16.1484 9.43129C15.7967 9.5368 13.3055 9.66576 12.1039 9.71705C12.1039 9.51922 14.8296 9.01365 15.6649 8.11243C16.5001 7.2112 16.5441 5.3427 13.3781 5.14498Z" fill="#91A7B8" stroke="#F5FAFF" stroke-width="0.1"/>
<defs>
<filter id="filter0_d_256_740" x="7.49678" y="3.08223" width="11.099" height="11.0992" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.62323 0 0 0 0 0.666031 0 0 0 0 0.704327 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter1_d_256_740" x="12.5091" y="1.22586" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter2_d_256_740" x="12.5091" y="15.0823" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter3_d_256_740" x="17.4088" y="3.25516" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter4_d_256_740" x="7.61044" y="13.054" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter5_d_256_740" x="19.4373" y="8.15457" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter6_d_256_740" x="5.58065" y="8.15457" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter7_d_256_740" x="17.408" y="13.054" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<filter id="filter8_d_256_740" x="7.60995" y="3.25613" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.485484 0 0 0 0 0.56436 0 0 0 0 0.638597 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_740"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_740" result="shape"/>
</filter>
<linearGradient id="paint0_linear_256_740" x1="24.4895" y1="2.15758" x2="21.4798" y2="5.8581" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint1_linear_256_740" x1="23.5584" y1="2.26682" x2="18.4079" y2="5.76594" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint2_linear_256_740" x1="24.4141" y1="2.055" x2="22.3051" y2="6.79212" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint3_linear_256_740" x1="23.3464" y1="2.13492" x2="16.9053" y2="9.9954" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint4_linear_256_740" x1="24.387" y1="6.28398" x2="20.3221" y2="8.77982" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint5_linear_256_740" x1="23.4722" y1="6.07887" x2="17.4539" y2="7.67616" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint6_linear_256_740" x1="24.3499" y1="6.16166" x2="20.7919" y2="9.93379" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint7_linear_256_740" x1="23.3158" y1="5.88369" x2="14.6362" y2="11.1694" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint8_linear_256_740" x1="24.3679" y1="9.51454" x2="19.9927" y2="11.4147" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint9_linear_256_740" x1="23.4909" y1="9.18297" x2="17.308" y2="9.91907" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint10_linear_256_740" x1="24.3483" y1="9.38823" x2="20.2957" y2="12.6232" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint11_linear_256_740" x1="23.3636" y1="8.96777" x2="14.0275" y2="12.9819" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint12_linear_256_740" x1="22.8353" y1="12.3542" x2="18.104" y2="12.9601" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint13_linear_256_740" x1="22.0854" y1="11.7914" x2="15.9422" y2="10.7757" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint14_linear_256_740" x1="22.8517" y1="12.2274" x2="18.0583" y2="14.2051" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint15_linear_256_740" x1="22.0231" y1="11.5492" x2="11.9384" y2="12.8032" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint16_linear_256_740" x1="1.51073" y1="2.15758" x2="4.52046" y2="5.8581" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint17_linear_256_740" x1="2.44188" y1="2.26682" x2="7.5923" y2="5.76594" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint18_linear_256_740" x1="1.58611" y1="2.055" x2="3.69514" y2="6.79212" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint19_linear_256_740" x1="2.65386" y1="2.13492" x2="9.09494" y2="9.9954" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint20_linear_256_740" x1="1.61327" y1="6.28398" x2="5.67813" y2="8.77982" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint21_linear_256_740" x1="2.52809" y1="6.07887" x2="8.54634" y2="7.67616" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint22_linear_256_740" x1="1.65036" y1="6.16166" x2="5.20838" y2="9.93379" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint23_linear_256_740" x1="2.6844" y1="5.88369" x2="11.364" y2="11.1694" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint24_linear_256_740" x1="1.63239" y1="9.51454" x2="6.00753" y2="11.4147" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint25_linear_256_740" x1="2.50933" y1="9.18297" x2="8.69228" y2="9.91907" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint26_linear_256_740" x1="1.65195" y1="9.38823" x2="5.70454" y2="12.6232" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint27_linear_256_740" x1="2.63669" y1="8.96777" x2="11.9727" y2="12.9819" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint28_linear_256_740" x1="3.16499" y1="12.3542" x2="7.89629" y2="12.9601" gradientUnits="userSpaceOnUse">
<stop stop-color="#A7BBCE"/>
<stop offset="1" stop-color="#758695"/>
</linearGradient>
<linearGradient id="paint29_linear_256_740" x1="3.91482" y1="11.7914" x2="10.058" y2="10.7757" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#BFCEDB"/>
</linearGradient>
<linearGradient id="paint30_linear_256_740" x1="3.14855" y1="12.2274" x2="7.94198" y2="14.2051" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#E4EAF0"/>
<stop offset="1" stop-color="#A7BACD"/>
</linearGradient>
<linearGradient id="paint31_linear_256_740" x1="3.97715" y1="11.5492" x2="14.0619" y2="12.8032" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#8699AA"/>
</linearGradient>
<linearGradient id="paint32_linear_256_740" x1="5.27588" y1="0.861328" x2="15.9246" y2="16.4021" gradientUnits="userSpaceOnUse">
<stop offset="0.242116" stop-color="#EEF2F5"/>
<stop offset="0.557692" stop-color="#C8D4DF"/>
<stop offset="1" stop-color="#768797"/>
</linearGradient>
<linearGradient id="paint33_linear_256_740" x1="13.0463" y1="0.861328" x2="13.0463" y2="16.4021" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white" stop-opacity="0.6"/>
</linearGradient>
<linearGradient id="paint34_linear_256_740" x1="13.047" y1="1.33887" x2="13.047" y2="2.06815" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
<linearGradient id="paint35_linear_256_740" x1="13.047" y1="15.1953" x2="13.047" y2="15.9246" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
<linearGradient id="paint36_linear_256_740" x1="17.9465" y1="3.36784" x2="17.9465" y2="4.09712" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
<linearGradient id="paint37_linear_256_740" x1="8.14819" y1="13.1667" x2="8.14819" y2="13.8959" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
<linearGradient id="paint38_linear_256_740" x1="19.9747" y1="8.26758" x2="19.9747" y2="8.99686" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
<linearGradient id="paint39_linear_256_740" x1="6.11802" y1="8.26758" x2="6.11802" y2="8.99686" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
<linearGradient id="paint40_linear_256_740" x1="17.9453" y1="13.1666" x2="17.9453" y2="13.8959" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
<linearGradient id="paint41_linear_256_740" x1="8.14716" y1="3.36876" x2="8.14716" y2="4.09804" gradientUnits="userSpaceOnUse">
<stop stop-color="#DDE4EC"/>
<stop offset="0.688858" stop-color="#B6C7D6"/>
<stop offset="0.985577" stop-color="#9BADBD"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,340 @@
<svg width="26" height="18" viewBox="0 0 26 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.084 1.77441L20.1682 5.51069L20.7939 8.63189C24.6015 5.94241 25.0839 4.43137 25.084 1.77441Z" fill="url(#paint0_linear_256_804)" stroke="url(#paint1_linear_256_804)" stroke-width="0.05"/>
<path d="M25.1108 1.74219L19.5271 4.19773L20.126 7.28362C23.4547 6.06964 24.9557 2.90886 25.1108 1.74219Z" fill="url(#paint2_linear_256_804)" stroke="url(#paint3_linear_256_804)" stroke-width="0.05"/>
<path d="M25.0747 6.11915L19.1994 8.01789L18.7569 11.1703C23.24 9.89256 24.1953 8.62633 25.0747 6.11915Z" fill="url(#paint4_linear_256_804)" stroke="url(#paint5_linear_256_804)" stroke-width="0.05"/>
<path d="M25.1108 6.09705L19.0291 6.56618L18.5729 9.67637C22.1158 9.63249 24.5783 7.14664 25.1108 6.09705Z" fill="url(#paint6_linear_256_804)" stroke="url(#paint7_linear_256_804)" stroke-width="0.05"/>
<path d="M25.072 9.44796L18.9882 10.5026L18.1072 13.5616C22.7254 12.9262 23.849 11.8067 25.072 9.44796Z" fill="url(#paint8_linear_256_804)" stroke="url(#paint9_linear_256_804)" stroke-width="0.05"/>
<path d="M25.1107 9.43114L19.0234 9.04135L18.1349 12.0566C21.6488 12.5108 24.4361 10.3955 25.1107 9.43114Z" fill="url(#paint10_linear_256_804)" stroke="url(#paint11_linear_256_804)" stroke-width="0.05"/>
<path d="M23.53 12.4864L17.3933 11.8043L15.695 14.4966C20.3072 15.1732 21.6983 14.4111 23.53 12.4864Z" fill="url(#paint12_linear_256_804)" stroke="url(#paint13_linear_256_804)" stroke-width="0.05"/>
<path d="M23.572 12.481L17.8343 10.4107L16.1409 13.059C19.3891 14.4742 22.6554 13.2193 23.572 12.481Z" fill="url(#paint14_linear_256_804)" stroke="url(#paint15_linear_256_804)" stroke-width="0.05"/>
<path d="M0.916275 1.77441L5.83203 5.51069L5.20633 8.63189C1.39875 5.94241 0.91639 4.43137 0.916275 1.77441Z" fill="url(#paint16_linear_256_804)" stroke="url(#paint17_linear_256_804)" stroke-width="0.05"/>
<path d="M0.889464 1.74219L6.47314 4.19773L5.87423 7.28362C2.54558 6.06964 1.04452 2.90886 0.889464 1.74219Z" fill="url(#paint18_linear_256_804)" stroke="url(#paint19_linear_256_804)" stroke-width="0.05"/>
<path d="M0.925496 6.11915L6.8008 8.01789L7.24337 11.1703C2.76025 9.89256 1.80497 8.62633 0.925496 6.11915Z" fill="url(#paint20_linear_256_804)" stroke="url(#paint21_linear_256_804)" stroke-width="0.05"/>
<path d="M0.889453 6.09705L6.97116 6.56618L7.42732 9.67637C3.88448 9.63249 1.4219 7.14664 0.889453 6.09705Z" fill="url(#paint22_linear_256_804)" stroke="url(#paint23_linear_256_804)" stroke-width="0.05"/>
<path d="M0.928288 9.44796L7.01205 10.5026L7.89304 13.5616C3.27489 12.9262 2.15122 11.8067 0.928288 9.44796Z" fill="url(#paint24_linear_256_804)" stroke="url(#paint25_linear_256_804)" stroke-width="0.05"/>
<path d="M0.889507 9.43114L6.97681 9.04135L7.86533 12.0566C4.35145 12.5108 1.56411 10.3955 0.889507 9.43114Z" fill="url(#paint26_linear_256_804)" stroke="url(#paint27_linear_256_804)" stroke-width="0.05"/>
<path d="M2.47021 12.4864L8.60691 11.8043L10.3053 14.4966C5.693 15.1732 4.30191 14.4111 2.47021 12.4864Z" fill="url(#paint28_linear_256_804)" stroke="url(#paint29_linear_256_804)" stroke-width="0.05"/>
<path d="M2.42825 12.481L8.16592 10.4107L9.85936 13.059C6.61116 14.4742 3.34484 13.2193 2.42825 12.481Z" fill="url(#paint30_linear_256_804)" stroke="url(#paint31_linear_256_804)" stroke-width="0.05"/>
<circle cx="13.0002" cy="8.63179" r="8.10445" fill="#ED9152"/>
<circle cx="13.0001" cy="8.63171" r="7.74538" fill="url(#paint32_linear_256_804)" stroke="url(#paint33_linear_256_804)" stroke-width="0.05"/>
<circle cx="13.0002" cy="8.63225" r="6.10764" fill="#FFA76C" stroke="#FFC8A3" stroke-width="0.1"/>
<g filter="url(#filter0_d_256_804)">
<circle cx="13.0002" cy="8.63174" r="5.34952" fill="#FFD6BB"/>
<circle cx="13.0002" cy="8.63174" r="5.29952" stroke="#FB9653" stroke-width="0.1"/>
</g>
<g filter="url(#filter1_d_256_804)">
<circle cx="12.9992" cy="1.70335" r="0.44749" fill="#FFEFE4"/>
</g>
<circle cx="12.9997" cy="1.70351" r="0.364642" fill="url(#paint34_linear_256_804)"/>
<g filter="url(#filter2_d_256_804)">
<circle cx="12.9992" cy="15.5598" r="0.44749" fill="#FFEFE4"/>
</g>
<circle cx="12.9997" cy="15.56" r="0.364642" fill="url(#paint35_linear_256_804)"/>
<g filter="url(#filter3_d_256_804)">
<circle cx="17.8989" cy="3.73246" r="0.44749" transform="rotate(45 17.8989 3.73246)" fill="#FFEFE4"/>
</g>
<circle cx="17.8992" cy="3.73248" r="0.364642" transform="rotate(45 17.8992 3.73248)" fill="url(#paint36_linear_256_804)"/>
<g filter="url(#filter4_d_256_804)">
<circle cx="8.10059" cy="13.5313" r="0.44749" transform="rotate(45 8.10059 13.5313)" fill="#FFEFE4"/>
</g>
<circle cx="8.10083" cy="13.5313" r="0.364642" transform="rotate(45 8.10083 13.5313)" fill="url(#paint37_linear_256_804)"/>
<g filter="url(#filter5_d_256_804)">
<circle cx="19.9278" cy="8.63206" r="0.44749" transform="rotate(90 19.9278 8.63206)" fill="#FFEFE4"/>
</g>
<circle cx="19.9276" cy="8.63222" r="0.364642" transform="rotate(90 19.9276 8.63222)" fill="url(#paint38_linear_256_804)"/>
<g filter="url(#filter6_d_256_804)">
<circle cx="6.07106" cy="8.63206" r="0.44749" transform="rotate(90 6.07106 8.63206)" fill="#FFEFE4"/>
</g>
<circle cx="6.07091" cy="8.63222" r="0.364642" transform="rotate(90 6.07091 8.63222)" fill="url(#paint39_linear_256_804)"/>
<g filter="url(#filter7_d_256_804)">
<circle cx="17.8984" cy="13.5312" r="0.44749" transform="rotate(135 17.8984 13.5312)" fill="#FFEFE4"/>
</g>
<circle cx="17.8981" cy="13.5312" r="0.364642" transform="rotate(135 17.8981 13.5312)" fill="url(#paint40_linear_256_804)"/>
<g filter="url(#filter8_d_256_804)">
<circle cx="8.10031" cy="3.7334" r="0.44749" transform="rotate(135 8.10031 3.7334)" fill="#FFEFE4"/>
</g>
<circle cx="8.10004" cy="3.7334" r="0.364642" transform="rotate(135 8.10004 3.7334)" fill="url(#paint41_linear_256_804)"/>
<g filter="url(#filter9_d_256_804)">
<path d="M13.4399 5.42274C10.6997 5.36508 9.79097 6.67641 9.67915 7.33927C10.3804 7.41653 11.245 7.74484 11.5896 7.89933C11.8814 6.92168 13.5449 6.61525 14.0118 7.2427C14.3854 7.74466 12.932 7.76314 12.1587 7.70964V9.21259C12.5636 9.05887 13.6309 9.23609 14.114 9.30015C14.5809 9.66494 14.1577 10.0151 12.9904 10.0881C12.0565 10.1465 11.6674 9.6163 11.5896 9.34392C11.4962 9.46066 10.2276 9.77195 9.60498 9.913C9.67308 10.4918 10.4046 11.6845 12.786 11.8245C15.1674 11.9646 15.5374 11.2358 16.1567 10.5842C16.9855 8.73982 15.4417 8.43436 14.5662 8.51218C16.7842 8.24953 16.8652 5.49481 13.4399 5.42274Z" fill="#E18546"/>
<path d="M13.4399 5.42274C10.6997 5.36508 9.79097 6.67641 9.67915 7.33927C10.3804 7.41653 11.245 7.74484 11.5896 7.89933C11.8814 6.92168 13.5449 6.61525 14.0118 7.2427C14.3854 7.74466 12.932 7.76314 12.1587 7.70964V9.21259C12.5636 9.05887 13.6309 9.23609 14.114 9.30015C14.5809 9.66494 14.1577 10.0151 12.9904 10.0881C12.0565 10.1465 11.6674 9.6163 11.5896 9.34392C11.4962 9.46066 10.2276 9.77195 9.60498 9.913C9.67308 10.4918 10.4046 11.6845 12.786 11.8245C15.1674 11.9646 15.5374 11.2358 16.1567 10.5842C16.9855 8.73982 15.4417 8.43436 14.5662 8.51218C16.7842 8.24953 16.8652 5.49481 13.4399 5.42274Z" stroke="#FFECDE" stroke-width="0.1"/>
</g>
<defs>
<filter id="filter0_d_256_804" x="7.45063" y="3.08223" width="11.099" height="11.0992" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.909649 0 0 0 0 0.572 0 0 0 0 0.341785 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter1_d_256_804" x="12.4618" y="1.22586" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter2_d_256_804" x="12.4618" y="15.0823" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter3_d_256_804" x="17.3614" y="3.25516" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter4_d_256_804" x="7.56308" y="13.054" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter5_d_256_804" x="19.3902" y="8.15457" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter6_d_256_804" x="5.53354" y="8.15457" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter7_d_256_804" x="17.3609" y="13.054" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter8_d_256_804" x="7.56283" y="3.25613" width="1.07502" height="1.07453" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.06"/>
<feGaussianBlur stdDeviation="0.045"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.947172 0 0 0 0 0.59246 0 0 0 0 0.346029 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<filter id="filter9_d_256_804" x="9.35005" y="5.17109" width="7.29355" height="6.92051" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.939547 0 0 0 0 0.555727 0 0 0 0 0.294032 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_256_804"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_256_804" result="shape"/>
</filter>
<linearGradient id="paint0_linear_256_804" x1="24.4895" y1="2.15758" x2="21.4798" y2="5.8581" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint1_linear_256_804" x1="23.5584" y1="2.26682" x2="18.4079" y2="5.76594" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint2_linear_256_804" x1="24.4141" y1="2.055" x2="22.3051" y2="6.79212" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint3_linear_256_804" x1="23.3464" y1="2.13492" x2="16.9053" y2="9.9954" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint4_linear_256_804" x1="24.387" y1="6.28398" x2="20.3221" y2="8.77982" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint5_linear_256_804" x1="23.4722" y1="6.07887" x2="17.4539" y2="7.67616" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint6_linear_256_804" x1="24.3499" y1="6.16166" x2="20.7919" y2="9.93379" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint7_linear_256_804" x1="23.3158" y1="5.88369" x2="14.6362" y2="11.1694" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint8_linear_256_804" x1="24.3679" y1="9.51454" x2="19.9927" y2="11.4147" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint9_linear_256_804" x1="23.4909" y1="9.18297" x2="17.308" y2="9.91907" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint10_linear_256_804" x1="24.3483" y1="9.38823" x2="20.2957" y2="12.6232" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint11_linear_256_804" x1="23.3636" y1="8.96777" x2="14.0275" y2="12.9819" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint12_linear_256_804" x1="22.8353" y1="12.3542" x2="18.104" y2="12.9601" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint13_linear_256_804" x1="22.0854" y1="11.7914" x2="15.9422" y2="10.7757" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint14_linear_256_804" x1="22.8517" y1="12.2274" x2="18.0583" y2="14.2051" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint15_linear_256_804" x1="22.0231" y1="11.5492" x2="11.9384" y2="12.8032" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint16_linear_256_804" x1="1.51073" y1="2.15758" x2="4.52046" y2="5.8581" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint17_linear_256_804" x1="2.44188" y1="2.26682" x2="7.5923" y2="5.76594" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint18_linear_256_804" x1="1.58611" y1="2.055" x2="3.69514" y2="6.79212" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint19_linear_256_804" x1="2.65386" y1="2.13492" x2="9.09494" y2="9.9954" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint20_linear_256_804" x1="1.61327" y1="6.28398" x2="5.67813" y2="8.77982" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint21_linear_256_804" x1="2.52809" y1="6.07887" x2="8.54634" y2="7.67616" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint22_linear_256_804" x1="1.65036" y1="6.16166" x2="5.20838" y2="9.93379" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint23_linear_256_804" x1="2.6844" y1="5.88369" x2="11.364" y2="11.1694" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint24_linear_256_804" x1="1.63239" y1="9.51454" x2="6.00753" y2="11.4147" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint25_linear_256_804" x1="2.50933" y1="9.18297" x2="8.69228" y2="9.91907" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint26_linear_256_804" x1="1.65195" y1="9.38823" x2="5.70454" y2="12.6232" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint27_linear_256_804" x1="2.63669" y1="8.96777" x2="11.9727" y2="12.9819" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint28_linear_256_804" x1="3.16499" y1="12.3542" x2="7.89629" y2="12.9601" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9944F"/>
<stop offset="1" stop-color="#BD642B"/>
</linearGradient>
<linearGradient id="paint29_linear_256_804" x1="3.91482" y1="11.7914" x2="10.058" y2="10.7757" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F7D6BE"/>
</linearGradient>
<linearGradient id="paint30_linear_256_804" x1="3.14855" y1="12.2274" x2="7.94198" y2="14.2051" gradientUnits="userSpaceOnUse">
<stop offset="0.442308" stop-color="#FFD9BF"/>
<stop offset="1" stop-color="#ED9051"/>
</linearGradient>
<linearGradient id="paint31_linear_256_804" x1="3.97715" y1="11.5492" x2="14.0619" y2="12.8032" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FEEEE1"/>
</linearGradient>
<linearGradient id="paint32_linear_256_804" x1="5.22974" y1="0.861328" x2="15.8785" y2="16.4021" gradientUnits="userSpaceOnUse">
<stop offset="0.242116" stop-color="#FFEBDE"/>
<stop offset="0.557692" stop-color="#FFC59E"/>
<stop offset="1" stop-color="#CD7337"/>
</linearGradient>
<linearGradient id="paint33_linear_256_804" x1="13.0001" y1="0.861328" x2="13.0001" y2="16.4021" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white" stop-opacity="0.6"/>
</linearGradient>
<linearGradient id="paint34_linear_256_804" x1="12.9997" y1="1.33887" x2="12.9997" y2="2.06815" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
<linearGradient id="paint35_linear_256_804" x1="12.9997" y1="15.1953" x2="12.9997" y2="15.9246" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
<linearGradient id="paint36_linear_256_804" x1="17.8992" y1="3.36784" x2="17.8992" y2="4.09712" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
<linearGradient id="paint37_linear_256_804" x1="8.10083" y1="13.1667" x2="8.10083" y2="13.8959" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
<linearGradient id="paint38_linear_256_804" x1="19.9276" y1="8.26758" x2="19.9276" y2="8.99686" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
<linearGradient id="paint39_linear_256_804" x1="6.0709" y1="8.26758" x2="6.0709" y2="8.99686" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
<linearGradient id="paint40_linear_256_804" x1="17.8981" y1="13.1666" x2="17.8981" y2="13.8959" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
<linearGradient id="paint41_linear_256_804" x1="8.10004" y1="3.36876" x2="8.10004" y2="4.09804" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD3B5"/>
<stop offset="0.688858" stop-color="#FEA567"/>
<stop offset="0.985577" stop-color="#E17C39"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

27324
src/assets/maps/china.json Normal file

File diff suppressed because it is too large Load Diff

24
src/assets/play.svg Normal file
View File

@ -0,0 +1,24 @@
<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_261_2700)">
<circle cx="21.0001" cy="19" r="17" fill="white"/>
</g>
<circle cx="21.0001" cy="19" r="14" fill="url(#paint0_linear_261_2700)"/>
<path d="M17.5001 24.8565V13.1435C17.5001 12.7526 17.9284 12.5129 18.2616 12.7173L27.8056 18.5738C28.1236 18.769 28.1236 19.231 27.8056 19.4262L18.2616 25.2827C17.9284 25.4871 17.5001 25.2474 17.5001 24.8565Z" fill="white"/>
<defs>
<filter id="filter0_d_261_2700" x="6.10352e-05" y="0" width="42" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.499541 0 0 0 0 0.38654 0 0 0 0 0.996056 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_261_2700"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_261_2700" result="shape"/>
</filter>
<linearGradient id="paint0_linear_261_2700" x1="7.00006" y1="19" x2="35.0001" y2="19" gradientUnits="userSpaceOnUse">
<stop stop-color="#E96B5D"/>
<stop offset="0.5" stop-color="#7F63FE"/>
<stop offset="1" stop-color="#459DDC"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,52 @@
<!--
* @Author: 田鑫
* @Date: 2023-03-05 18:14:16
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 19:17:52
* @Description:
-->
<script lang="ts" setup>
import type { RouteLocationNormalized } from 'vue-router';
const router = useRouter();
const route = useRoute();
const matched = computed(() => {
if (route.matched.length === 1 && route.matched[0].path === '/') {
return [];
} else {
return route.matched.reduce((t: RouteLocationNormalized[], o) => {
const isExist = t.find((c) => c.name === o.name);
return isExist ? t : [...t, router.resolve(o)];
}, []);
}
});
</script>
<template>
<view></view>
<!-- <a-breadcrumb class="container-breadcrumb">
<a-breadcrumb-item v-for="{ meta, name } in matched" :key="name">
<router-link v-slot="{ href, navigate }" :to="{ name }" custom>
<a-link v-if="meta.needNavigate" :href="href" @click="navigate">{{
meta.locale ? meta.locale : '主页'
}}</a-link>
<a-link v-else disabled>{{ meta.locale ? meta.locale : '主页' }}</a-link>
</router-link>
</a-breadcrumb-item>
</a-breadcrumb> -->
</template>
<style scoped lang="less">
.container-breadcrumb {
margin: 16px 0;
:deep(.arco-breadcrumb-item) {
> a {
color: rgb(var(--gray-6));
}
&:last-child {
color: rgb(var(--gray-8));
}
}
}
</style>

View File

@ -0,0 +1,5 @@
export { default as Navbar } from './navbar/index.vue';
export { default as Menu } from './menu/index.vue';
export { default as TabBar } from './tab-bar/index.vue';
export { default as Breadcrumb } from './breadcrumb/index.vue';
export { default as ModalSimple } from './modal/index.vue';

View File

@ -0,0 +1,140 @@
<script lang="tsx">
import type { RouteMeta, RouteRecordRaw } from 'vue-router';
import { useAppStore } from '@/stores';
import { listenerRouteChange } from '@/utils/route-listener';
import { openWindow, regexUrl } from '@/utils';
import useMenuTree from './use-menu-tree';
export default defineComponent({
emit: ['collapse'],
setup() {
const appStore = useAppStore();
const router = useRouter();
const route = useRoute();
const { menuTree } = useMenuTree();
const collapsed = computed({
get() {
if (appStore.device === 'desktop') return appStore.menuCollapse;
return false;
},
set(value: boolean) {
appStore.updateSettings({ menuCollapse: value });
},
});
const topMenu = computed(() => appStore.topMenu);
const openKeys = ref<string[]>([]);
const selectedKey = ref<string[]>([]);
const goto = (item: RouteRecordRaw) => {
// Open external link
if (regexUrl.test(item.path)) {
openWindow(item.path);
selectedKey.value = [item.name as string];
return;
}
// Eliminate external link side effects
const { hideInMenu, activeMenu } = item.meta as RouteMeta;
if (route.name === item.name && !hideInMenu && !activeMenu) {
selectedKey.value = [item.name as string];
return;
}
// Trigger router change
router.push({
name: item.name,
});
};
const findMenuOpenKeys = (target: string) => {
const result: string[] = [];
let isFind = false;
const backtrack = (item: RouteRecordRaw, keys: string[]) => {
if (item.name === target) {
isFind = true;
result.push(...keys);
return;
}
if (item.children?.length) {
item.children.forEach((el) => {
backtrack(el, [...keys, el.name as string]);
});
}
};
menuTree.value.forEach((el: RouteRecordRaw) => {
if (isFind) return; // Performance optimization
backtrack(el, [el.name as string]);
});
return result;
};
listenerRouteChange((newRoute) => {
const { requiresAuth, activeMenu, hideInMenu } = newRoute.meta;
if (requiresAuth && (!hideInMenu || activeMenu)) {
const menuOpenKeys = findMenuOpenKeys((activeMenu || newRoute.name) as string);
const keySet = new Set([...menuOpenKeys, ...openKeys.value]);
openKeys.value = [...keySet];
selectedKey.value = [activeMenu || menuOpenKeys[menuOpenKeys.length - 1]];
}
}, true);
const setCollapse = (val: boolean) => {
if (appStore.device === 'desktop') appStore.updateSettings({ menuCollapse: val });
};
const renderSubMenu = () => {
function travel(_route: RouteRecordRaw[], nodes = []) {
if (_route) {
_route.forEach((element) => {
// This is demo, modify nodes as needed
const icon = element?.meta?.icon ? () => h(element?.meta?.icon as object) : null;
const node =
element?.children && element?.children.length !== 0 ? (
<a-sub-menu
key={element?.name}
v-slots={{
icon,
title: () => element?.meta?.locale || '',
}}
>
{travel(element?.children)}
</a-sub-menu>
) : (
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={() => goto(element)}>
{element?.meta?.locale || ''}
</a-menu-item>
);
nodes.push(node as never);
});
}
return nodes;
}
return travel(menuTree.value);
};
return () => (
<a-menu
mode={topMenu.value ? 'horizontal' : 'vertical'}
v-model:collapsed={collapsed.value}
v-model:open-keys={openKeys.value}
show-collapse-button={appStore.device !== 'mobile'}
auto-open={false}
selected-keys={selectedKey.value}
auto-open-selected={true}
level-indent={34}
style="height: 100%;width:100%;"
onCollapse={setCollapse}
>
{renderSubMenu()}
</a-menu>
);
},
});
</script>
<style lang="less" scoped>
:deep(.arco-menu-inner) {
.arco-menu-inline-header {
display: flex;
align-items: center;
}
.arco-icon {
&:not(.arco-icon-down) {
font-size: 18px;
}
}
}
</style>

View File

@ -0,0 +1,60 @@
import type { RouteRecordRaw, RouteRecordNormalized } from 'vue-router';
import { useAppStore } from '@/stores';
import appClientMenus from '@/router/app-menus';
export default function useMenuTree() {
const appStore = useAppStore();
const appRoute = computed(() => {
if (appStore.menuFromServer) {
// return appClientMenus.concat(toRaw(appStore.appAsyncMenus));
return toRaw(appStore.appAsyncMenus);
}
return appClientMenus;
});
const menuTree = computed(() => {
const copyRouter = cloneDeep(appRoute.value) as RouteRecordNormalized[];
copyRouter.sort((a: RouteRecordNormalized, b: RouteRecordNormalized) => {
return (a.meta.order || 0) - (b.meta.order || 0);
});
function travel(_routes: RouteRecordRaw[], layer: number) {
if (!_routes) return null;
const collector: any = _routes.map((element) => {
// leaf node
if (element.meta?.hideChildrenInMenu || !element.children) {
element.children = [];
return element;
}
// route filter hideInMenu true
element.children = element.children.filter((x) => x.meta?.hideInMenu !== true);
// Associated child node
const subItem = travel(element.children, layer + 1);
if (subItem.length) {
element.children = subItem;
return element;
}
// the else logic
if (layer > 1) {
element.children = subItem;
return element;
}
if (element.meta?.hideInMenu === false) {
return element;
}
return null;
});
return collector.filter(Boolean);
}
return travel(copyRouter, 0);
});
return {
menuTree,
};
}

View File

@ -0,0 +1,32 @@
<script setup lang="ts">
import type { Component, DefineComponent } from 'vue';
import IconHover from '@arco-design/web-vue/es/_components/icon-hover';
defineProps<{
title?: string;
content?: string | (() => DefineComponent | Component);
}>();
defineEmits(['close']);
</script>
<template>
<slot name="header">
<div class="flex justify-end mb7">
<slot name="close">
<icon-hover @click="$emit('close')">
<icon-close />
</icon-hover>
</slot>
</div>
</slot>
<slot>
<div class="flex flex-col text-center">
<div v-if="title" class="mb4 text-lg font-600">{{ title }}</div>
<template v-else />
<component :is="content" v-if="typeof content === 'function'" />
<div v-else>{{ content }}</div>
</div>
</slot>
</template>

View File

@ -0,0 +1,114 @@
<script lang="ts" setup>
import { useAppStore } from '@/stores';
import { IconExport, IconFile, IconCaretDown } from '@arco-design/web-vue/es/icon';
import { fetchMenusTree } from '@/api/all';
const lists = ref([]);
const getMenus = async () => {
const res = await fetchMenusTree();
lists.value = res;
};
onMounted(() => {
getMenus();
});
const appStore = useAppStore();
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
const avatar = computed(
() => '//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image',
);
const topMenu = computed(() => appStore.topMenu && appStore.menu);
const toggleDrawerMenu = inject('toggleDrawerMenu') as () => void;
function setServerMenu() {
appStore.fetchServerMenuConfig();
console.log(appStore.serverMenu);
}
const handleSelect = (index: any) => {
console.log(index);
};
</script>
<template>
<div class="navbar">
<div class="left-side">
<a-space>
<img src="@/assets/LOGO.svg" alt="" />
</a-space>
</div>
<div class="center-side">
<div class="menu-demo">
<a-menu mode="horizontal" :default-selected-keys="['1']">
<a-menu-item :key="'1'">
<view>工作台</view>
</a-menu-item>
<a-menu-item v-for="(item, index) in lists" :key="index + 2">
<a-dropdown @select="handleSelect" :popup-max-height="false">
<a-button>{{ item.name }}<icon-caret-down /></a-button>
<template #content>
<a-doption v-for="(child, index) in item.children" :key="index">{{ child.name }}</a-doption>
</template>
</a-dropdown>
</a-menu-item>
</a-menu>
</div>
</div>
<ul class="right-side">
<li>
<a-dropdown trigger="click">
<a-avatar class="cursor-pointer" :size="32">
<img alt="avatar" :src="avatar" />
</a-avatar>
</a-dropdown>
</li>
</ul>
</div>
</template>
<style scoped lang="less">
.navbar {
display: flex;
justify-content: space-between;
height: 100%;
background-color: var(--color-bg-2);
border-bottom: 1px solid var(--color-border);
}
.left-side {
display: flex;
align-items: center;
padding-left: 20px;
}
.center-side {
flex: 1;
display: flex;
align-items: center;
margin-left: 40px;
}
.cneter-tip {
font-size: 16px;
font-weight: 400;
color: var(--color-text-1);
}
.menu-demo {
flex: 1;
}
.right-side {
display: flex;
padding-right: 20px;
list-style: none;
li {
display: flex;
align-items: center;
padding: 0 10px;
}
a {
color: var(--color-text-1);
text-decoration: none;
}
.nav-btn {
border-color: rgb(var(--gray-2));
color: rgb(var(--gray-8));
font-size: 16px;
}
}
</style>

View File

@ -0,0 +1,83 @@
<script lang="ts" setup>
import type { RouteLocationNormalized } from 'vue-router';
import { listenerRouteChange, removeRouteListener } from '@/utils/route-listener';
import { useAppStore, useTabBarStore } from '@/stores';
import TabItem from './tab-item.vue';
const appStore = useAppStore();
const tabBarStore = useTabBarStore();
const affixRef = ref();
const tagList = computed(() => {
return tabBarStore.getTabList;
});
const offsetTop = computed(() => {
return appStore.navbar ? 60 : 0;
});
watch(
() => appStore.navbar,
() => {
affixRef.value.updatePosition();
},
);
listenerRouteChange((route: RouteLocationNormalized) => {
if (!route.meta.noAffix && !tagList.value.some((tag) => tag.fullPath === route.fullPath)) {
tabBarStore.updateTabList(route);
}
}, true);
onUnmounted(() => {
removeRouteListener();
});
</script>
<template>
<div class="tab-bar-container">
<a-affix ref="affixRef" :offset-top="offsetTop">
<div class="tab-bar-box">
<div class="tab-bar-scroll">
<div class="tags-wrap">
<tab-item v-for="(tag, index) in tagList" :key="tag.fullPath" :index="index" :item-data="tag" />
</div>
</div>
<div class="tag-bar-operation"></div>
</div>
</a-affix>
</div>
</template>
<style scoped lang="less">
.tab-bar-container {
position: relative;
background-color: var(--color-bg-2);
.tab-bar-box {
display: flex;
padding: 0 0 0 20px;
background-color: var(--color-bg-2);
border-bottom: 1px solid var(--color-border);
.tab-bar-scroll {
height: 32px;
flex: 1;
overflow: hidden;
.tags-wrap {
padding: 4px 0;
height: 48px;
white-space: nowrap;
overflow-x: auto;
:deep(.arco-tag) {
display: inline-flex;
align-items: center;
margin-right: 6px;
cursor: pointer;
&:first-child {
.arco-tag-close-btn {
display: none;
}
}
}
}
}
}
.tag-bar-operation {
width: 100px;
height: 32px;
}
}
</style>

View File

@ -0,0 +1,177 @@
<script lang="ts" setup>
import type { PropType } from 'vue';
import type { TagProps } from '@/stores/modules/tab-bar/types';
import { useTabBarStore } from '@/stores';
import { DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants';
const props = defineProps({
itemData: {
type: Object as PropType<TagProps>,
default() {
return [];
},
},
index: {
type: Number,
default: 0,
},
});
// eslint-disable-next-line no-shadow
enum Eaction {
reload = 'reload',
current = 'current',
left = 'left',
right = 'right',
others = 'others',
all = 'all',
}
const router = useRouter();
const route = useRoute();
const tabBarStore = useTabBarStore();
const goto = (tag: TagProps) => {
router.push({ ...tag });
};
const tagList = computed(() => {
return tabBarStore.getTabList;
});
const disabledReload = computed(() => {
return props.itemData.fullPath !== route.fullPath;
});
const disabledCurrent = computed(() => {
return props.index === 0;
});
const disabledLeft = computed(() => {
return [0, 1].includes(props.index);
});
const disabledRight = computed(() => {
return props.index === tagList.value.length - 1;
});
const tagClose = (tag: TagProps, idx: number) => {
tabBarStore.deleteTag(idx, tag);
if (props.itemData.fullPath === route.fullPath) {
const latest = tagList.value[idx - 1]; // 获取队列的前一个tab
router.push({ name: latest.name });
}
};
const findCurrentRouteIndex = () => {
return tagList.value.findIndex((el) => el.fullPath === route.fullPath);
};
const actionSelect = async (value: any) => {
const { itemData, index } = props;
const copyTagList = [...tagList.value];
if (value === Eaction.current) {
tagClose(itemData, index);
} else if (value === Eaction.left) {
const currentRouteIdx = findCurrentRouteIndex();
copyTagList.splice(1, props.index - 1);
tabBarStore.freshTabList(copyTagList);
if (currentRouteIdx < index) {
router.push({ name: itemData.name });
}
} else if (value === Eaction.right) {
const currentRouteIdx = findCurrentRouteIndex();
copyTagList.splice(props.index + 1);
tabBarStore.freshTabList(copyTagList);
if (currentRouteIdx > index) {
router.push({ name: itemData.name });
}
} else if (value === Eaction.others) {
const filterList = tagList.value.filter((el, idx) => {
return idx === 0 || idx === props.index;
});
tabBarStore.freshTabList(filterList);
router.push({ name: itemData.name });
} else if (value === Eaction.reload) {
tabBarStore.deleteCache(itemData);
await router.push({
name: REDIRECT_ROUTE_NAME,
params: {
path: route.fullPath,
},
});
tabBarStore.addCache(itemData.name);
} else {
tabBarStore.resetTabList();
router.push({ name: DEFAULT_ROUTE_NAME });
}
};
</script>
<template>
<a-dropdown trigger="contextMenu" :popup-max-height="false" @select="actionSelect">
<span
:class="[
'arco-tag arco-tag-size-medium arco-tag-checked',
{ 'link-activated': itemData.fullPath === $route.fullPath },
]"
@click="goto(itemData)"
>
<span class="tag-link">{{ itemData.title }}</span>
<span
class="arco-icon-hover arco-tag-icon-hover arco-icon-hover-size-medium arco-tag-close-btn"
@click.stop="tagClose(itemData, index)"
>
<icon-close />
</span>
</span>
<template #content>
<a-doption :disabled="disabledReload" :value="Eaction.reload">
<icon-refresh />
<span>重新加载</span>
</a-doption>
<a-doption class="sperate-line" :disabled="disabledCurrent" :value="Eaction.current">
<icon-close />
<span>关闭当前标签页</span>
</a-doption>
<a-doption :disabled="disabledLeft" :value="Eaction.left">
<icon-to-left />
<span>关闭左侧标签页</span>
</a-doption>
<a-doption class="sperate-line" :disabled="disabledRight" :value="Eaction.right">
<icon-to-right />
<span>关闭右侧标签页</span>
</a-doption>
<a-doption :value="Eaction.others">
<icon-swap />
<span>关闭其它标签页</span>
</a-doption>
<a-doption :value="Eaction.all">
<icon-folder-delete />
<span>关闭全部标签页</span>
</a-doption>
</template>
</a-dropdown>
</template>
<style scoped lang="less">
.tag-link {
color: var(--color-text-2);
text-decoration: none;
}
.link-activated {
color: rgb(var(--link-6));
.tag-link {
color: rgb(var(--link-6));
}
& + .arco-tag-close-btn {
color: rgb(var(--link-6));
}
}
:deep(.arco-dropdown-option-content) {
span {
margin-left: 10px;
}
}
.arco-dropdown-open {
.tag-link {
color: rgb(var(--danger-6));
}
.arco-tag-close-btn {
color: rgb(var(--danger-6));
}
}
.sperate-line {
border-bottom: 1px solid var(--color-neutral-3);
}
</style>

View File

@ -0,0 +1,70 @@
<!--
* @Author: 田鑫
* @Date: 2023-02-16 11:58:01
* @LastEditors: 田鑫
* @LastEditTime: 2023-02-16 16:56:27
* @Description: 二次确认框
-->
<template>
<div>
<a-popconfirm
:content="content"
:position="position"
:ok-text="okText"
:cancel-text="cancelText"
:type="popupType"
@ok="handleConfirm"
@cancel="handleCancel"
>
<slot></slot>
</a-popconfirm>
</div>
</template>
<script setup lang="ts">
import type { PropType } from 'vue-demi';
type Position = 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'left' | 'lt' | 'lb' | 'right' | 'rt' | 'rb';
type PopupType = 'info' | 'success' | 'warning' | 'error';
const props = defineProps({
content: {
type: String,
default: '是否确认?',
},
position: {
type: String as PropType<Position>,
default: 'top',
},
okText: {
type: String,
default: '确定',
},
cancelText: {
type: String,
default: '取消',
},
popupType: {
type: String as PropType<PopupType>,
default: 'info',
},
});
const emit = defineEmits(['confirmEmit', 'cancelEmit']);
/**
* 确定事件
*/
function handleConfirm() {
emit('confirmEmit');
}
/**
* 确定事件
*/
function handleCancel() {
emit('cancelEmit');
}
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,5 @@
# 动态配置form表单
示例见 views/components/form
参数 fieldList配置项包括arco.design Form.Item和Input、Select以及自定义属性component等
参数 model: 传默认值
通过ref获取实例调用子组件实例updateFieldsList方法更新配置项调用setModel方法更新数据调用setForm方法更新Form属性自定义事件change处理逻辑

View File

@ -0,0 +1,40 @@
// form.item的属性名称集合
export const formItemKeys = [
'field',
'label',
'tooltip',
'showColon',
'noStyle',
'disabled',
'help',
'extra',
'required',
'asteriskPosition',
'rules',
'validateStatus',
'validateTrigger',
'wrapperColProps',
'hideLabel',
'hideAsterisk',
'labelColStyle',
'wrapperColStyle',
'rowProps',
'rowClass',
'contentClass',
'contentFlex',
'labelColFlex',
'feedback',
'labelComponent',
'labelAttrs',
];
// 自定义属性名称集合
export const customKeys = ['component', 'lists'];
// 响应式栅格默认配置
export const COL_PROPS = {
xs: 12,
sm: 12,
md: 8,
lg: 8,
xl: 6,
xxl: 6,
};

View File

@ -0,0 +1,271 @@
<template>
<slot name="header"></slot>
<a-form v-bind="_options" ref="formRef" :model="model" @submit.prevent>
<a-row v-bind="rowProps" :gutter="20">
<template
v-for="{ field, component, formItemProps, componentProps, lists, colProps } in newFieldList"
:key="field"
>
<!-- 单选框 -->
<a-col v-if="component === 'radio'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-radio-group v-bind="componentProps" v-model="model[field]">
<a-radio v-for="val in lists" :key="val['value']" :label="val['value']" size="large">
{{ val['label'] }}
</a-radio>
</a-radio-group>
</a-form-item>
</a-col>
<!-- 复选框 -->
<a-col v-if="component === 'checkbox'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-checkbox-group v-bind="componentProps" v-model="model[field]">
<a-checkbox v-for="c in lists" :key="c['value']" :label="c['value']">{{ c['label'] }}</a-checkbox>
</a-checkbox-group>
</a-form-item>
</a-col>
<!-- 下拉框 -->
<a-col v-if="component === 'select'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-select v-bind="componentProps" v-model="model[field]">
<a-option v-for="s in lists" :key="s['value']" :label="s['label']" :value="s['value']" />
</a-select>
</a-form-item>
</a-col>
<!-- 文本域 -->
<a-col v-if="component === 'textarea'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-textarea v-bind="componentProps" v-model="model[field]" />
</a-form-item>
</a-col>
<!-- 时间选择器 -->
<a-col v-if="component === 'time'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-time-picker v-bind="componentProps" v-model="model[field]" />
</a-form-item>
</a-col>
<!-- 日期选择器 -->
<a-col v-if="component === 'date'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-date-picker v-bind="componentProps" v-model="model[field]" />
</a-form-item>
</a-col>
<!-- 日期范围选择器 -->
<a-col v-if="component === 'rangeDate'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-range-picker v-bind="componentProps" v-model="model[field]" />
</a-form-item>
</a-col>
<!-- 级联选择器 -->
<a-col v-if="component === 'cascader'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-cascader v-bind="componentProps" v-model="model[field]" />
</a-form-item>
</a-col>
<!-- 数字输入框 -->
<a-col v-if="component === 'inputNumber'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-input-number v-bind="componentProps" v-model="model[field]" />
</a-form-item>
</a-col>
<!-- 输入框 -->
<a-col v-if="component === 'input'" v-bind="colProps">
<a-form-item v-bind="formItemProps">
<a-input v-bind="componentProps" v-model="model[field]" />
</a-form-item>
</a-col>
<!-- 标题模块 -->
<a-col v-if="component === 'title'" :span="24">
<div class="title">
<div class="bar"></div>
<h4 class="text">{{ formItemProps.label }}</h4>
</div>
</a-col>
<!-- 自定义插槽slot -->
<a-col v-if="component === 'slot'" :span="24">
<slot :name="field"></slot>
</a-col>
</template>
<a-col :span="24">
<a-form-item>
<slot name="buttons" :model="model" :formRef="formRef">
<a-space>
<a-button type="primary" @click="onSubmit(formRef)">{{ _options.submitButtonText }}</a-button>
<a-button v-if="_options.showResetButton" @click="resetForm(formRef)">
{{ _options.resetButtonText }}
</a-button>
<a-button v-if="_options.showCancelButton" @click="emit('cancel')">
{{ _options.cancelButtonText }}
</a-button>
</a-space>
</slot>
</a-form-item>
</a-col>
</a-row>
</a-form>
<slot name="footer"></slot>
</template>
<script lang="ts" setup>
import type { FormInstance, RowProps, ValidatedError } from '@arco-design/web-vue';
import type { ComputedRef } from 'vue';
import type { Form } from './interface';
import { formItemKeys, customKeys, COL_PROPS } from './constants';
import { changeFormList } from './utils';
import type { FieldData } from '@arco-design/web-vue/es/form/interface';
// 父组件传递的值
interface Props {
fieldList: Form.FieldItem[];
model?: Record<string, any>;
options?: Form.Options;
rowProps?: RowProps;
}
interface EmitEvent {
(e: 'submit' | 'change', params: any): void;
(e: 'reset' | 'cancel'): void;
}
const props = defineProps<Props>();
const emit = defineEmits<EmitEvent>();
// 表单的数据
let model = ref<Record<string, any>>({});
const formRef = ref<FormInstance>();
// 初始化处理Form组件属性options
const _options = ref<Record<string, any>>({});
const initOptions = () => {
const option = {
layout: 'vertical',
disabled: false,
submitButtonText: '提交',
resetButtonText: '重置',
cancelButtonText: '取消',
showResetButton: true,
};
Object.assign(option, props?.options);
_options.value = option;
};
initOptions();
// 初始化处理model
const initFormModel = () => {
props.fieldList.forEach((item: Form.FieldItem) => {
// 如果类型为checkbox默认值需要设置一个空数组
const value = item.component === 'checkbox' ? [] : '';
const { field, component } = item;
if (component !== 'slot' && component !== 'title') {
model.value[item.field] = props?.model?.[field] || value;
}
});
};
initFormModel();
// 初始化处理fieldList
const newFieldList: any = ref(null);
const initFieldList = () => {
const list = props?.fieldList.map((item: Form.FieldItem) => {
const customProps = pick(item, customKeys);
const formItemProps = pick(item, formItemKeys);
const componentProps = omit(item, [...formItemKeys, ...customKeys, 'field', 'colProps']);
const { colProps = {}, field, placeholder, component = 'input', label } = item;
componentProps.onChange = (val: any) => onChange(field, val);
const newColProps = {
...colProps,
...COL_PROPS,
};
const obj = {
field,
colProps: newColProps,
...customProps,
formItemProps,
componentProps,
};
if ((component === 'input' || component === 'textarea') && !placeholder) {
componentProps.placeholder = `请输入${label}`;
}
if (component === 'select' && !placeholder) {
componentProps.placeholder = `请选择${label}`;
}
if (component === 'rangeDate') {
componentProps.value = [null, null];
}
return obj;
});
newFieldList.value = list;
return list;
};
initFieldList();
// 提交
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
let flag = false;
await formEl.validate((errors: undefined | Record<string, ValidatedError>) => {
if (!errors) {
emit('submit', model.value);
flag = true;
} else {
return false;
}
});
return (flag && model.value) || null;
};
// 提交--父组件调用
const submit = () => {
return onSubmit(formRef.value);
};
// 重置
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
};
// 表单变化
const onChange = (key: string, val: any) => {
emit('change', { key, val });
};
// 设置
const setModel = (data: Record<string, FieldData>) => {
const newData = {
...model.value,
...data,
};
model.value = newData;
};
// 设置Form
const setForm = (data: Form.Options) => {
const options = {
..._options.value,
...data,
};
_options.value = options;
};
// 更新配置项
const updateFieldsList = (updateList: any[]) => {
const list = changeFormList(newFieldList.value, updateList);
newFieldList.value = list;
};
defineExpose({
submit,
setModel,
setForm,
updateFieldsList,
});
</script>
<style lang="less" scoped>
:deep(.arco-picker) {
width: 100%;
}
.title {
display: flex;
align-items: center;
.bar {
width: 4px;
height: 14px;
border-radius: 1px;
margin-right: 8px;
background-color: rgb(var(--primary-6));
}
.text {
font-size: 16px;
color: var(--color-text-1);
}
}
</style>

95
src/components/wyg-form/interface.d.ts vendored Normal file
View File

@ -0,0 +1,95 @@
import type { FieldRule } from '@arco-design/web-vue/es/form/interface';
import { Size, ColProps, CascaderOption, InputProps } from '@arco-design/web-vue';
console.log(InputProps, 'InputProps=====');
export namespace Form {
/** 表单项自身Props */
interface FormItem<T = string> {
field: string;
label: string;
tooltip?: string;
showColon?: boolean;
noStyle?: boolean;
disabled?: boolean;
help?: string;
extra?: string;
required?: boolean;
asteriskPosition?: 'start' | 'end';
rules?: FieldRule | FieldRule[];
validateStatus?: 'success' | 'warning' | 'error' | 'validating';
validateTrigger?: 'change' | 'input' | 'focus' | 'blur';
labelColProps?: object;
wrapperColProps?: object;
hideLabel?: boolean;
hideAsterisk?: boolean;
labelColStyle?: object;
wrapperColStyle?: object;
rowProps?: object;
rowClass?: string | Array<T> | object;
contentClass?: string | Array<T> | object;
contentFlex?: boolean;
labelColFlex?: number | string;
feedback?: boolean;
labelComponent?: string;
labelAttrs?: object;
}
// 当前 fieldItem 的类型 默认值'input'
type ComponentType =
| 'input'
| 'textarea'
| 'radio'
| 'checkbox'
| 'select'
| 'time'
| 'date'
| 'rangeDate'
| 'inputNumber'
| 'cascader'
| 'title'
| 'slot';
/** 自定义Props */
interface CustomProps {
component?: ComponentType;
lists?: object; // 如果 type='checkbox' / 'radio' / 'select'时需传入此配置项。格式参考FieldItemOptions配置项
}
/** Input、Select组件等的Props */
interface ComponentProps {
placeholder?: string; // 输入框占位文本
readonly?: boolean; // 是否只读 false
allowClear?: boolean; // 是否可清空 false
onChange?: Function;
options?: CascaderOption[];
}
/** 每一项配置项的属性 */
interface FieldItem extends FormItem, CustomProps, ComponentProps {
colProps?: ColProps;
}
/** 处理后的配置项属性 */
interface NewFieldItem extends CustomProps {
formItemProps: FormItem;
componentProps: ComponentProps;
field: string;
colProps?: ColProps;
}
interface FieldItemOptions {
label: string | number;
value: string | number;
}
/** 表单Form自身Props */
interface Options {
layout?: 'horizontal' | 'vertical' | 'inline'; // 表单的布局方式,包括水平、垂直、多列
size?: Size; // 用于控制该表单内组件的尺寸
labelColProps?: object; // 标签元素布局选项。参数同 <col> 组件一致默认值span: 5, offset: 0
wrapperColProps?: object; // 表单控件布局选项。参数同 <col> 组件一致默认值span: 19, offset: 0
labelAlign?: 'left' | 'right'; // 标签的对齐方向,默认值'right'
disabled?: boolean; // 是否禁用表单
rules?: Record<string, FieldRule | FieldRule[]>; // 表单项校验规则
autoLabelWidth?: boolean; // 是否开启自动标签宽度,仅在 layout="horizontal" 下生效。默认值false
showResetButton?: boolean; // 是否展示重置按钮
showCancelButton?: boolean; // 是否展示取消按钮
submitButtonText?: string;
resetButtonText?: string;
cancelButtonText?: string;
}
}

View File

@ -0,0 +1,25 @@
import { formItemKeys, customKeys, COL_PROPS } from './constants';
import type { Form } from './interface';
// fieldList更新
export const changeFormList = (formList: Form.NewFieldItem[], updateList: Form.FieldItem[]): Form.NewFieldItem[] => {
let list: any = formList;
list.forEach((item: any, index: string | number) => {
updateList.forEach((ele: any) => {
if (item.field === ele.field) {
list[index] = { ...item, ...ele };
const keys: string[] = Object.keys(ele);
keys.forEach((key: string) => {
const val = ele[key];
if (formItemKeys.includes(key)) {
list[index].formItemProps[key] = val;
} else if (customKeys.includes(key) || key === 'colProps') {
list[index][key] = val;
} else {
list[index].componentProps[key] = val;
}
});
}
});
});
return list;
};

View File

@ -0,0 +1,73 @@
<!--
* @Author: 田鑫
* @Date: 2023-02-16 14:40:38
* @LastEditors: 田鑫
* @LastEditTime: 2023-02-16 16:37:52
* @Description: table公用封装
-->
<template>
<div>
<a-table
:loading="loading"
:size="size"
:data="tableData"
:bordered="{ cell: borderCell }"
:pagination="setPagination"
page-position="br"
v-bind="propsRes"
v-on="propsEvent"
>
<template #columns>
<slot></slot>
</template>
</a-table>
</div>
</template>
<script setup lang="ts">
import type { PaginationProps, TableData } from '@arco-design/web-vue';
import type { PropType } from 'vue-demi';
import Components from 'unplugin-vue-components/vite';
type Size = 'mini' | 'small' | 'medium' | 'large';
const props = defineProps({
size: {
type: String as PropType<Size>,
default: 'large',
},
tableData: {
type: Array as PropType<TableData[]>,
default: () => [],
},
borderCell: {
type: Boolean,
default: true,
},
pagination: {
type: Object as PropType<PaginationProps>,
default: () => {},
},
});
const loading = ref(false);
const setPagination = computed(() => {
const defaultPagination: PaginationProps = {
showPageSize: true,
showTotal: true,
showMore: true,
size: 'large',
};
return Object.assign(defaultPagination, props.pagination);
});
watchEffect(() => {
if (props.tableData.length === 0) {
loading.value = true;
} else {
loading.value = false;
}
});
</script>
<style lang="less" scoped></style>

17
src/config/settings.json Normal file
View File

@ -0,0 +1,17 @@
{
"theme": "light",
"colorWeak": false,
"navbar": true,
"menu": true,
"topMenu": false,
"hideMenu": false,
"menuCollapse": false,
"footer": true,
"themeColor": "#165DFF",
"menuWidth": 220,
"globalSettings": false,
"device": "desktop",
"tabBar": false,
"menuFromServer": false,
"serverMenu": []
}

0
src/core/dayjs.ts Normal file
View File

1
src/core/index.ts Normal file
View File

@ -0,0 +1 @@
import './dayjs';

0
src/directives/index.ts Normal file
View File

2
src/hooks/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './responsive';
export * from './modal';

55
src/hooks/modal.ts Normal file
View File

@ -0,0 +1,55 @@
/*
* @Author: 田鑫
* @Date: 2023-02-21 15:11:01
* @LastEditors: 田鑫
* @LastEditTime: 2023-02-21 15:11:02
* @Description:
*/
import type { AppContext, Component, DefineComponent } from 'vue';
import type { ModalConfig } from '@arco-design/web-vue';
import { ModalSimple } from '@/components/_base';
type CompType = DefineComponent | Component;
interface SlotsType {
default?: CompType;
header?: CompType;
close?: CompType;
}
export const useModal = () => {
const instance = getCurrentInstance();
const Modal = AModal;
Modal._context = instance?.appContext as AppContext;
Modal.simple = (config: ModalConfig, slots: SlotsType) => {
const { title, content, ..._config } = config || {};
const modal = Modal.open({
..._config,
simple: false,
footer: false,
closable: false,
content: () =>
h(
ModalSimple,
{
title,
content,
onClose: modal.close,
},
{
default: slots?.default,
header: slots?.header,
close: slots?.close,
},
),
});
return modal;
};
return { Modal };
};

30
src/hooks/responsive.ts Normal file
View File

@ -0,0 +1,30 @@
import { useAppStore } from '@/stores';
import { addEventListen, removeEventListen } from '@/utils/event';
const WIDTH = 992;
function queryDevice() {
const rect = document.body.getBoundingClientRect();
return rect.width - 1 < WIDTH;
}
export function useResponsive(immediate?: boolean) {
const appStore = useAppStore();
function resizeHandler() {
if (!document.hidden) {
const isMobile = queryDevice();
appStore.toggleDevice(isMobile ? 'mobile' : 'desktop');
appStore.toggleMenu(isMobile);
}
}
const debounceFn = useDebounceFn(resizeHandler, 100);
onMounted(() => {
if (immediate) debounceFn();
});
onBeforeMount(() => {
addEventListen(window, 'resize', debounceFn);
});
onBeforeUnmount(() => {
removeEventListen(window, 'resize', debounceFn);
});
}

172
src/hooks/table-hooks.ts Normal file
View File

@ -0,0 +1,172 @@
/*
* @Author: 田鑫
* @Date: 2023-02-16 15:02:51
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-09 11:20:59
* @Description: table-hooks
*/
import type { PaginationProps, TableBorder, TableColumnData, TableData, TableRowSelection } from '@arco-design/web-vue';
type Size = 'mini' | 'small' | 'medium' | 'large';
interface IDefaultProps {
/** 是否显示边框 */
bordered?: TableBorder;
/** 是否显示选中效果 */
hoverable?: boolean;
/** 表格的大小 */
size?: Size;
/** 是否允许调整列宽 */
'column-resizable'?: boolean;
/** 是否为加载中状态 */
loading?: boolean;
/** 分页参数 */
pagination?: PaginationProps;
/** table数据类型 */
data?: any[];
/** 表头参数 */
columns?: TableColumnData[];
/** 表格行 key 的取值字段 */
'row-key'?: string;
/** 表格的行选择器配置 */
'row-selection'?: TableRowSelection;
'selected-keys'?: (string | number)[];
[x: string]: any;
}
interface IPagination {
/** 当前页数 */
current?: number;
/** 总页数默认是0条 */
total?: number;
}
interface ITableResponse<T> {
current?: number;
records?: T[];
size?: number;
total?: number;
[x: string]: any;
}
type GetListFunc<T> = (v: object) => Promise<ITableResponse<T>>;
export default function useTableProps<T>(loadListFunc: GetListFunc<T>) {
const defaultProps: IDefaultProps = {
bordered: { cell: true },
size: 'large',
'column-resizable': true,
loading: true,
data: [] as any[],
pagination: {
current: 1,
pageSize: 20,
total: 0,
showPageSize: true,
showTotal: true,
},
hoverable: false,
columns: [],
};
//* 属性组
const propsRes = reactive<IDefaultProps>(defaultProps);
//* 设置请求参数,如果出了分页参数还有搜索参数,在模板页面调用此方法,可以加入参数
const loadListParams = reactive<object>({
page: 1,
size: 20,
});
/**
* 单独设置默认属性
* @param params
*/
const setProps = (params: IDefaultProps) => {
if (Object.keys(params).length > 0) {
Object.assign(defaultProps, params);
}
};
/**
* 设置表头数据
* @param columns
*/
const setColumns = (columns: TableColumnData[]) => {
propsRes.columns = columns;
};
/**
* 设置loading
* @param status
*/
const setLoading = (status: boolean) => {
propsRes.loading = status;
};
/**
* 设置分页
* @param param0
*/
const setPagination = ({ current, total }: IPagination) => {
propsRes.pagination!.current = current;
total && (propsRes.pagination!.total = total);
Object.assign(loadListParams, { page: current });
};
/**
* 设置列表请求参数
* @param params
*/
const setLoadListParams = <R>(params?: R) => {
Object.assign(loadListParams, params);
};
/**
* 加载列表
* @returns
*/
const loadTableData = async (resetPageIndex = false) => {
if (resetPageIndex) {
setPagination({ current: 1 });
}
setLoading(true);
try {
const resData = await loadListFunc({
...loadListParams,
});
console.log(resData);
const response = resData as ITableResponse<T>;
propsRes.data = response.records;
setPagination({
current: response.current,
total: response.total,
});
setLoading(false);
return resData;
} catch (error) {
setLoading(false);
return [];
}
};
// 事件触发组
const propsEvent = reactive({
// 排序触发
sorterChange: (dataIndex: string, direction: string) => {
console.log(dataIndex, direction);
},
// 分页触发
pageChange: (current: number) => {
setPagination({ current });
loadTableData();
},
// 修改每页显示条数
pageSizeChange: (size: number) => {
propsRes.pagination!.pageSize = size;
Object.assign(loadListParams, { size });
loadTableData();
},
selectionChange: (rowKeys: (string | number)[]) => {
propsRes['selected-keys'] = rowKeys;
},
});
return {
propsRes,
propsEvent,
loadListParams,
setProps,
setColumns,
setLoading,
setPagination,
loadTableData,
setLoadListParams,
};
}

52
src/hooks/useEffect.ts Normal file
View File

@ -0,0 +1,52 @@
/*
* @Author: 田鑫
* @Date: 2024-06-11 14:55:40
* @LastEditors: 田鑫
* @LastEditTime: 2024-06-11 14:55:41
* @Description:
*/
/**
* @description 模拟的react的useEffect
* @param fn
* @param deps
*/
export function useEffect(fn: () => void | (() => any), deps: any[] = []) {
const tract = new Error();
const resFn = ref(() => { });
if (deps.length === 0) {
onMounted(() => {
const res = fn();
if (typeof res === 'function') {
resFn.value = res;
} else {
resFn.value = () => { };
}
});
} else {
watch(
() => deps,
() => {
resFn.value();
const res = (() => {
try {
return fn();
} catch (error) {
console.error(tract);
console.error(error);
}
})();
if (typeof res === 'function') {
resFn.value = res;
} else {
resFn.value = () => { };
}
},
{ immediate: true, deep: true },
);
}
onUnmounted(() => {
resFn.value();
});
}

150
src/layouts/Basic.vue Normal file
View File

@ -0,0 +1,150 @@
<script setup lang="ts">
import { useAppStore } from '@/stores';
import { useResponsive } from '@/hooks';
const appStore = useAppStore();
const router = useRouter();
useResponsive(true);
const navbarHeight = `60px`;
const navbar = computed(() => appStore.navbar);
const renderMenu = computed(() => appStore.menu && !appStore.topMenu);
const hideMenu = computed(() => appStore.hideMenu);
const menuWidth = computed(() => {
return appStore.menuCollapse ? 48 : appStore.menuWidth;
});
const collapsed = computed(() => {
return appStore.menuCollapse;
});
const paddingStyle = computed(() => {
const paddingLeft = renderMenu.value && !hideMenu.value ? { paddingLeft: `${menuWidth.value}px` } : {};
const paddingTop = navbar.value ? { paddingTop: navbarHeight } : {};
return { ...paddingLeft, ...paddingTop };
});
const setCollapsed = (val: boolean) => {
appStore.updateSettings({ menuCollapse: val });
};
const drawerVisible = ref(false);
const drawerCancel = () => {
drawerVisible.value = false;
};
provide('toggleDrawerMenu', () => {
drawerVisible.value = !drawerVisible.value;
});
</script>
<template>
<a-layout :class="['layout', { mobile: appStore.hideMenu }]">
<div v-if="navbar" class="layout-navbar">
<base-navbar />
</div>
<a-layout>
<a-layout>
<a-layout-sider
v-if="renderMenu"
v-show="!hideMenu"
class="layout-sider"
breakpoint="xl"
:collapsed="collapsed"
:width="menuWidth"
:style="{ paddingTop: navbar ? '60px' : '' }"
collapsible
hide-trigger
@collapse="setCollapsed"
>
<div class="menu-wrapper">
<base-menu />
</div>
</a-layout-sider>
<a-drawer
v-if="hideMenu"
:visible="drawerVisible"
placement="left"
:footer="false"
mask-closable
:closable="false"
@cancel="drawerCancel"
>
<base-menu />
</a-drawer>
<a-layout class="layout-content" :style="paddingStyle">
<base-tab-bar v-if="appStore.tabBar" />
<a-layout-content class="px-5">
<base-breadcrumb />
<layout-page />
</a-layout-content>
</a-layout>
</a-layout>
</a-layout>
</a-layout>
</template>
<style scoped lang="less">
@nav-size-height: 60px;
@layout-max-width: 1100px;
.layout {
width: 100%;
height: 100%;
}
.layout-navbar {
position: fixed;
top: 0;
left: 0;
z-index: 100;
width: 100%;
height: @nav-size-height;
}
.layout-sider {
position: fixed;
top: 0;
left: 0;
z-index: 99;
height: 100%;
transition: all 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
&::after {
position: absolute;
top: 0;
right: -1px;
display: block;
width: 1px;
height: 100%;
background-color: var(--color-border);
content: '';
}
> :deep(.arco-layout-sider-children) {
overflow-y: hidden;
}
}
.menu-wrapper {
height: 100%;
overflow: auto;
overflow-x: hidden;
:deep(.arco-menu) {
::-webkit-scrollbar {
width: 12px;
height: 4px;
}
::-webkit-scrollbar-thumb {
border: 4px solid transparent;
background-clip: padding-box;
border-radius: 7px;
background-color: var(--color-text-4);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--color-text-3);
}
}
}
.layout-content {
min-width: 1366px;
min-height: 100vh;
overflow-y: hidden;
background-color: var(--color-fill-2);
transition: padding 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
}
</style>

26
src/layouts/NotFound.vue Normal file
View File

@ -0,0 +1,26 @@
<script setup lang="ts">
const router = useRouter();
const back = () => {
router.replace('/');
};
</script>
<template>
<div class="content">
<a-result class="result" status="404" subtitle="页面跑路了" />
<div class="operation-row">
<a-button key="back" type="primary" @click="back">返回</a-button>
</div>
</div>
</template>
<style scoped lang="less">
.content {
position: absolute;
top: 50%;
left: 50%;
margin-left: -95px;
margin-top: -121px;
text-align: center;
}
</style>

40
src/layouts/Page.vue Normal file
View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import { useTabBarStore } from '@/stores';
import { useRoute } from 'vue-router';
const tabBarStore = useTabBarStore();
const cacheList = computed(() => tabBarStore.getCacheList);
/** - <router-view> 上需要设置一个key避免组件复用 */
const route = useRoute();
const routerKey = computed(() => {
return route.path + Math.random();
});
/*** - end */
</script>
<template>
<router-view v-slot="{ Component, route }" :key="routerKey">
<transition name="fade" mode="out-in" appear>
<component :is="Component" v-if="route.meta.ignoreCache" :key="route.fullPath" />
<keep-alive v-else :include="cacheList">
<component :is="Component" :key="route.fullPath" />
</keep-alive>
</transition>
<view class="footer">
<view>闽公网安备 352018502850842 闽ICP备20250520582号 © 2025小题科技All Rights Reserved.</view>
<view>* 数据通过公开渠道获取灵机进行统计分析</view>
</view>
</router-view>
</template>
<style lang="less" scoped>
.footer {
margin: 20px;
width: 98%;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 10px;
font-weight: 400;
color: rgb(var(--color-text-6));
}
</style>

16
src/main.ts Normal file
View File

@ -0,0 +1,16 @@
import App from './App.vue';
import router from './router';
import store from './stores';
import '@/api/index';
import './styles';
import './core';
import 'uno.css';
import './mock';
import '@/styles/vars.css'; // 优先加载
import '@arco-design/web-vue/dist/arco.css'; // Arco 默认样式
const app = createApp(App);
app.use(store);
app.use(router);
app.mount('#app');

15
src/mock/index.ts Normal file
View File

@ -0,0 +1,15 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 18:49:06
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 18:49:20
* @Description: Mock数据
*/
import Mock from 'mockjs';
import './table';
Mock.setup({
timeout: '600-1000',
});

31
src/mock/setup-mock.ts Normal file
View File

@ -0,0 +1,31 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 18:59:18
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 19:02:04
* @Description: mock设置
*/
export default ({ mock, setup }: { mock?: boolean; setup: () => void }) => {
if (mock !== false) setup();
};
export const successResponseWrap = (data: unknown) => {
return {
success: true,
msg: '请求成功',
code: 200,
data,
timestamp: new Date().getTime(),
};
};
export const failResponseWrap = (data: unknown, msg: string, code = 50000) => {
return {
data,
success: false,
msg,
code,
timestamp: new Date().getTime(),
};
};

28
src/mock/table.ts Normal file
View File

@ -0,0 +1,28 @@
import type { IExample } from '@/api/example';
import Mock from 'mockjs';
import setupMock, { successResponseWrap } from './setup-mock';
setupMock({
setup() {
Mock.mock(new RegExp('/api/example-table'), () => {
let tableData: IExample.ITableResponse[] = [];
let count = 20;
tableData = new Array(count).fill('').map((item, index) => ({
id: `${item + 1}`,
column1: `${item + 1}列的第${index + 1}条数据`,
column2: `${item + 2}列的第${index + 1}条数据`,
column3: `${item + 3}列的第${index + 1}条数据`,
column4: `${item + 4}列的第${index + 1}条数据`,
column5: `${item + 5}列的第${index + 1}条数据`,
}));
return successResponseWrap({
records: tableData,
total: tableData.length,
current: 1,
size: 20,
});
});
},
});

View File

@ -0,0 +1,16 @@
import { appRoutes, appExternalRoutes } from '../routes';
const mixinRoutes = [...appRoutes, ...appExternalRoutes];
const appClientMenus = mixinRoutes.map((el) => {
const { name, path, meta, redirect, children } = el;
return {
name,
path,
meta,
redirect,
children,
};
});
export default mixinRoutes;

25
src/router/constants.ts Normal file
View File

@ -0,0 +1,25 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 18:14:17
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 18:24:40
* @Description:
*/
export const WHITE_LIST = [
{ name: 'notFound', children: [] },
{ name: 'login', children: [] },
];
export const NOT_FOUND = {
name: 'notFound',
};
export const REDIRECT_ROUTE_NAME = 'Redirect';
export const DEFAULT_ROUTE_NAME = 'main';
export const DEFAULT_ROUTE = {
title: '首页',
name: DEFAULT_ROUTE_NAME,
fullPath: '/',
};

24
src/router/guard/index.ts Normal file
View File

@ -0,0 +1,24 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 18:14:17
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 19:21:15
* @Description:
*/
import type { Router } from 'vue-router';
import { setRouteEmitter } from '@/utils/route-listener';
import setupUserLoginInfoGuard from './userLoginInfo';
import setupPermissionGuard from './permission';
function setupPageGuard(router: Router) {
router.beforeEach(async (to) => {
// emit route change
setRouteEmitter(to);
});
}
export default function createRouteGuard(router: Router) {
setupPageGuard(router);
setupUserLoginInfoGuard(router);
setupPermissionGuard(router);
}

View File

@ -0,0 +1,29 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 14:46:43
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 15:55:36
* @Description: 路由权限守卫
*/
import type { Router, RouteRecordNormalized } from 'vue-router';
import NProgress from 'nprogress'; // progress bar
import { useAppStore } from '@/stores';
export default function setupPermissionGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
console.log('access permission router guard');
const appStore = useAppStore();
//* 菜单是否为服务端渲染
if (appStore.menuFromServer) {
//* 没有服务端渲染的菜单
if(!appStore.appAsyncMenus) {
// todo 请求服务端渲染菜单的接口当前为mock数据
await appStore.fetchServerMenuConfig();
}
next();
} else {
next();
}
NProgress.done();
});
}

View File

@ -0,0 +1,43 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 14:46:43
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 15:59:25
* @Description: 路由登录状态守卫
*/
import type { Router, LocationQueryRaw } from 'vue-router';
import NProgress from 'nprogress'; // progress bar
import { isLogin, clearAllLocalStorage } from '@/utils/auth';
import { useUserStore } from '@/stores/modules/user';
export default function setupUserLoginInfoGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
console.log('access login info router guard');
NProgress.start();
if (to.name === 'auth') {
next();
}
const userStore = useUserStore();
//* 判断用户是否登录,若登录则放过,进入下一步
//* 若无,则清空所有缓存并弹回登录鉴权页
if (isLogin()) {
if (userStore.role) {
next();
} else {
userStore.getUserInfo();
next();
}
} else {
clearAllLocalStorage();
// todo 跳转回登录鉴权页当前为mock路由地址
next({
name: 'auth',
query: {
redirect: to.name,
...to.query,
} as LocationQueryRaw,
});
}
});
}

50
src/router/index.ts Normal file
View File

@ -0,0 +1,50 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 18:14:17
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 19:20:40
* @Description:
*/
import { createRouter, createWebHistory } from 'vue-router';
import { appRoutes } from './routes';
import { REDIRECT_MAIN, NOT_FOUND_ROUTE } from './routes/base';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import createRouteGuard from './guard';
NProgress.configure({ showSpinner: false }); // NProgress Configuration
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'workplace',
component: () => import('@/views/components/workplace'),
},
{
path: '/dataEngine',
name: 'dataEngine',
redirect: '@/views/components/dataEngine',
children: [...appRoutes, REDIRECT_MAIN, NOT_FOUND_ROUTE],
},
{
path: '/permission',
name: 'permission',
component: () => import('@/views/components/permission/choose-enterprise.vue'),
},
{
path: '/auth',
name: 'auth',
component: () => import('@/views/components/permission/auth.vue'),
},
],
scrollBehavior() {
return { top: 0 };
},
});
createRouteGuard(router);
export default router;

28
src/router/routes/base.ts Normal file
View File

@ -0,0 +1,28 @@
import type { RouteRecordRaw } from 'vue-router';
import { REDIRECT_ROUTE_NAME } from '@/router/constants';
export const REDIRECT_MAIN: RouteRecordRaw = {
path: '/redirect',
name: 'redirect',
meta: {
requiresAuth: true,
hideInMenu: true,
},
children: [
{
path: '/redirect/:path',
name: REDIRECT_ROUTE_NAME,
component: () => import('@/layouts/Basic.vue'),
meta: {
requiresAuth: true,
hideInMenu: true,
},
},
],
};
export const NOT_FOUND_ROUTE: RouteRecordRaw = {
path: '/:pathMatch(.*)*',
name: 'notFound',
component: () => import('@/layouts/NotFound.vue'),
};

View File

@ -0,0 +1,20 @@
import type { RouteRecordNormalized } from 'vue-router';
const modules = import.meta.glob('./modules/*.ts', { eager: true });
const externalModules = import.meta.glob('./externalModules/*.ts', {
eager: true,
});
function formatModules(_modules: any, result: RouteRecordNormalized[]) {
Object.keys(_modules).forEach((key) => {
const defaultModule = _modules[key].default;
if (!defaultModule) return;
const moduleList = Array.isArray(defaultModule) ? [...defaultModule] : [defaultModule];
result.push(...moduleList);
});
return result;
}
export const appRoutes: RouteRecordNormalized[] = formatModules(modules, []);
export const appExternalRoutes: RouteRecordNormalized[] = formatModules(externalModules, []);

View File

@ -0,0 +1,77 @@
import { IconBookmark } from '@arco-design/web-vue/es/icon';
import type { AppRouteRecordRaw } from '../types';
const COMPONENTS: AppRouteRecordRaw = {
path: 'dataEngine',
name: 'dataEngine',
meta: {
locale: '全域数据引擎',
icon: IconBookmark,
requiresAuth: true,
roles: ['*'],
},
children: [
{
path: 'hotTranslation',
name: '行业热门话题洞察',
meta: {
locale: '行业热门话题洞察',
requiresAuth: true,
roles: ['*'],
},
component: () => import('@/views/components/dataEngine/hotTranslation.vue'),
},
{
path: 'hotCloud',
name: '行业词云',
meta: {
locale: '行业词云',
requiresAuth: true,
roles: ['*'],
},
component: () => import('@/views/components/dataEngine/hotCloud.vue'),
},
{
path: 'keyWord',
name: '行业关键词动向',
meta: {
locale: '行业关键词动向',
requiresAuth: true,
roles: ['*'],
},
component: () => import('@/views/components/dataEngine/keyWord.vue'),
},
{
path: 'userPainPoints',
name: '用户痛点观察',
meta: {
locale: '用户痛点观察',
requiresAuth: true,
roles: ['*'],
},
component: () => import('@/views/components/dataEngine/userPainPoints.vue'),
},
{
path: 'keyBrandMovement',
name: '重点品牌动向',
meta: {
locale: '重点品牌动向',
requiresAuth: true,
roles: ['*'],
},
component: () => import('@/views/components/dataEngine/keyBrandMovement.vue'),
},
{
path: 'userPersona',
name: '用户画像',
meta: {
locale: '用户画像',
requiresAuth: true,
roles: ['*'],
},
component: () => import('@/views/components/dataEngine/userPersona.vue'),
},
],
};
export default COMPONENTS;

View File

@ -0,0 +1,14 @@
import type { RouteMeta, NavigationGuard, RouteComponent } from 'vue-router';
export interface AppRouteRecordRaw {
path: string;
name?: string | symbol;
meta?: RouteMeta;
redirect?: string;
component?: RouteComponent;
children?: AppRouteRecordRaw[];
alias?: string | string[];
props?: Record<string, any>;
beforeEnter?: NavigationGuard | NavigationGuard[];
fullPath?: string;
}

17
src/router/typeings.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
import { RouteComponent } from 'vue-router';
declare module 'vue-router' {
interface RouteMeta {
roles?: string[]; // Controls roles that have access to the page
requiresAuth: boolean; // Whether login is required to access the current page (every route must declare)
icon?: RouteComponent; // The icon show in the side menu
locale?: string; // The locale name show in side menu and breadcrumb
needNavigate?: boolean; // if set true, the breadcrumb will support navigate
hideInMenu?: boolean; // If true, it is not displayed in the side menu
hideChildrenInMenu?: boolean; // if set true, the children are not displayed in the side menu
activeMenu?: string; // if set name, the menu will be highlighted according to the name you set
order?: number; // Sort routing menu items. If set key, the higher the value, the more forward it is
noAffix?: boolean; // if set true, the tag will not affix in the tab-bar
ignoreCache?: boolean; // if set true, the page will not be cached
}
}

5
src/stores/index.ts Normal file
View File

@ -0,0 +1,5 @@
const pinia = createPinia();
export default pinia;
export * from './modules';

View File

@ -0,0 +1,101 @@
import type { AppState, RouteRecordNormalized, NotificationReturn } from './types';
import defaultSettings from '@/config/settings.json';
import type { AppRouteRecordRaw } from '@/router/routes/types';
export const useAppStore = defineStore('app', {
state: (): AppState => ({ ...defaultSettings }),
getters: {
appCurrentSetting(state: AppState): AppState {
return { ...state };
},
appDevice(state: AppState) {
return state.device;
},
appAsyncMenus(state: AppState): AppRouteRecordRaw[] {
return state.serverMenu as unknown as AppRouteRecordRaw[];
},
},
actions: {
// Update app settings
updateSettings(partial: Partial<AppState>) {
// @ts-ignore-next-line
this.$patch(partial);
},
// Change theme color
toggleTheme(dark: boolean) {
if (dark) {
this.theme = 'dark';
document.body.setAttribute('arco-theme', 'dark');
} else {
this.theme = 'light';
document.body.removeAttribute('arco-theme');
}
},
toggleDevice(device: string) {
this.device = device;
},
toggleMenu(value: boolean) {
this.hideMenu = value;
},
async fetchServerMenuConfig() {
this.serverMenu = [
{
path: '/enterprise',
name: 'enterprise',
meta: {
locale: '企业票夹',
requiresAuth: true,
roles: ['*'],
},
children: [
{
path: 'input',
name: 'input',
meta: {
icon: IconImport,
locale: '进项管理',
requiresAuth: true,
roles: ['*'],
},
},
{
path: 'output',
name: 'output',
meta: {
locale: '销项管理',
requiresAuth: true,
roles: ['*'],
},
},
{
path: 'red-invoice',
name: 'red-invoice',
meta: {
locale: '全电红字确认单管理',
requiresAuth: true,
roles: ['*'],
},
},
{
path: 'information',
name: 'information',
meta: {
locale: '企业信息',
requiresAuth: true,
roles: ['*'],
},
},
],
},
];
this.menuFromServer = true;
},
clearServerMenu() {
this.serverMenu = [];
},
},
});

View File

@ -0,0 +1,24 @@
import type { RouteRecordNormalized } from 'vue-router';
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
import type { AppRouteRecordRaw } from '@/router/routes/types';
export { RouteRecordNormalized, NotificationReturn };
export interface AppState {
theme: string;
colorWeak: boolean;
navbar: boolean;
menu: boolean;
topMenu: boolean;
hideMenu: boolean;
menuCollapse: boolean;
footer: boolean;
themeColor: string;
menuWidth: number;
globalSettings: boolean;
device: string;
tabBar: boolean;
menuFromServer: boolean;
serverMenu: AppRouteRecordRaw[];
[key: string]: unknown;
}

View File

@ -0,0 +1,10 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 18:14:17
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 18:24:57
* @Description:
*/
export * from './app';
export * from './tab-bar';
export * from './user';

View File

@ -0,0 +1,67 @@
import type { TabBarState, TagProps, RouteLocationNormalized } from './types';
import { DEFAULT_ROUTE, DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants';
import { isString } from '@/utils/is';
const formatTag = (route: RouteLocationNormalized): TagProps => {
const { name, meta, fullPath, query } = route;
return {
title: meta.locale || '',
name: String(name),
fullPath,
query,
ignoreCache: meta.ignoreCache,
};
};
const BAN_LIST = [REDIRECT_ROUTE_NAME];
export const useTabBarStore = defineStore('tabBar', {
state: (): TabBarState => ({
cacheTabList: new Set([DEFAULT_ROUTE_NAME]),
tagList: [DEFAULT_ROUTE],
}),
getters: {
getTabList(): TagProps[] {
return this.tagList;
},
getCacheList(): string[] {
return Array.from(this.cacheTabList);
},
},
actions: {
updateTabList(route: RouteLocationNormalized) {
if (BAN_LIST.includes(route.name as string)) return;
this.tagList.push(formatTag(route));
if (!route.meta.ignoreCache) {
this.cacheTabList.add(route.name as string);
}
},
deleteTag(idx: number, tag: TagProps) {
this.tagList.splice(idx, 1);
this.cacheTabList.delete(tag.name);
},
addCache(name: string) {
if (isString(name) && name !== '') this.cacheTabList.add(name);
},
deleteCache(tag: TagProps) {
this.cacheTabList.delete(tag.name);
},
freshTabList(tags: TagProps[]) {
this.tagList = tags;
this.cacheTabList.clear();
// 要先判断ignoreCache
this.tagList
.filter((el) => !el.ignoreCache)
.map((el) => el.name)
.forEach((x) => this.cacheTabList.add(x));
},
resetTabList() {
this.tagList = [DEFAULT_ROUTE];
this.cacheTabList.clear();
this.cacheTabList.add(DEFAULT_ROUTE_NAME);
},
},
});

View File

@ -0,0 +1,14 @@
export type { RouteLocationNormalized } from 'vue-router';
export interface TagProps {
title: string;
name: string;
fullPath: string;
query?: any;
ignoreCache?: boolean;
}
export interface TabBarState {
tagList: TagProps[];
cacheTabList: Set<string>;
}

View File

@ -0,0 +1,46 @@
/*
* @Author: 田鑫
* @Date: 2023-03-05 14:57:17
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 15:29:15
* @Description: 用户相关状态
*/
type Role = 'ENTERPRISE' | 'PERSON';
interface UserState {
role: Role;
isLogin: boolean;
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
role: 'PERSON',
isLogin: false,
}),
getters: {
userRole(state) {
return state.role;
},
userIsLogin(state) {
return state.isLogin;
},
},
actions: {
setUserRole(role: Role) {
this.role = role;
},
setUserLoginStatus(isLogin: boolean) {
this.isLogin = isLogin;
},
async getUserInfo() {
// todo 调用获取用户信息接口当前用mock数据表示
AMessage.success(`当前用户角色为ENTERPRISE`)
this.setUserRole('ENTERPRISE');
},
},
});

3
src/styles/index.ts Normal file
View File

@ -0,0 +1,3 @@
import 'normalize.css';
import 'uno.css';
import './vars.css';

106
src/styles/vars.css Normal file
View File

@ -0,0 +1,106 @@
:root {
/* 修改主色 */
--arco-primary-6: #dd3e05 !important; /* 默认主色 */
--arco-primary-5: #40ffdf !important; /* 悬停色 */
--arco-primary-7: #c9dbed !important; /* 点击色 */
/* 修改成功/警告/错误色 */
--arco-success-6: #52c41a !important;
--arco-warning-6: #faad14 !important;
--arco-error-6: #f5222d !important;
/* 修改文字/背景色 */
--arco-text-1: #ed0707 !important; /* 主要文字 */
--arco-bg-1: #f7f8fa !important; /* 页面背景 */
}
* {
box-sizing: border-box;
}
.flex {
display: flex;
}
.item-center {
align-items: center;
}
.box-container {
border-radius: 2px;
padding: 20px;
margin: 8px;
background-color: #fff;
}
:root {
/* 主色覆盖(强制替换 Arco 默认蓝色) */
--arco-primary-6: #6d4cfe !important;
--arco-primary-5: #8a70fe !important;
--arco-primary-7: #573dcb !important;
--arco-primary-1: #f5f2ff !important; /* 浅色背景 */
}
/* 强制所有组件使用主色 */
.arco-btn-primary,
.arco-menu-selected,
.arco-switch-checked,
.arco-radio-checked,
.arco-checkbox-checked,
.arco-slider-button,
.arco-tabs-header-item-active,
.arco-alert-info,
.arco-badge-status-processing {
background-color: var(--arco-primary-6) !important;
border-color: var(--arco-primary-6) !important;
color: white !important; /* 文字颜色(按需调整) */
}
.arco-menu-selected-label {
background-color: var(--arco-primary-6) !important;
}
.arco-menu-item,
.arco-btn-secondary,
.arco-menu-light {
background-color: transparent !important;
}
.arco-menu-light .arco-menu-item.arco-menu-selected .arco-icon,
.arco-menu-light .arco-menu-group-title.arco-menu-selected .arco-icon,
.arco-menu-light .arco-menu-pop-header.arco-menu-selected .arco-icon,
.arco-menu-light .arco-menu-inline-header.arco-menu-selected .arco-icon,
.arco-menu-light .arco-menu-item.arco-menu-selected .arco-menu-icon,
.arco-menu-light .arco-menu-group-title.arco-menu-selected .arco-menu-icon,
.arco-menu-light .arco-menu-pop-header.arco-menu-selected .arco-menu-icon,
.arco-menu-light .arco-menu-inline-header.arco-menu-selected .arco-menu-icon {
color: var(--arco-primary-6) !important;
}
.arco-menu-light,
.arco-menu-item,
.arco-menu-selected {
background-color: transparent !important;
}
.arco-menu-selected button {
color: var(--arco-primary-6) !important;
}
.arco-space {
border-radius: 8px !important;
}
.arco-btn-primary {
border-radius: 4px;
}
.arco-menu-selected {
border-radius: 8px;
color: var(--arco-primary-6) !important;
}
/* 强制所有组件使用主色 */
.arco-switch,
.arco-radio-checked,
.arco-checkbox-checked,
.arco-slider-button,
.arco-link {
--color-primary-6: var(--arco-primary-6) !important;
}

26
src/utils/auth.ts Normal file
View File

@ -0,0 +1,26 @@
/*
* @Author: 田鑫
* @Date: 2023-02-21 15:09:11
* @LastEditors: 田鑫
* @LastEditTime: 2023-03-05 19:21:56
* @Description:
*/
const TOKEN_KEY = 'satoken';
const isLogin = () => {
return !!localStorage.getItem(TOKEN_KEY);
};
const getToken = () => {
return localStorage.getItem(TOKEN_KEY);
};
const setToken = (token: string) => {
localStorage.setItem(TOKEN_KEY, token);
};
const clearToken = () => {
localStorage.removeItem(TOKEN_KEY);
};
const clearAllLocalStorage = () => {
localStorage.clear();
};
export { isLogin, getToken, setToken, clearToken, clearAllLocalStorage };

25
src/utils/event.ts Normal file
View File

@ -0,0 +1,25 @@
// eslint-disable-next-line max-params
export function addEventListen(
target: Window | HTMLElement,
event: string,
// eslint-disable-next-line no-undef
handler: EventListenerOrEventListenerObject,
capture = false,
) {
if (target.addEventListener && typeof target.addEventListener === 'function') {
target.addEventListener(event, handler, capture);
}
}
// eslint-disable-next-line max-params
export function removeEventListen(
target: Window | HTMLElement,
event: string,
// eslint-disable-next-line no-undef
handler: EventListenerOrEventListenerObject,
capture = false,
) {
if (target.removeEventListener && typeof target.removeEventListener === 'function') {
target.removeEventListener(event, handler, capture);
}
}

20
src/utils/index.ts Normal file
View File

@ -0,0 +1,20 @@
type TargetContext = '_self' | '_parent' | '_blank' | '_top';
export const openWindow = (url: string, opts?: { target?: TargetContext; [key: string]: any }) => {
const { target = '_blank', ...others } = opts || {};
window.open(
url,
target,
Object.entries(others)
.reduce((preValue: string[], curValue) => {
const [key, value] = curValue;
return [...preValue, `${key}=${value}`];
}, [])
.join(','),
);
};
export const regexUrl =
/^(?!mailto:)(?:(?:http|https|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))|localhost)(?::\d{2,5})?(?:(\/|\?|#)[^\s]*)?$/i;
export default null;

53
src/utils/is.ts Normal file
View File

@ -0,0 +1,53 @@
const opt = Object.prototype.toString;
export function isArray(obj: any): obj is any[] {
return opt.call(obj) === '[object Array]';
}
export function isObject(obj: any): obj is { [key: string]: any } {
return opt.call(obj) === '[object Object]';
}
export function isString(obj: any): obj is string {
return opt.call(obj) === '[object String]';
}
export function isNumber(obj: any): obj is number {
return opt.call(obj) === '[object Number]' && obj === obj; // eslint-disable-line
}
export function isRegExp(obj: any) {
return opt.call(obj) === '[object RegExp]';
}
export function isFile(obj: any): obj is File {
return opt.call(obj) === '[object File]';
}
export function isBlob(obj: any): obj is Blob {
return opt.call(obj) === '[object Blob]';
}
export function isUndefined(obj: any): obj is undefined {
return obj === undefined;
}
export function isNull(obj: any): obj is null {
return obj === null;
}
export function isFunction(obj: any): obj is (...args: any[]) => any {
return typeof obj === 'function';
}
export function isEmptyObject(obj: any): boolean {
return isObject(obj) && Object.keys(obj).length === 0;
}
export function isExist(obj: any): boolean {
return obj || obj === 0;
}
export function isWindow(el: any): el is Window {
return el === window;
}

View File

@ -0,0 +1,30 @@
/**
* Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management
* 单独监听路由会浪费渲染性能。使用发布订阅模式去进行分发管理。
*/
import type { RouteLocationNormalized } from 'vue-router';
// @ts-ignore
import mitt, { type Handler } from 'mitt';
const emitter = mitt();
const key = Symbol('ROUTE_CHANGE');
let latestRoute: RouteLocationNormalized;
export function setRouteEmitter(to: RouteLocationNormalized) {
emitter.emit(key, to);
latestRoute = to;
}
export function listenerRouteChange(handler: (route: RouteLocationNormalized) => void, immediate = true) {
emitter.on(key, handler as Handler);
if (immediate && latestRoute) {
handler(latestRoute);
}
}
export function removeRouteListener() {
emitter.off(key);
}

48
src/utils/validators.ts Normal file
View File

@ -0,0 +1,48 @@
/*
* @Author: 田鑫
* @Date: 2023-02-21 15:05:52
* @LastEditors: 田鑫
* @LastEditTime: 2023-02-21 15:08:07
* @Description:
*/
/**
* 11位有效电话号码验证规则
*/
const validPhoneNumber = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/;
/**
* 手机号验证
*/
export function validateMobile(value, callback) {
if (value == '' || value == undefined) {
callback();
} else {
// const reg = /^1([38]\d|5[0-35-9]|7[3678])\d{8}$/
const reg = /^(13[0-9]|14[01456879]|15[0-3,5-9]|16[2567]|17[0-8]|18[0-9]|19[0-3,5-9])\d{8}$/;
if (!reg.test(value)) {
callback(new Error('不是有效的手机号'));
} else {
callback();
}
}
}
/**
* 邮箱校验
*/
export function validateEmail(value, callback) {
if (value == '' || value == undefined) {
callback();
} else {
const reg = /^([0-9a-zA-Z_.-]+[@][0-9a-zA-Z_.-]+([.][a-zA-Z]+){1,2})+$/;
if (!reg.test(value)) {
callback(new Error('邮箱格式不正确'));
} else {
callback();
}
}
}
export const uuid = '1234';
export { validPhoneNumber };

View File

@ -0,0 +1,184 @@
<template>
<view>
<topHeader ref="topHeaderRef"></topHeader>
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
<a-space align="center">
<span>行业词云</span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<div class="multi-row-tag-cloud">
<!-- 动态生成多行标签 -->
<div
v-for="(row, rowIndex) in tagRows"
:key="rowIndex"
class="tag-row"
:style="{ justifyContent: row.align || 'center' }"
>
<a-tag
v-for="(tag, tagIndex) in row.tags"
:key="tagIndex"
:style="{
fontSize: `${getFontSize(rowIndex, tagIndex)}px`,
lineHeight: `${getLineHeight(rowIndex, tagIndex) + 10}px`,
color: '#6d4cfe',
backgroundColor: '#F0EDFF',
margin: '12px',
transition: 'all 0.3s',
paddingLeft: `${getPaddingLeft(rowIndex, tagIndex)}px`,
paddingRight: `${getPaddingLeft(rowIndex, tagIndex)}px`,
paddingTop: `${getPadding(rowIndex, tagIndex)}px`,
paddingBottom: `${getPadding(rowIndex, tagIndex)}px`,
borderRadius: '100px',
borderRadius: '100px',
}"
@mouseenter="hoverTag = tag"
@mouseleave="hoverTag = null"
>
<a-space>
<a-tooltip :content="`性价比:${Number(tag.rate * 100)}%`" position="tl">
<a-space>{{ tag.term }}</a-space>
</a-tooltip>
</a-space>
</a-tag>
</div>
</div>
</a-space>
</view>
</template>
<script setup>
import topHeader from './topHeader.vue';
import { ref, computed } from 'vue';
import { fetchindustryTerms } from '@/api/all/index';
const topHeaderRef = ref();
// 从topHeader获取统一的状态
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
const getIndustryTerms = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchindustryTerms(params);
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
tagRows.value = processTagData(res);
};
// 标签数据(按行分组)
const tagRows = ref();
const hoverTag = ref(null);
// 根据行列位置计算字体大小(中间最大)
const getFontSize = (rowIndex, tagIndex) => {
const centerRow = Math.floor(tagRows.value.length / 2);
const distance = Math.abs(rowIndex - centerRow);
return 38 - distance * 8 + (tagIndex % 2) * 5; // 基础24px行距影响4px微调差异2px
};
const getLineHeight = (rowIndex, tagIndex) => {
const centerRow = Math.floor(tagRows.value.length / 2);
const distance = Math.abs(rowIndex - centerRow);
return 48 - distance * 8 + (tagIndex % 2) * 5; // 基础24px行距影响4px微调差异2px
};
const getPaddingLeft = (rowIndex, tagIndex) => {
const centerRow = Math.floor(tagRows.value.length / 2);
const distance = Math.abs(rowIndex - centerRow);
return 30 - distance * 8 + (tagIndex % 2) * 5; // 基础24px行距影响4px微调差异2px
};
const getPadding = (rowIndex, tagIndex) => {
const centerRow = Math.floor(tagRows.value.length / 2);
const distance = Math.abs(rowIndex - centerRow);
return 28 - distance * 8 + (tagIndex % 2) * 5; // 基础24px行距影响4px微调差异2px
};
// 处理API返回数据为tagRows格式
const processTagData = (apiData) => {
const totalGroups = 7; // 总组数
const middleIndex = Math.floor(totalGroups / 2); // 中间位置索引3
const chunkSize = Math.ceil(apiData.length / totalGroups); // 每组大小
const arr = [];
// 1. 先按顺序分组
const tempGroups = [];
for (let i = 0; i < totalGroups; i++) {
const start = i * chunkSize;
const end = start + chunkSize;
tempGroups.push(apiData.slice(start, end));
}
// 2. 从中间开始,左右交替插入
for (let i = 0; i < totalGroups; i++) {
// 计算当前组应该插入的位置
const insertPos = i % 2 === 0 ? middleIndex + i / 2 : middleIndex - Math.ceil(i / 2);
// 确保不越界
if (insertPos >= 0 && insertPos < totalGroups) {
arr[insertPos] = { tags: tempGroups[i], align: 'center' };
}
}
return arr.filter(Boolean); // 移除可能的空项
};
// 监听筛选条件变化
watch([selectedIndustry, selectedTimePeriod], () => {
getIndustryTerms();
});
onMounted(() => {
getIndustryTerms();
});
</script>
<style scoped>
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
}
:deep(.arco-table-tr):hover {
background-color: var(--color-fill-1);
}
:deep(.arco-btn-outline) {
color: #6d4cfe !important;
border-color: #6d4cfe !important;
}
:deep(.arco-modal-body) {
padding: 0px;
}
.multi-row-tag-cloud {
width: 100%;
padding: 20px;
}
.tag-row {
display: flex;
flex-wrap: wrap;
}
/* 悬停放大效果 */
a-tag:hover {
transform: scale(1.1);
z-index: 1;
}
.pop-btn {
background: #fff !important;
border-color: #fff !important;
color: #737478 !important;
margin-left: -5px;
}
</style>

View File

@ -0,0 +1,224 @@
<template>
<view>
<topHeader ref="topHeaderRef"></topHeader>
<!-- tabel -->
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin-bottom: 24px">
<a-space align="center">
<span>行业热门话题洞察</span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<a-table :data="dataList">
<template #columns>
<a-table-column title="排名" data-index="rank">
<template #cell="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
</a-table-column>
<a-table-column title="话题名称" data-index="name" />
<a-table-column title="关键词" data-index="keywords">
<template #cell="{ record }">
<a-tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
</template>
</a-table-column>
<a-table-column title="热度" data-index="heatLevel">
<template #cell="{ record }">
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
</template>
</a-table-column>
<a-table-column title="情感" data-index="sentiment">
<template #cell="{ record }">
<img
v-if="record.felling == '2'"
src="@/assets/img/hottranslation/good.png"
style="width: 16px; height: 16px"
/>
<img
v-else-if="record.felling == '1'"
src="@/assets/img/hottranslation/normal.png"
style="width: 16px; height: 16px"
/>
<img
v-else-if="record.felling == '0'"
src="@/assets/img/hottranslation/poor.png"
style="width: 16px; height: 16px"
/>
</template>
</a-table-column>
<a-table-column title="操作" data-index="optional">
<template #cell="{ record }">
<a-button type="outline" @click="gotoDetail(record)">详情</a-button>
</template>
</a-table-column>
</template>
<template #rank="{ record }">
<a-tag color="blue" v-if="record.rank == 1">1</a-tag>
</template>
</a-table>
</a-space>
<!-- modal -->
<a-modal :visible="visible" @ok="handleOk" @cancel="handleCancel" unmountOnClose>
<template #title>
<span style="text-align: left; width: 100%">行业热门话题洞察</span>
</template>
<div>
<a-space direction="vertical">
<a-space>
<span style="margin-right: 16px">话题名称</span>
<span>{{ topicInfo.name }}</span>
</a-space>
<a-space>
<span style="margin-right: 16px">话题简介</span>
<span>{{ topicInfo.intro }}</span>
</a-space>
<a-space>
<span style="margin-right: 16px">关键词</span>
<a-tag v-for="item in topicInfo.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
</a-space>
<a-space>
<span style="margin-right: 16px">热度指数</span>
<img v-for="i in topicInfo.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
</a-space>
<a-space>
<span style="margin-right: 16px">情感指数</span>
<img
v-if="topicInfo.felling == '2'"
src="@/assets/img/hottranslation/good.png"
style="width: 16px; height: 16px"
/>
<img
v-else-if="topicInfo.felling == '1'"
src="@/assets/img/hottranslation/normal.png"
style="width: 16px; height: 16px"
/>
<img
v-else-if="topicInfo.felling == '0'"
src="@/assets/img/hottranslation/poor.png"
style="width: 16px; height: 16px"
/>
</a-space>
<a-space direction="top">
<span style="margin-right: 16px; width: 60px; font-size: 12px">原始来源 </span>
<a-space direction="vertical" style="margin-left: 15px">
<a-space v-for="item in topicInfo.industry_topic_sources" :key="item">
<a-link style="background-color: initial" :href="item.link">{{ item.title }}</a-link>
<img src="@/assets/img/hottranslation/xhs.png" style="width: 16px; height: 16px" />
</a-space>
</a-space>
</a-space>
</a-space>
</div>
</a-modal>
</view>
</template>
<script setup>
import topHeader from './topHeader.vue';
import { ref, computed } from 'vue';
import { fetchIndustriesTree, fetchIndustryTopics, fetchIndustryTopicDetail } from '@/api/all/index';
import star1 from '@/assets/img/hottranslation/star-fill1.png';
import star2 from '@/assets/img/hottranslation/star-fill2.png';
import star3 from '@/assets/img/hottranslation/star-fill3.png';
import star4 from '@/assets/img/hottranslation/star-fill4.png';
import star5 from '@/assets/img/hottranslation/star-fill5.png';
import top1 from '@/assets/img/captcha/top1.svg';
import top2 from '@/assets/img/captcha/top2.svg';
import top3 from '@/assets/img/captcha/top3.svg';
const starImages = [star1, star2, star3, star4, star5];
const topImages = [top1, top2, top3];
// 行业大类
const industriesTree = ref([]);
// 行业热门话题洞察
const dataList = ref([]);
// 显示详情
const visible = ref(false);
const topicInfo = ref({});
const topHeaderRef = ref();
// 从topHeader获取统一的状态
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
// 监听筛选条件变化
watch([selectedIndustry, selectedTimePeriod], () => {
getIndustryTopics();
});
onMounted(() => {
getIndustriesTree();
});
// 获取行业大类数据
const getIndustriesTree = async () => {
const res = await fetchIndustriesTree();
industriesTree.value = res;
selectedIndustry.value = res[0].id;
getIndustryTopics();
};
// 行业热门话题
const getIndustryTopics = async () => {
let parms = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchIndustryTopics(parms);
console.log(res);
dataList.value = res;
};
// 详情
const gotoDetail = async (record) => {
console.log(record);
const res = await fetchIndustryTopicDetail(record.id);
console.log(res);
visible.value = true;
topicInfo.value = res;
};
// 弹窗的取消
const handleCancel = () => {
visible.value = false;
};
// 弹窗的确定
const handleOk = () => {
visible.value = false;
};
</script>
<style scoped>
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
}
:deep(.arco-table-tr):hover {
background-color: var(--color-fill-1);
}
:deep(.arco-btn-outline) {
color: #6d4cfe !important;
border-color: #6d4cfe !important;
}
:deep(.arco-modal-body) {
padding: 0px;
}
.pop-btn {
background: #fff !important;
border-color: #fff !important;
color: #737478 !important;
margin-left: -5px;
}
</style>

View File

@ -0,0 +1,165 @@
<template>
<view>
<topHeader ref="topHeaderRef"></topHeader>
<!-- 重点品牌列表 -->
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
<a-space align="center">
<span>重点品牌列表 </span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<a-table :data="dataList" :pagination="false" style="font-size: 12px">
<template #columns>
<a-table-column title="排名" data-index="rank">
<template #cell="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
</a-table-column>
<a-table-column title="品牌名称" data-index="name" />
<a-table-column title="热度指数" data-index="keywords">
<template #cell="{ record }">
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
</template>
</a-table-column>
<a-table-column title="变化幅度" data-index="frequency">
<template #cell="{ record }">
<a-statistic
style="font-size: 14px"
v-if="record.trend > 0"
:value="record.trend * 100"
:value-style="{ color: '#F64B31' }"
>
<template #prefix>
<icon-arrow-rise />
</template>
<template #suffix>%</template>
</a-statistic>
<a-statistic
v-else
style="font-size: 14px"
:value="record.trend * 100"
:value-style="{ color: '#25C883' }"
></a-statistic>
</template>
</a-table-column>
<a-table-column title="占总声量比例" data-index="content">
<template #cell="{ record }"> <a-statistic :value="record.volume_rate * 100" />% </template>
</a-table-column>
</template>
</a-table>
</a-space>
<!-- 舆情 & 敏感动态-->
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
<a-space align="center">
<span>舆情 & 敏感动态 </span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<a-table :data="otherList" :pagination="false" style="font-size: 12px">
<template #columns>
<a-table-column title="品牌" data-index="brand" />
<a-table-column title="事件标题" data-index="title" />
<a-table-column title="事件详情" data-index="content" />
</template>
</a-table>
</a-space>
</view>
</template>
<script setup>
import topHeader from './topHeader.vue';
import { fetchFocusBrandsList, fetchEventDynamicsList } from '@/api/all/index';
import { ref, onMounted, computed } from 'vue';
import star1 from '@/assets/img/hottranslation/star-fill1.png';
import star2 from '@/assets/img/hottranslation/star-fill2.png';
import star3 from '@/assets/img/hottranslation/star-fill3.png';
import star4 from '@/assets/img/hottranslation/star-fill4.png';
import star5 from '@/assets/img/hottranslation/star-fill5.png';
import top1 from '@/assets/img/captcha/top1.svg';
import top2 from '@/assets/img/captcha/top2.svg';
import top3 from '@/assets/img/captcha/top3.svg';
const topImages = [top1, top2, top3];
const starImages = [star1, star2, star3, star4, star5];
const topHeaderRef = ref();
// 从topHeader获取统一的状态
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
const dataList = ref([]);
const otherList = ref([]);
const getFocusBrandsList = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchFocusBrandsList(params);
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
dataList.value = res;
};
const getEventDynamicsList = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchEventDynamicsList(params);
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
otherList.value = res;
};
onMounted(() => {
getFocusBrandsList();
getEventDynamicsList();
});
// 监听筛选条件变化
watch([selectedIndustry, selectedTimePeriod], () => {
getFocusBrandsList();
getEventDynamicsList();
});
</script>
<style scoped>
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
}
:deep(.arco-table-tr):hover {
background-color: var(--color-fill-1);
}
:deep(.arco-statistic-content .arco-statistic-value-integer) {
font-size: 14px;
}
.pop-btn {
background: #fff !important;
border-color: #fff !important;
color: #737478 !important;
margin-left: -5px;
}
:deep(.arco-btn-outline) {
color: #6d4cfe !important;
border-color: #6d4cfe !important;
}
</style>

View File

@ -0,0 +1,359 @@
<template>
<view>
<topHeader ref="topHeaderRef"></topHeader>
<!-- 关键词热度榜 -->
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
<a-space align="center">
<span>关键词热度榜</span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<a-table :data="dataList" :pagination="false">
<template #columns>
<a-table-column title="排名" data-index="rank">
<template #cell="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
</a-table-column>
<a-table-column title="关键词名称" data-index="name" />
<a-table-column title="热度指数" data-index="heatLevel">
<template #cell="{ record }">
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
</template>
</a-table-column>
<a-table-column title="变化幅度" data-index="heatLevel">
<template #cell="{ record }">
<a-statistic
style="font-size: 14px"
v-if="record.trend > 0"
:value="record.trend * 100"
:value-style="{ color: '#F64B31' }"
>
<template #prefix>
<icon-arrow-rise />
</template>
<template #suffix>%</template>
</a-statistic>
<a-statistic
v-else
style="font-size: 14px"
:value="record.trend * 100"
:value-style="{ color: '#25C883' }"
></a-statistic>
</template>
</a-table-column>
<a-table-column title="情感倾向" data-index="sentiment">
<template #cell="{ record }">
<img
v-if="record.felling == '2'"
src="@/assets/img/hottranslation/good.png"
style="width: 16px; height: 16px"
/>
<img
v-else-if="record.felling == '1'"
src="@/assets/img/hottranslation/normal.png"
style="width: 16px; height: 16px"
/>
<img
v-else-if="record.felling == '0'"
src="@/assets/img/hottranslation/poor.png"
style="width: 16px; height: 16px"
/>
</template>
</a-table-column>
</template>
</a-table>
</a-space>
<!-- 行业情绪 -->
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
<a-space align="center">
<span>行业情绪</span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<a-space align="center">
<a-space direction="vertical">
<template>
<div ref="chartRef" style="width: 400px; height: 400px"></div>
</template>
</a-space>
<a-space>
<a-space>
<div id="container" style="height: 180px; width: 180px"></div>
<a-space direction="vertical" style="font-size: 14px" v-if="fellingRate.length > 0">
<a-space>
<span style="width: 8px; height: 8px; background-color: #25c883; border-radius: 50%"></span>
<span>正面情绪 </span>
<span style="width: 40px">{{ fellingRate[0] * 100 }}%</span>
</a-space>
<a-space>
<span style="width: 8px; height: 8px; background-color: #f64b31; border-radius: 50%"></span>
<span>负面情绪 </span>
<span style="width: 40px">{{ fellingRate[1] * 100 }}%</span>
</a-space>
</a-space>
</a-space>
<a-table :pagination="false" :span-method="dataSpanMethod" :data="rowData" style="margin-left: 40px">
<template #columns>
<a-table-column title="情绪分布">
<template #cell="{ record }">
<a-space v-if="record.felling == '2'">
<img src="@/assets/img/hottranslation/good.png" style="width: 16px; height: 16px" />
<a-space>正面情绪</a-space>
</a-space>
<a-space v-else-if="record.felling == '1'">
<img src="@/assets/img/hottranslation/normal.png" style="width: 16px; height: 16px" />
<a-space>中性情绪</a-space>
</a-space>
<a-space v-else-if="record.felling == '0'">
<img src="@/assets/img/hottranslation/poor.png" style="width: 16px; height: 16px" />
<a-space>负面情绪</a-space>
</a-space>
</template>
</a-table-column>
<a-table-column title="主要观点" data-index="content" />
</template>
</a-table>
</a-space>
</a-space>
</a-space>
<!-- 新兴关键词 -->
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
<a-space align="center">
<span>新兴关键词 </span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<a-table :data="keywordList" :pagination="false">
<template #columns>
<a-table-column title="排名" data-index="rank" />
<a-table-column title="新兴关键词名称" data-index="name" />
<a-table-column title="首次大规模出现" data-index="first_appeared_at">
<template #cell="{ record }">
<div>{{ formatTimestamp(record.first_appeared_at) }}</div>
</template>
</a-table-column>
<a-table-column title="当前热度指数" data-index="heatLevel">
<template #cell="{ record }">
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
</template>
</a-table-column>
<a-table-column title="变化幅度" data-index="trend">
<template #cell="{ record }">
<a-statistic
style="font-size: 14px"
v-if="record.trend > 0"
:value="record.trend * 100"
:value-style="{ color: '#F64B31' }"
>
<template #prefix>
<icon-arrow-rise />
</template>
<template #suffix>%</template>
</a-statistic>
<a-statistic
v-else
style="font-size: 14px"
:value="record.trend * 100"
:value-style="{ color: '#25C883' }"
></a-statistic>
</template>
</a-table-column>
</template>
</a-table>
</a-space>
</view>
</template>
<script setup>
import topHeader from './topHeader.vue';
import { fetchKeywordTrendsList, fetchIndustryEmotions, fetchNewKeywordList } from '@/api/all/index';
import { ref, onMounted, onBeforeUnmount, watchEffect, computed } from 'vue';
import * as echarts from 'echarts';
import star1 from '@/assets/img/hottranslation/star-fill1.png';
import star2 from '@/assets/img/hottranslation/star-fill2.png';
import star3 from '@/assets/img/hottranslation/star-fill3.png';
import star4 from '@/assets/img/hottranslation/star-fill4.png';
import star5 from '@/assets/img/hottranslation/star-fill5.png';
import top1 from '@/assets/img/captcha/top1.svg';
import top2 from '@/assets/img/captcha/top2.svg';
import top3 from '@/assets/img/captcha/top3.svg';
const starImages = [star1, star2, star3, star4, star5];
const topImages = [top1, top2, top3];
const chartRef = (ref < HTMLElement) | (null > null);
const topHeaderRef = ref();
// 从topHeader获取统一的状态
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
const dataList = ref([]);
const rowData = ref([]);
const keywordList = ref([]);
const fellingRate = ref([]);
const getIndustryEmotions = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchIndustryEmotions(params);
fellingRate.value.push(res['good_felling_rate']);
fellingRate.value.push(res['bad_felling_rate']);
drawChart();
rowData.value = res['industry_emotion_view_points'];
let items = groupedData();
console.log('行业情绪', items);
};
const groupedData = () => {
const groups = {
negative: { name: '负面', items: [], color: '#F64B31' },
neutral: { name: '中性', items: [], color: '#FFAA16' },
positive: { name: '正面', items: [], color: '#25C883' },
};
rowData.value.forEach((item) => {
if (item.felling === 0) groups.negative.items.push(item);
else if (item.felling === 1) groups.neutral.items.push(item);
else if (item.felling === 2) groups.positive.items.push(item);
});
return groups;
};
const getKeywordTrendsList = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchKeywordTrendsList(params);
console.log('关键词热度榜', res);
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
dataList.value = res;
};
const formatTimestamp = (timestamp) => {
if (!timestamp) return '未记录';
try {
return dayjs.unix(timestamp).format('YYYY-MM-DD HH:mm');
} catch (e) {
console.error('时间格式转换错误', e);
return '格式错误';
}
};
const getNewKeywordList = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchNewKeywordList(params);
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
keywordList.value = res;
};
const drawChart = () => {
var dom = document.getElementById('container');
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false,
});
var option;
option = {
color: ['#25C883', '#F64B31'],
series: [
{
type: 'pie',
avoidLabelOverlap: false,
data: fellingRate.value,
labelLine: {
show: false, // 不显示引导线
},
radius: ['40%', '55%'],
},
],
};
if (option && typeof option === 'object') {
myChart.setOption(option);
}
};
// 监听筛选条件变化
watch([selectedIndustry, selectedTimePeriod], () => {
getKeywordTrendsList();
getIndustryEmotions();
getNewKeywordList();
});
onMounted(() => {
getKeywordTrendsList();
getIndustryEmotions();
getNewKeywordList();
});
// const chartData = computed(() => {
// const result = [
// { name: '正面', value: 0, color: '#25C883' },
// { name: '中性', value: 0, color: '#FFAA16' },
// { name: '负面', value: 0, color: '#F64B31' },
// ];
// rawData.value.forEach((item) => {
// if (item.felling === 2) result[0].value++;
// else if (item.felling === 1) result[1].value++;
// else result[2].value++;
// });
// return result.filter((item) => item.value > 0); // 过滤空数据
// });
</script>
<style scoped>
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
}
:deep(.arco-table-tr):hover {
background-color: var(--color-fill-1);
}
:deep(.arco-statistic-content .arco-statistic-value-integer) {
font-size: 14px;
}
.pop-btn {
background: #fff !important;
border-color: #fff !important;
color: #737478 !important;
margin-left: -5px;
}
</style>

View File

@ -0,0 +1,217 @@
<template>
<view>
<!-- 头部 -->
<a-space
direction="vertical"
style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0; color: #737478; font-size: 14px"
>
<a-space align="center">
<!-- 行业选择 -->
<a-space align="center">
<span>行业大类</span>
<a-tag
size="Medium"
v-for="item in industriesTree"
:key="item.id"
:checkable="true"
:checked="selectedIndustry == item.id"
@check="handleIndustryCheck(item.id)"
style="padding: 4px 16px; border-radius: 30px; height: 28px"
:style="
selectedIndustry == item.id
? 'color: #6d4cfe; background-color: #f0edff'
: 'color: #3C4043; background-color: #F7F8FA'
"
>{{ item.name }}</a-tag
>
</a-space>
</a-space>
<a-space align="center" style="margin-left: 'auto'; margin-top: 20px">
<!-- 二级类目 -->
<a-space align="center">
<span>二级类目</span>
<a-tag
size="Medium"
v-for="item in subCategories"
:key="item.value"
:checkable="true"
:checked="selectedSubCategory == item.value"
@check="handleSubCategoryCheck(item.value)"
style="padding: 4px 16px; border-radius: 30px; height: 28px"
:style="
selectedSubCategory == item.value
? 'color: #6d4cfe; background-color: #f0edff'
: 'color: #3C4043; background-color: #F7F8FA'
"
>{{ item.label }}</a-tag
>
</a-space>
</a-space>
<a-space align="center" style="margin-left: 'auto'; margin-top: 20px">
<!-- 时间筛选 -->
<a-space align="center">
<span>时间筛选</span>
<a-tag
size="Medium"
v-for="item in timePeriods"
:key="item.value"
:checkable="true"
:checked="selectedTimePeriod == item.value"
@check="handleTimePeriodCheck(item.value)"
style="padding: 4px 16px; border-radius: 30px; height: 28px"
:style="
selectedTimePeriod == item.value
? 'color: #6d4cfe; background-color: #f0edff'
: 'color: #3C4043; background-color: #F7F8FA'
"
>{{ item.label }}
</a-tag>
</a-space>
</a-space>
<!-- 搜索区域 -->
<a-space style="margin-left: 'auto'; margin-top: 20px">
<a-button type="primary" @click="handleSearch">
<template #icon>
<icon-search />
</template>
<!-- Use the default slot to avoid extra spaces -->
<template #default>搜索</template>
</a-button>
<a-button type="primary" style="background-color: #fff; color: #000">
<template #icon>
<icon-refresh />
</template>
<!-- Use the default slot to avoid extra spaces -->
<template #default>重置</template>
</a-button>
</a-space>
</a-space>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import { fetchIndustriesTree, fetchIndustryTopics, fetchIndustryTopicDetail } from '@/api/all/index';
import star1 from '@/assets/img/hottranslation/star-fill1.png';
import star2 from '@/assets/img/hottranslation/star-fill2.png';
import star3 from '@/assets/img/hottranslation/star-fill3.png';
import star4 from '@/assets/img/hottranslation/star-fill4.png';
import star5 from '@/assets/img/hottranslation/star-fill5.png';
const starImages = [star1, star2, star3, star4, star5];
// 行业大类
const industriesTree = ref([]);
// 数据状态
const selectedIndustry = ref();
const selectedSubCategory = ref('all');
const selectedTimePeriod = ref('7');
// 暴露这些状态给父组件
defineExpose({
selectedIndustry,
selectedSubCategory,
selectedTimePeriod,
});
// 行业热门话题洞察
const dataList = ref([]);
// 显示详情
const visible = ref(false);
const topicInfo = ref({});
// 二级类目选项
const subCategories = [
{ value: 'all', label: '全部' },
{ value: 'airline', label: '航司' },
{ value: 'hotel', label: '酒店' },
{ value: 'entertainment', label: '玩乐' },
{ value: 'cruise', label: '游轮' },
];
// 时间周期选项
const timePeriods = [
{
value: '7',
label: '近7天',
},
{
value: '15',
label: '近15天',
},
{
value: '30',
label: '近30天',
},
];
onMounted(() => {
getIndustriesTree();
});
// 获取行业大类数据
const getIndustriesTree = async () => {
const res = await fetchIndustriesTree();
industriesTree.value = res;
selectedIndustry.value = res[0].id;
getIndustryTopics();
};
// 行业热门话题
const getIndustryTopics = async () => {
let parms = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchIndustryTopics(parms);
dataList.value = res;
};
const handleIndustryCheck = (value) => {
selectedIndustry.value = value;
};
const handleSubCategoryCheck = (value) => {
selectedSubCategory.value = value;
};
const handleTimePeriodCheck = (value) => {
selectedTimePeriod.value = value;
};
// 详情
const gotoDetail = async (record) => {
console.log(record);
const res = await fetchIndustryTopicDetail(record.id);
console.log(res);
visible.value = true;
topicInfo.value = res;
};
// 搜索
const handleSearch = () => {
getIndustryTopics();
};
// 弹窗的取消
const handleCancel = () => {
visible.value = false;
};
// 弹窗的确定
const handleBeforeOk = () => {
visible.value = false;
};
</script>
<style scoped>
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
}
:deep(.arco-table-tr):hover {
background-color: var(--color-fill-1);
}
:deep(.arco-btn-outline) {
color: #6d4cfe !important;
border-color: #6d4cfe !important;
}
:deep(.arco-modal-body) {
padding: 0px;
}
</style>

View File

@ -0,0 +1,191 @@
<template>
<view>
<topHeader ref="topHeaderRef"></topHeader>
<!-- 用户痛点观察 -->
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
<a-space align="center">
<span>用户痛点观察 </span>
<a-popover position="tl">
<a-button type="primary" class="pop-btn">
<template #icon>
<icon-question-circle />
</template>
</a-button>
<template #content>
<p>基于xxx获取数据xxx一段文字描述该数据的获取方式和来源等xxx</p>
</template>
</a-popover>
</a-space>
<a-table :data="dataList" :pagination="false">
<template #columns>
<a-table-column title="排名" data-index="rank">
<template #cell="{ record }">
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
<span v-else>{{ record.rank }}</span>
</template>
</a-table-column>
<a-table-column title="痛点名称" data-index="name" />
<a-table-column title="关键词" data-index="keywords">
<template #cell="{ record }">
<a-tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
</template>
</a-table-column>
<a-table-column title="频次" data-index="frequency">
<template #cell="{ record }">
<a-tag v-if="record.frequency == 0" style="margin-right: 5px; background-color: #ebf7f2; color: #1bae71"
>低频</a-tag
>
<a-tag
v-else-if="record.frequency == 1"
style="margin-right: 5px; background-color: #fff5de; color: #cc8b00"
>中频</a-tag
>
<a-tag
v-else-if="record.frequency == 2"
style="margin-right: 5px; background-color: #ffe7e4; color: #c53c27"
>高频</a-tag
>
</template>
</a-table-column>
<a-table-column title="代表性发言" data-index="content"> </a-table-column>
<a-table-column title="操作" data-index="optional">
<template #cell="{ record }">
<a-button type="outline" @click="gotoDetail(record)">详情</a-button>
</template>
</a-table-column>
</template>
</a-table>
</a-space>
<a-modal :visible="visible" @ok="handleOk" @cancel="handleCancel" unmountOnClose>
<template #title>
<span style="text-align: left; width: 100%">用户痛点观察</span>
</template>
<div>
<a-space direction="vertical" style="font-size: 12px">
<a-space>
<span style="margin-right: 16px; width: 60px">痛点</span>
<span>{{ topicInfo.name }}</span>
</a-space>
<a-space>
<span style="margin-right: 16px; width: 60px; font-size: 12px">关键词</span>
<a-tag v-for="item in topicInfo.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
</a-space>
<a-space>
<span style="margin-right: 16px; width: 60px; font-size: 12px">频次</span>
<a-tag v-if="topicInfo.frequency == 0" style="margin-right: 5px; background-color: #ebf7f2; color: #1bae71"
>低频</a-tag
>
<a-tag
v-else-if="topicInfo.frequency == 1"
style="margin-right: 5px; background-color: #fff5de; color: #cc8b00"
>中频</a-tag
>
<a-tag
v-else-if="topicInfo.frequency == 2"
style="margin-right: 5px; background-color: #ffe7e4; color: #c53c27"
>高频</a-tag
>
</a-space>
<a-space>
<span style="margin-right: 16px; width: 60px; font-size: 12px">代表性发言</span>
<span>{{ topicInfo.content }}</span>
</a-space>
<a-space direction="top">
<span style="margin-right: 16px; width: 60px; font-size: 12px">原始来源 </span>
<a-space direction="vertical" style="margin-left: 15px">
<a-space v-for="item in topicInfo.user_pain_point_sources" :key="item">
<a-link style="background-color: initial" :href="item.link">{{ item.title }}</a-link>
<img src="@/assets/img/hottranslation/xhs.png" style="width: 16px; height: 16px" />
</a-space>
</a-space>
</a-space>
</a-space>
</div>
</a-modal>
</view>
</template>
<script setup>
import topHeader from './topHeader.vue';
import { fetchUserPainPointsDetail, fetchUserPainPointsList } from '@/api/all/index';
import { ref, onMounted, computed } from 'vue';
import top1 from '@/assets/img/captcha/top1.svg';
import top2 from '@/assets/img/captcha/top2.svg';
import top3 from '@/assets/img/captcha/top3.svg';
const topImages = [top1, top2, top3];
const visible = ref(false);
const topicInfo = ref({});
const topHeaderRef = ref();
// 从topHeader获取统一的状态
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
const dataList = ref([]);
// 详情
const gotoDetail = async (record) => {
console.log(record);
const res = await fetchUserPainPointsDetail(record.id);
console.log(res);
visible.value = true;
topicInfo.value = res;
};
const getUserPainPointsList = async () => {
const params = {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
const res = await fetchUserPainPointsList(params);
console.log('关键词热度榜', res);
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
dataList.value = res;
};
// 弹窗的取消
const handleCancel = () => {
visible.value = false;
};
// 弹窗的确定
const handleOk = () => {
visible.value = false;
};
// 监听筛选条件变化
watch([selectedIndustry, selectedTimePeriod], () => {
getUserPainPointsList();
});
onMounted(() => {
getUserPainPointsList();
});
</script>
<style scoped>
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
}
:deep(.arco-table-tr):hover {
background-color: var(--color-fill-1);
}
:deep(.arco-statistic-content .arco-statistic-value-integer) {
font-size: 14px;
}
.pop-btn {
background: #fff !important;
border-color: #fff !important;
color: #737478 !important;
margin-left: -5px;
}
:deep(.arco-btn-outline) {
color: #6d4cfe !important;
border-color: #6d4cfe !important;
}
</style>

Some files were not shown because too many files have changed in this diff Show More