Commit 844d66de by 王立鹏

Merge branch 'feature/17679-企业文化平台搭建' into 'master'

Feature/17679 企业文化平台搭建

See merge request !1
parents fef463d6 84a098a3
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"DirectiveBinding": true,
"EffectScope": true,
"ElMessage": true,
"ElMessageBox": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"PropType": true,
"Ref": true,
"ShallowRef": true,
"Slot": true,
"Slots": true,
"VNode": true,
"WritableComputedRef": true,
"computed": true,
"createApp": true,
"customRef": true,
"defineAsyncComponent": true,
"defineComponent": true,
"effectScope": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"getCurrentWatcher": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"isShallow": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeRouteLeave": true,
"onBeforeRouteUpdate": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onUnmounted": true,
"onUpdated": true,
"onWatcherCleanup": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useId": true,
"useLink": true,
"useModel": true,
"useRoute": true,
"useRouter": true,
"useSlots": true,
"useTemplateRef": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
"watchSyncEffect": true
}
}
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue').EffectScope
const ElMessage: typeof import('element-plus/es').ElMessage
const ElMessageBox: typeof import('element-plus/es').ElMessageBox
const ElSwitch: typeof import('element-plus/es').ElSwitch
const computed: typeof import('vue').computed
const createApp: typeof import('vue').createApp
const customRef: typeof import('vue').customRef
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const defineComponent: typeof import('vue').defineComponent
const effectScope: typeof import('vue').effectScope
const getCurrentInstance: typeof import('vue').getCurrentInstance
const getCurrentScope: typeof import('vue').getCurrentScope
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
const h: typeof import('vue').h
const inject: typeof import('vue').inject
const isProxy: typeof import('vue').isProxy
const isReactive: typeof import('vue').isReactive
const isReadonly: typeof import('vue').isReadonly
const isRef: typeof import('vue').isRef
const isShallow: typeof import('vue').isShallow
const markRaw: typeof import('vue').markRaw
const nextTick: typeof import('vue').nextTick
const onActivated: typeof import('vue').onActivated
const onBeforeMount: typeof import('vue').onBeforeMount
const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave
const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
const onDeactivated: typeof import('vue').onDeactivated
const onErrorCaptured: typeof import('vue').onErrorCaptured
const onMounted: typeof import('vue').onMounted
const onRenderTracked: typeof import('vue').onRenderTracked
const onRenderTriggered: typeof import('vue').onRenderTriggered
const onScopeDispose: typeof import('vue').onScopeDispose
const onServerPrefetch: typeof import('vue').onServerPrefetch
const onUnmounted: typeof import('vue').onUnmounted
const onUpdated: typeof import('vue').onUpdated
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
const provide: typeof import('vue').provide
const reactive: typeof import('vue').reactive
const readonly: typeof import('vue').readonly
const ref: typeof import('vue').ref
const resolveComponent: typeof import('vue').resolveComponent
const shallowReactive: typeof import('vue').shallowReactive
const shallowReadonly: typeof import('vue').shallowReadonly
const shallowRef: typeof import('vue').shallowRef
const toRaw: typeof import('vue').toRaw
const toRef: typeof import('vue').toRef
const toRefs: typeof import('vue').toRefs
const toValue: typeof import('vue').toValue
const triggerRef: typeof import('vue').triggerRef
const unref: typeof import('vue').unref
const useAttrs: typeof import('vue').useAttrs
const useCssModule: typeof import('vue').useCssModule
const useCssVars: typeof import('vue').useCssVars
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
const useTemplateRef: typeof import('vue').useTemplateRef
const watch: typeof import('vue').watch
const watchEffect: typeof import('vue').watchEffect
const watchPostEffect: typeof import('vue').watchPostEffect
const watchSyncEffect: typeof import('vue').watchSyncEffect
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import { GlobalComponents } from 'vue'
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ArticleContent: typeof import('./src/components/common/ArticleContent/index.vue')['default']
BaseButton: typeof import('./src/components/common/ElComponents/ElButton/BaseButton.vue')['default']
Comment: typeof import('./src/components/common/Comment/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload']
IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
LoadingComponent: typeof import('./src/components/common/LoadingComponent/index.vue')['default']
PublishBox: typeof import('./src/components/common/PublishBox/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SelectTags: typeof import('./src/components/common/SelectTags/index.vue')['default']
SelectTagsDialog: typeof import('./src/components/common/PublishBox/components/selectTagsDialog.vue')['default']
SvgIcon: typeof import('./src/components/common/SvgIcon/svgIcon.vue')['default']
UploadFile: typeof import('./src/components/common/UploadFile/index.vue')['default']
UploadVideo: typeof import('./src/components/common/UploadVideo/index.vue')['default']
}
export interface GlobalDirectives {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}
// For TSX support
declare global {
const ArticleContent: typeof import('./src/components/common/ArticleContent/index.vue')['default']
const BaseButton: typeof import('./src/components/common/ElComponents/ElButton/BaseButton.vue')['default']
const Comment: typeof import('./src/components/common/Comment/index.vue')['default']
const ElAlert: typeof import('element-plus/es')['ElAlert']
const ElAvatar: typeof import('element-plus/es')['ElAvatar']
const ElButton: typeof import('element-plus/es')['ElButton']
const ElCard: typeof import('element-plus/es')['ElCard']
const ElCarousel: typeof import('element-plus/es')['ElCarousel']
const ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
const ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
const ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
const ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
const ElDialog: typeof import('element-plus/es')['ElDialog']
const ElDivider: typeof import('element-plus/es')['ElDivider']
const ElDropdown: typeof import('element-plus/es')['ElDropdown']
const ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
const ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
const ElEmpty: typeof import('element-plus/es')['ElEmpty']
const ElForm: typeof import('element-plus/es')['ElForm']
const ElFormItem: typeof import('element-plus/es')['ElFormItem']
const ElIcon: typeof import('element-plus/es')['ElIcon']
const ElImage: typeof import('element-plus/es')['ElImage']
const ElInput: typeof import('element-plus/es')['ElInput']
const ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
const ElLink: typeof import('element-plus/es')['ElLink']
const ElMenu: typeof import('element-plus/es')['ElMenu']
const ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
const ElOption: typeof import('element-plus/es')['ElOption']
const ElPagination: typeof import('element-plus/es')['ElPagination']
const ElPopover: typeof import('element-plus/es')['ElPopover']
const ElProgress: typeof import('element-plus/es')['ElProgress']
const ElRadio: typeof import('element-plus/es')['ElRadio']
const ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
const ElSelect: typeof import('element-plus/es')['ElSelect']
const ElSlider: typeof import('element-plus/es')['ElSlider']
const ElSwitch: typeof import('element-plus/es')['ElSwitch']
const ElTable: typeof import('element-plus/es')['ElTable']
const ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
const ElTabPane: typeof import('element-plus/es')['ElTabPane']
const ElTabs: typeof import('element-plus/es')['ElTabs']
const ElTag: typeof import('element-plus/es')['ElTag']
const ElTooltip: typeof import('element-plus/es')['ElTooltip']
const ElUpload: typeof import('element-plus/es')['ElUpload']
const IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
const IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
const IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
const IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
const IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
const LoadingComponent: typeof import('./src/components/common/LoadingComponent/index.vue')['default']
const PublishBox: typeof import('./src/components/common/PublishBox/index.vue')['default']
const RouterLink: typeof import('vue-router')['RouterLink']
const RouterView: typeof import('vue-router')['RouterView']
const SelectTags: typeof import('./src/components/common/SelectTags/index.vue')['default']
const SelectTagsDialog: typeof import('./src/components/common/PublishBox/components/selectTagsDialog.vue')['default']
const SvgIcon: typeof import('./src/components/common/SvgIcon/svgIcon.vue')['default']
const UploadFile: typeof import('./src/components/common/UploadFile/index.vue')['default']
const UploadVideo: typeof import('./src/components/common/UploadVideo/index.vue')['default']
}
\ No newline at end of file
import cp from 'node:child_process'
import ssh from 'ssh2'
import archiver from 'archiver'
import fs from 'node:fs'
import path from 'node:path'
const unzipDirMode = {
spawn: 'pnpm run build:test',
// 解压文件名
unzipDir: 'culture/',
// 终端定位位置 cd
servicePath: '/usr/local/nginx/',
// 压缩包存放位置
serviceFilePath: '/usr/local/nginx/dist.tar.gz',
}
const __dirname = path.resolve()
// 文件所在地
const distPath = path.resolve(__dirname, 'dist')
// 打包后位置
const zipPath = path.resolve(__dirname, 'dist.tar.gz')
// 远程服务器存放位置
const { spawn, servicePath, serviceFilePath, unzipDir } = unzipDirMode
// 服务器连接信息
const connectInfo = {
host: '47.119.149.50',
port: '22',
username: 'root',
password: 'Qdt20250205',
}
//链接服务器
let conn = new ssh.Client()
async function start() {
try {
// 1. 打包
await build()
// 2. 压缩zip
await startZip()
// 3. 将zip文件传输至远程服务器
await connect()
// 4. 部署解压
await shellCmd(conn)
console.log('部署完成')
} catch (error) {
console.error('Error:', error.message)
} finally {
// 5. 断开ssh,并删除本地压缩包
conn.end()
delZip()
}
}
start()
/**
* 1. 本地构建项目
*/
function build() {
return new Promise((resolve, reject) => {
//对项目进行打包,然后生成压缩文件
let pro = cp.spawn(spawn, {
shell: true,
stdio: 'inherit',
})
pro.on('exit', (code) => {
//打包完成后 开始链接目标服务器,并自动部署
if (code === 0) {
console.log('---构建成功---')
resolve()
} else {
reject(new Error('构建失败'))
}
})
})
}
/**
* 2. 将打包后文件压缩zip
* @returns
*/
function startZip() {
return new Promise((resolve, reject) => {
console.log('开始打包tar')
//定义打包格式和相关配置
const archive = archiver('tar', {
gzip: true, // 如果需要压缩,可以使用 gzip
gzipOptions: { level: 9 }, // gzip 压缩级别
}).on('error', (err) => reject(err))
const output = fs.createWriteStream(zipPath)
//监听流的打包
output.on('close', (err) => {
console.log('目标打包完成')
resolve(true)
})
//开始压缩
archive.pipe(output)
// 文件夹压缩
archive.directory(distPath, false)
archive.finalize()
})
}
/**
* 3. 将zip文件传输至远程服务器
*/
function connect() {
return new Promise((resolve, reject) => {
conn
.on('ready', () => {
conn.sftp((err, sftp) => {
if (err) {
return reject(err)
}
sftp.fastPut(zipPath, serviceFilePath, {}, (err, result) => {
if (err) {
return reject(err)
}
//开始上传
console.log('文件上传成功')
resolve()
})
})
})
.on('error', (err) => reject(err))
.connect(connectInfo)
})
}
/**
* 4. 解压部署操作
* @param {*} conn
*/
async function shellCmd(conn) {
return new Promise((resolve, reject) => {
conn.shell((err, stream) => {
if (err) {
return reject(err)
}
//进入服务器暂存地址
//解压上传的压缩包
//移动解压后的文件到发布目录
//删除压缩包
//退出
const commands = `
cd ${servicePath} &&
mkdir -p ${unzipDir} &&
tar -xzf dist.tar.gz -C ${unzipDir} &&
rm -rf dist.tar.gz &&
exit
`
console.log('终端执行命令:', commands)
stream
.on('close', () => resolve())
.on('data', (data) => console.log(data.toString()))
.stderr.on('data', (data) => console.error('Error:', data.toString()))
stream.end(commands)
})
})
}
/**
* 5. 删除本地的dist.zip
*/
function delZip() {
fs.unlink(zipPath, function (err) {
if (err) {
console.error('删除文件失败:', err.message)
}
console.log('文件:' + zipPath + '删除成功!')
})
}
...@@ -4,8 +4,8 @@ import pluginVue from 'eslint-plugin-vue' ...@@ -4,8 +4,8 @@ import pluginVue from 'eslint-plugin-vue'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: // To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript' import { configureVueProject } from '@vue/eslint-config-typescript'
// configureVueProject({ scriptLangs: ['ts', 'tsx'] }) configureVueProject({ scriptLangs: ['ts', 'tsx'] })
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup // More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
export default defineConfigWithVueTs( export default defineConfigWithVueTs(
...@@ -19,4 +19,16 @@ export default defineConfigWithVueTs( ...@@ -19,4 +19,16 @@ export default defineConfigWithVueTs(
pluginVue.configs['flat/essential'], pluginVue.configs['flat/essential'],
vueTsConfigs.recommended, vueTsConfigs.recommended,
skipFormatting, skipFormatting,
{
rules: {
'vue/multi-word-component-names': [
'error',
{
ignores: ['index'],
},
],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
},
) )
<!DOCTYPE html> <!DOCTYPE html>
<html lang=""> <html lang="">
<head>
<head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/webicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title> <title>企业文化平台</title>
</head> </head>
<body>
<body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -13,10 +13,20 @@ ...@@ -13,10 +13,20 @@
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --build", "type-check": "vue-tsc --build",
"lint": "eslint . --fix --cache", "lint": "eslint . --fix --cache",
"format": "prettier --write src/" "format": "prettier --write src/",
"deploy:test": "node deploy/deploytest.js",
"build:test": "nvm use 20 && vite build --mode test"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@vueuse/components": "^14.0.0",
"@vueuse/core": "^14.0.0",
"archiver": "^7.0.1",
"axios": "^1.13.0",
"dayjs": "^1.11.19",
"element-plus": "^2.11.5",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"ssh2": "^1.17.0",
"vue": "^3.5.22", "vue": "^3.5.22",
"vue-router": "^4.6.3" "vue-router": "^4.6.3"
}, },
...@@ -33,8 +43,13 @@ ...@@ -33,8 +43,13 @@
"jiti": "^2.6.1", "jiti": "^2.6.1",
"npm-run-all2": "^8.0.4", "npm-run-all2": "^8.0.4",
"prettier": "3.6.2", "prettier": "3.6.2",
"sass-embedded": "^1.93.2",
"typescript": "~5.9.0", "typescript": "~5.9.0",
"unocss": "^66.5.4",
"unplugin-auto-import": "^20.2.0",
"unplugin-vue-components": "^30.0.0",
"vite": "^7.1.11", "vite": "^7.1.11",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-devtools": "^8.0.3", "vite-plugin-vue-devtools": "^8.0.3",
"vue-tsc": "^3.1.1" "vue-tsc": "^3.1.1"
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
</script>
<template> <template>
<header> <el-config-provider :locale="locale">
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" /> <router-view v-slot="{ Component }">
<component :is="Component" />
<div class="wrapper"> </router-view>
<HelloWorld msg="You did it!" /> </el-config-provider>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<RouterView />
</template> </template>
<script setup lang="ts">
<style scoped> import zhCn from 'element-plus/es/locale/lang/zh-cn'
header { import { useUserStore } from '@/stores/user'
line-height: 1.5; const locale = ref(zhCn)
max-height: 100vh; // const userStore = useUserStore()
} // userStore.fetchUserInfo().then((res) => {
// console.log(res)
.logo { // })
display: block; </script>
margin: 0 auto 2rem;
}
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
}
nav a.router-link-exact-active {
color: var(--color-text);
}
nav a.router-link-exact-active:hover {
background-color: transparent;
}
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
border: 0;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
nav {
text-align: left;
margin-left: -1rem;
font-size: 1rem;
padding: 1rem 0;
margin-top: 1rem;
}
}
</style>
import service from '@/utils/request/index'
import type {
AddOrUpdateArticleDto,
ArticleItemDto,
ArticleSearchParams,
InterviewOptionDto,
ColumnOptionDto,
AddCommentDto,
CommentItemDto,
CommentSearchParams,
InterviewItemDto,
ColumnItemDto,
} from './types'
import type { BackendServicePageResult, PageSearchParams } from '@/utils/request/types'
// 文章相关的接口(帖子 视频 实践等)
/**
* 发布文章 暂时由 有帖子 专栏 专访
*/
export const addOrUpdateArticle = (data: AddOrUpdateArticleDto) => {
return service.request<boolean>({
url: '/api/cultureArticle/addOrUpdateArticle',
method: 'POST',
data,
})
}
/**
* 文章列表
*/
export const getArticleList = (data: ArticleSearchParams) => {
return service.request<BackendServicePageResult<ArticleItemDto>>({
url: '/api/cultureArticle/listByPage',
method: 'POST',
data,
})
}
/**
* 文章详情
*/
export const getArticleDetail = (articleId: number | string) => {
return service.request<ArticleItemDto>({
url: `/api/cultureArticle/getArticleDetail?articleId=${articleId}`,
method: 'POST',
})
}
/**
* 获取专访列表选项 --不分页 用户新增时候选择
*/
export const getInterviewOptions = () => {
return service.request<InterviewOptionDto[]>({
url: '/api/cultureColumn/listNoPage?type=interview',
method: 'POST',
})
}
/**
* 获取首页专访列表list —— 分页
*/
export const getInterviewList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<InterviewItemDto>>({
url: '/api/yaCulture/listByPage',
method: 'POST',
data: {
...data,
type: 'interview',
},
})
}
/**
* 获取专栏列表选项-- 不分页 用户新增时候选择
*/
export const getColumnOptions = () => {
return service.request<ColumnOptionDto[]>({
url: '/api/cultureColumn/listNoPage?type=column',
method: 'POST',
})
}
/**
* 获取首页专栏列表list —— 分页
*/
export const getColumnList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<ColumnItemDto>>({
url: '/api/yaCulture/listByPage',
method: 'POST',
data: {
...data,
type: 'column',
},
})
}
/**
* 获取首页视频列表list —— 分页
*/
export const getVideoList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<ColumnItemDto>>({
url: '/api/yaCulture/listByPage',
method: 'POST',
data: {
...data,
type: 'video',
},
})
}
/**
* 点赞或者取消点赞文章
*/
export const addOrCanceArticlelLike = (articleId: number | string) => {
return service.request<boolean>({
url: `/api/cultureArticle/likeArticle?articleId=${articleId}`,
method: 'POST',
})
}
/**
* 收藏或者取消收藏文章
*/
export const addOrCanceArticlelCollect = (articleId: number | string) => {
return service.request<boolean>({
url: `/api/cultureArticle/collectArticle?articleId=${articleId}`,
method: 'POST',
})
}
/**
* 获取评论列表
*/
export const getCommentList = (data: CommentSearchParams) => {
return service.request<BackendServicePageResult<CommentItemDto>>({
url: `/api/cultureComment/getComment`,
method: 'POST',
data,
})
}
/**
* 新增评论
*/
export const addComment = (data: AddCommentDto) => {
return service.request<boolean>({
url: `/api/cultureComment/addComment`,
method: 'POST',
data,
})
}
/**
* 点赞评论
*/
export const addOrCancelCommentLike = (commentId: number | string) => {
return service.request<boolean>({
url: `/api/cultureComment/likeComment?commentId=${commentId}`,
method: 'POST',
})
}
/**
* 修改文章的是否推荐/置顶字段
*/
export const updateArticleRecommend = (articleId: number) => {
return service.request({
url: `/api/cultureArticle/isRecommend?articleId=${articleId}`,
method: 'POST',
data: {},
})
}
/**
* 删除文章接口
*/
export const deleteArticle = (id: number) => {
return service.request({
url: `/api/personalCenter/deleteArticle`,
method: 'POST',
data: {
id,
},
})
}
/**
* 删除评论接口
*/
export const deleteComment = (commentId: number) => {
return service.request({
url: `/api/cultureComment/deleteComment?commentId=${commentId}`,
method: 'POST',
})
}
/**
* 给文章投币
*/
export const addOrCancelArticleReward = (data: { articleId: number; ayabi: number }) => {
return service.request({
url: `/api/culture/action/record/reward`,
method: 'POST',
data,
})
}
import { ArticleTypeEnum, ReleaseStatusTypeEnum, BooleanFlag, SendTypeEnum } from '@/constants'
import type { PageSearchParams } from '@/utils/request/types'
/**
* 搜索文章的参数
*/
export interface ArticleSearchParams extends PageSearchParams {
type?: ArticleTypeEnum
sortLogic?: number
title?: string
}
/**
* 添加或更新文章DTO(带枚举版本)
*/
export type AddOrUpdateArticleDto =
| AddOrUpdatePostDto
| AddOrUpdateColumnDto
| AddOrUpdateInterviewDto
| AddOrUpdateVideoDto
/**
* 添加帖子的DTO
*/
export interface AddOrUpdatePostDto {
id?: number
title?: string
content?: string
releaseStatus?: ReleaseStatusTypeEnum
sendType?: SendTypeEnum
faceUrl?: string
imgUrl?: string
sendTime?: string
type?: ArticleTypeEnum.POST
}
interface AddOrUpdateColumnBase {
id?: number
type: ArticleTypeEnum.COLUMN
title: string
content: string
releaseStatus: ReleaseStatusTypeEnum
sendType: SendTypeEnum
faceUrl?: string
imgUrl?: string
// 关联的专栏栏目
relateColumnId?: number
mainTagId: string
isRelateColleague: BooleanFlag
sendTime?: string
isRecommend: BooleanFlag
}
/**
* 添加专栏的原始form类型
*/
export interface AddOrUpdateColumnForm extends AddOrUpdateColumnBase {
tagList: number[]
}
/**
* 添加专栏的DTO
*/
export interface AddOrUpdateColumnDto extends AddOrUpdateColumnBase {
tagList: { tagId: number; sort: number }[]
}
export interface AddOrUpdateInterviewBase {
id?: number
type: ArticleTypeEnum.INTERVIEW
title: string
content: string
releaseStatus: ReleaseStatusTypeEnum
sendType: SendTypeEnum
faceUrl?: string
imgUrl?: string
// 关联的专访栏目
relateColumnId?: number
mainTagId: string
sendTime?: string
isRecommend: BooleanFlag
}
export interface AddOrUpdateInterviewForm extends AddOrUpdateInterviewBase {
tagList: number[]
}
/**
* 添加专访的DTO
*/
export interface AddOrUpdateInterviewDto extends AddOrUpdateInterviewBase {
tagList: { tagId: number; sort: number }[]
}
export interface AddOrUpdateVideoDto {
id?: number
title: string
type: ArticleTypeEnum.VIDEO
content: string
videoUrl: string
videoDuration: string
releaseStatus: ReleaseStatusTypeEnum
sendType: SendTypeEnum
faceUrl?: string
imgUrl?: string
sendTime?: string
isRecommend?: BooleanFlag
mainTagId: string | number
tagList: { tagId: number; sort: number }[] | number[]
}
/**
* 添加专访的DTO
*/
/**
* 文章详情
*/
export interface ArticleItemDto {
id: number
title: string
content: string
faceUrl: string
videoUrl: string
description: string
createUserId: number
createTime: number
viewCount: number
isRecommend: BooleanFlag
type: ArticleTypeEnum
isRelateColleague: BooleanFlag
releaseStatus: ReleaseStatusTypeEnum
tagNameList: string[]
praiseCount: number
collectionCount: number
replyCount: number
hasPraised: boolean
hasCollect: boolean
imgUrl: string
createUserAvatar: string
createUserName: string
showAvatar: string
showName: string
videoDuration: string
showComment?: boolean
}
/**
* 专栏选项
*/
export interface ColumnOptionDto {
color: string
createTime: number
createUserId: number
id: number
isDelete: BooleanFlag
sort: number
status: BooleanFlag
title: string
type: 'column'
}
/**
* 专栏列表Item
*/
export interface ColumnItemDto {
title: string
color: string
sort: number
yaColumnVoList: {
articleId: number
collectCount: number
content: string
createTime: number
description: string
faceUrl: string
hasPraised: boolean
isRecommend: number
praiseCount: number
replyCount: number
title: string
type: ArticleTypeEnum.COLUMN
viewCount: number
}[]
}
/**
* 专访选项
*/
export interface InterviewOptionDto {
color: string
createTime: number
createUserId: number
id: number
isDelete: BooleanFlag
sort: number
status: BooleanFlag
title: string
type: 'column'
}
/**
* 专访列表Item
*/
export interface InterviewItemDto {
title: string
color: string
sort: number
yaColumnVoList: {
articleId: number
collectCount: number
content: string
createTime: number
description: string
faceUrl: string
hasPraised: boolean
isRecommend: number
praiseCount: number
replyCount: number
title: string
type: ArticleTypeEnum.INTERVIEW
viewCount: number
}[]
}
/**
* 评论列表
*/
export interface CommentSearchParams extends PageSearchParams {
articleId: number | string
sortType: number
}
/**
* 新增评论
*/
export interface AddCommentDto {
articleId: number | string
content: string
pId?: number | string
}
/**
* 评论信息
*/
export interface CommentItemDto {
articleId: number
avatar: string
children: CommentItemDto[]
content: string
createTime: number
hasPraise: BooleanFlag
hiddenAvatar: string
hiddenName: string
id: number
isFeatured: number
isTop: number
pid: number
postPriseCount: number
region: string
regionHide: number
replyUser: string
replyName: string
userId: number
}
import service from '@/utils/request/index'
import type { AddOrUpdateTagDto, BackendTagListItemDto, BackendTagSearchParams } from './types'
import type { BackendServicePageResult } from '@/utils/request/types'
// 后台管理标签相关接口
/**
* 获取轮播图列表 不分页 数量不多
*/
export const getCarouselList = () => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/cultureCarousel/listNoPage',
method: 'POST',
data: {},
})
}
/**
* 添加轮播图
*/
export const addCarousel = (data: AddOrUpdateCarouselDto) => {
return service.request({
url: '/api/cultureCarousel/addCarousel',
method: 'POST',
data,
})
}
/**
* 更新轮播图
*/
export const editCarousel = (data: AddOrUpdateCarouselDto) => {
return service.request({
url: '/api/cultureCarousel/editCarousel',
method: 'POST',
data,
})
}
/**
* 更改发布状态
*/
export const deleteCarousel = (id: number) => {
return service.request<boolean>({
url: `/api/cultureCarousel/updateRelease?id=${id}`,
method: 'POST',
})
}
import service from '@/utils/request/index'
import type { AddOrUpdateTagDto, BackendTagListItemDto, BackendTagSearchParams } from './types'
import type { BackendServicePageResult } from '@/utils/request/types'
// 后台管理案例库相关接口
/**
* 案例库后台列表
*/
export const getCaseList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/cultureCase/caseListByPage',
method: 'POST',
data,
})
}
/**
* 审核接口
*/
export const auditCase = (data: AddOrUpdateCarouselDto) => {
return service.request({
url: '/api/cultureCase/auditCase',
method: 'POST',
data,
})
}
/**
* 删除案例库
*/
export const deleteCase = (id: number) => {
return service.request({
url: `/api/cultureCase/deleteCase?id=${id}`,
method: 'POST',
})
}
import service from '@/utils/request/index'
import type {
AddOrUpdateColumnDto,
BackendColumnSearchParams,
BackendColumnListItemDto,
} from './types'
import type { BackendServicePageResult } from '@/utils/request/types'
// 后台管理栏目相关内容
/**
* 新增或者修改栏目
*/
export const addOrUpdateColumn = (data: AddOrUpdateColumnDto) => {
return service.request({
url: '/api/cultureColumn/addOrUpdate',
method: 'POST',
data,
})
}
/**
* 栏目列表
*/
export const listOfCultureColumn = (data: BackendColumnSearchParams) => {
return service.request<BackendServicePageResult<BackendColumnListItemDto>>({
url: '/api/cultureColumn/listByPage',
method: 'POST',
data,
})
}
/**
* 删除栏目设置
*/
export const deleteColumn = (ids: number[]) => {
return service.request<boolean>({
url: `/api/cultureColumn/delete`,
method: 'POST',
data: ids,
})
}
/**
* 隐藏栏目设置
*/
export const hideColumn = (ids: number[]) => {
return service.request<boolean>({
url: `/api/cultureColumn/hidden`,
method: 'POST',
data: ids,
})
}
import type { PageSearchParams } from '@/utils/request/types'
export interface BackendColumnSearchParams extends PageSearchParams {
type: string
status: number
title: string
}
export interface AddOrUpdateColumnDto {
id?: number
color: string
title: string
type: string
sort: number
}
export interface BackendColumnListItemDto {
color: string
createTime: number
createUserId: number
id: number
isDelete: number
postCount: number
sort: number
status: number
title: string
type: string
}
// 方法
export * from './tags'
export * from './carousel'
export * from './columnSettings'
export * from './case'
export * from './shop'
// 类型
export * from './tags/types'
export * from './carousel/types'
export * from './columnSettings/types'
export * from './case/types'
export * from './shop/types'
import service from '@/utils/request/index'
import type { AddOrUpdateTagDto, BackendTagListItemDto, BackendTagSearchParams } from './types'
import type { BackendServicePageResult } from '@/utils/request/types'
// 后台管理 积分商城相关接口
/**
* 商品配置列表
*/
export const getShopItemList = (params: BackendTagSearchParams) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/culture/shop/item/productList',
method: 'POST',
data: params,
})
}
/**
* 新增/编辑商品
*/
export const addOrUpdateShopItem = (data: AddOrUpdateTagDto) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/culture/shop/item/addOrUpdate',
method: 'POST',
data,
})
}
/*
* 删除商品
*/
export const deleteShopItem = (id: number) => {
return service.request({
url: `/api/culture/shop/item/deleteProduct?id=${id}`,
method: 'POST',
})
}
/**
* 后台商品领用列表
*/
export const getBackendExchangeList = (data: BackendTagSearchParams) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/culture/shop/order/background/productList',
method: 'POST',
data,
})
}
/**
* 发放 取消发放
*/
export const issueProduct = (data) => {
return service.request({
url: `/api/culture/shop/order/issueProduct`,
method: 'POST',
data,
})
}
import type { PageSearchParams } from '@/utils/request/types'
export interface BackendTagSearchParams extends PageSearchParams {
title: string
type: string
}
export interface AddOrUpdateTagDto {
id?: number
color: string
description: string
title: string
type: string
}
export interface BackendTagListItemDto {
color: string
createId: number
createTime: number
description: string
id: number
imgUrl: string
style: number
title: string
type: string
}
import service from '@/utils/request/index'
import type { AddOrUpdateTagDto, BackendTagListItemDto, BackendTagSearchParams } from './types'
import type { BackendServicePageResult } from '@/utils/request/types'
// 后台管理标签相关接口
/**
* 获取标签列表 分页
*/
export const getTagList = (params: BackendTagSearchParams) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/cultureTag/listByPage',
method: 'POST',
data: params,
})
}
/**
* 新增或者更新标签
*/
export const addOrUpdateTag = (data: AddOrUpdateTagDto) => {
return service.request({
url: '/api/cultureTag/addOrUpdate',
method: 'POST',
data,
})
}
/**
* 删除标签
*/
export const deleteTag = (id: number) => {
return service.request<boolean>({
url: `/api/cultureTag/delete?id=${id}`,
method: 'POST',
})
}
import type { PageSearchParams } from '@/utils/request/types'
export interface BackendTagSearchParams extends PageSearchParams {
title: string
type: string
}
export interface AddOrUpdateTagDto {
id?: number
color: string
description: string
title: string
type: string
}
export interface BackendTagListItemDto {
color: string
createId: number
createTime: number
description: string
id: number
imgUrl: string
style: number
title: string
type: string
}
import service from '@/utils/request/index'
import type { AddOrUpdateCaseDto } from './types'
// 案例库相关的接口
/**
* 获取最新的案例编号
*/
export const getMaxCaseNumber = () => {
return service.request<string>({
url: '/api/cultureCase/getMaxCaseNumber',
method: 'POST',
data: {},
})
}
/**
* 新增案例库
*/
export const addOrUpdateCase = (data: AddOrUpdateCaseDto) => {
return service.request({
url: '/api/cultureCase/addOrUpdateCase',
method: 'POST',
data,
})
}
/**
* 添加或更新案例库DTO
*/
import { TagTypeEnum, TagLevelEnum, BooleanFlag, ReleaseStatusTypeEnum } from '@/constants'
export type TagItemDto = {
tagId: number
type: TagTypeEnum
keywordType: TagLevelEnum
}
export interface AddOrUpdateCaseDto {
id?: number
content: string
title: string
tagRelationDtoList: TagItemDto[]
isSync: BooleanFlag
releaseStatus: ReleaseStatusTypeEnum
}
// 专栏列表
import service from '@/utils/request/index'
import type { ColumnOptionDto } from './types'
/**
* 获取专栏列表
*/
export const getColumnOptions = () => {
return service.request<ColumnOptionDto[]>({
url: '/api/cultureColumn/listNoPage?type=column',
method: 'POST',
})
}
import { BooleanFlag } from '@/constants'
export interface ColumnOptionDto {
color: string
createTime: number
createUserId: number
id: number
isDelete: BooleanFlag
sort: number
status: BooleanFlag
title: string
type: 'column'
}
// 常规的接口
import service from '@/utils/request/index'
import type { FielItemDto } from './types'
/**
* 获取常规的接口
*/
// export const uploadFile = (file: File, onProgress?: (progress: number) => void) => {
// const formData = new FormData()
// formData.append('file', file)
// return service.request<FielItemDto>({
// url: '/mobiles/file-upload/singleUpload',
// method: 'POST',
// data: formData,
// onUploadProgress: (progressEvent) => {
// const percentCompleted = Math.round((progressEvent.loaded * 100) / (progressEvent.total || 1))
// onProgress?.(percentCompleted)
// },
// })
// }
/**
* 暂时调用oa正式接口
*/
import axios from 'axios'
export const uploadFile = (file: File, onProgress?: (progress: number) => void) => {
const formData = new FormData()
formData.append('fileList', file)
return axios.post('http://47.112.96.71:8082/mobiles/uploadFile', formData, {
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / (progressEvent.total || 1))
onProgress?.(percentCompleted)
},
})
}
export interface FielItemDto {
fileId: string
fileName: string
fileUrl: string
finalName: string
msg: string
status: number
}
import service from '@/utils/request/index'
import type { CarouselItemDto, UserAccountDataDto, UserRecordDataDto } from './types'
import { BooleanFlag } from '@/constants'
/**
* 获取首页轮播图列表
*/
export const getCarouselList = () => {
return service.request<CarouselItemDto[]>({
url: '/api/cultureCarousel/listNoPage',
method: 'POST',
data: {
isRelease: BooleanFlag.YES,
},
})
}
/**
* 获取首页用户相关数据 亚币 等级等
*/
export const getUserAccountData = () => {
return service.request<UserAccountDataDto>({
url: '/api/personalCenter/selfAccountData',
method: 'POST',
data: {},
})
}
/**
* 获取首页信息 是否已经签到
*/
export const getRecordData = () => {
return service.request<UserRecordDataDto>({
url: '/api/culture/action/record/baseData',
method: 'POST',
data: {},
})
}
import { BooleanFlag } from '@/constants'
/**
* 轮播图的item
*/
export interface CarouselItemDto {
articleId: number
assetUrl: string
isRelease: BooleanFlag
sort: number
type: BooleanFlag
url: string
}
/**
* 用户相关数据
*/
export interface UserAccountDataDto {
ayabiAvailable: number
ayabiTotal: number
createTime: number
expTotal: number
id: number
isDelete: number
level: number
remark: string
updateTime: number
userId: string
}
/**
* 用户信息记录
*/
export interface UserRecordDataDto {
actionType: null
actionTypeText: null
createdAt: null
createdTimeText: null
currentValue: null
id: null
incrText: null
isDelete: null
isIncr: null
isSign: BooleanFlag
relationId: null
remark: null
scoreAyabi: null
scoreExp: null
subType: null
userId: null
}
// 企业文化接口
export * from './task'
export * from './sign'
export * from './article'
export * from './shop'
// export * from './column'
// export * from './interview'
export * from './tag'
export * from './article'
export * from './user'
export * from './case'
export * from './home'
export * from './practice'
export * from './common'
export * from './login'
export * from './article'
// 导出类型
export * from './task/types'
export * from './shop/types'
export * from './article/types'
// export * from './column/types'
// export * from './interview/types'
export * from './tag/types'
export * from './article/types'
export * from './user/types'
export * from './case/types'
export * from './home/types'
export * from './practice/types'
export * from './common/types'
export * from './login/types'
export * from './article/types'
// 专栏列表
import service from '@/utils/request/index'
import type { ColumnOptionDto } from './types'
/**
* 获取专栏列表
*/
export const getInterviewOptions = () => {
return service.request<ColumnOptionDto[]>({
url: '/api/cultureColumn/listNoPage?type=interview',
method: 'POST',
})
}
import { BooleanFlag } from '@/constants'
export interface InterviewOptionDto {
color: string
createTime: number
createUserId: number
id: number
isDelete: BooleanFlag
sort: number
status: BooleanFlag
title: string
type: 'column'
}
import service from '@/utils/request/index'
import type { LoginParams, LoginResponseDto } from './types'
/**
* 登录 —— 根据 邮箱密码登录
*/
export const loginByEmail = (data: LoginParams) => {
return service.request<LoginResponseDto>({
url: '/api/auth/login',
method: 'POST',
data,
})
}
/**
* 企业微信应用登录
* 1. 直接拿 code登录
* 2. 根据 code + cutEmail + isCodeLogin // 切换账号登录
*/
export const loginByCode = ({
code,
isCodeLogin,
cutEmail,
}: {
code: string
isCodeLogin?: number
cutEmail?: string
}) => {
return service.request<LoginResponseDto>({
url: '/api/auth/applicationLogin',
method: 'POST',
data: {
code,
isCodeLogin,
cutEmail,
},
})
}
/**
* 生成随机密钥-切换官方账号用
*/
interface GenerateLoginKeyData {
cutEmail: string
timestamp: number
type: 1 | 2 // 1: 多平台跳转 2: 官方账号切换
userId: number
}
export const generateLoginKey = (data: GenerateLoginKeyData) => {
return service.request<string>({
url: '/api/auth/generateLoginKey',
method: 'POST',
data,
})
}
export interface LoginParams {
email: string
password: string
}
export interface LoginResponseDto {
id: number
account: string
name: string
email: string
avatar: string
deptId: number | null
roleList: any[] | null
deptName: string | null
roleNames: string | null
permissions: any[] | null
userRelated: any[] | null
isOfficialAccount: boolean | null
firstDeptId: number | null
password: string | null
enabled: boolean
username: string
authorities: any[] | null
accountNonExpired: boolean
credentialsNonExpired: boolean
accountNonLocked: boolean
token: string
}
import service from '@/utils/request/index'
import type { AddOrUpdatePracticeDto, PracticeSearchParams, PracticeItemDto } from './types'
import type { BackendServicePageResult } from '@/utils/request/types'
// 关于实践相关接口
/**
* 发布或者更新实践
*/
export const addOrUpdatePractice = (data: AddOrUpdatePracticeDto) => {
return service.request<boolean>({
url: '/api/cultureArticle/addOrUpdatePractice',
method: 'POST',
data,
})
}
/**
* 实践列表接口
*/
export const getPracticeList = (data: PracticeSearchParams) => {
return service.request<BackendServicePageResult<PracticeItemDto>>({
url: '/api/yaCulture/practiceList',
method: 'POST',
data,
})
}
import { ArticleTypeEnum, ReleaseStatusTypeEnum, BooleanFlag, SendTypeEnum } from '@/constants'
import type { PageSearchParams } from '@/utils/request/types'
/**
* 添加或更新实践DTO
*/
export interface AddOrUpdatePracticeDto {
id?: number
title: string
content: string
faceUrl: string
imgUrl: string
releaseStatus: ReleaseStatusTypeEnum
mainTagId: number | string
tagList: { tagId: number; sort: number }[]
sendType: SendTypeEnum
sendTime: string
type?: ArticleTypeEnum
}
/**
* 搜索文章的参数
*/
export interface PracticeSearchParams extends PageSearchParams {
sortLogic?: number
tagIdList?: number[]
}
/**
* 添加或更新文章DTO(带枚举版本)
*/
export interface AddOrUpdateArticleDto {
/** 内容 */
content?: string
/** 创建人id */
createUserId?: number
/** 描述 */
description?: string
/** 封面图 */
faceUrl?: string
/** id,编辑时必传 */
id?: number
/** 是否关联同事 */
isRelateColleague?: number
/** 主标签id */
mainTagId?: number
/** 发布状态 */
releaseStatus?: ReleaseStatusTypeEnum
/** 标签列表 */
tagList?: { tagId: number; sort: number }[]
/** 标题 */
title?: string
/** 文章类型 */
type?: ArticleTypeEnum
/** 视频url */
videoUrl?: string
}
/**
* 文章详情
*/
// 更严格的类型定义
export interface ArticleItemDto {
id: number
title: string
content: string
faceUrl: string
videoUrl: string
description: string
createUserId: number
createTime: number
viewCount: number
isRecommend: BooleanFlag
type: ArticleTypeEnum
isRelateColleague: BooleanFlag
releaseStatus: ReleaseStatusTypeEnum
tagNameList: string[]
praiseCount: number
collectionCount: number
replyCount: number
hasPraised: BooleanFlag
}
/**
* 实践列表item
*/
export interface PracticeItemDto {
collectionCount: number
content: string
createTime: number
createUserId: number
description: string
faceUrl: string
hasPraised: boolean
id: number
isRecommend: BooleanFlag
praiseCount: number
releaseStatus: ReleaseStatusTypeEnum
replyCount: number
showAvatar: string
showName: string
tagNameList: string[]
title: string
type: ArticleTypeEnum.PRACTICE
viewCount: number
}
import service from '@/utils/request/index'
import type { BackendServicePageResult, PageSearchParams } from '@/utils/request/types'
import type {
ExchangeGoodsParams,
ExchangeGoodsRecordItemDto,
ShopItemDto,
ShopSearchParams,
YaBiData,
ExchangeYabiRecordItemDto,
ExchangeGoodsRecordSearchParams,
BackendShopItemDto,
} from './types'
/**
* 获取用户亚币相关数据
*/
export const getYaBiData = () => {
return service.request<YaBiData>({
url: '/api/culture/action/record/yabiData',
method: 'POST',
data: {},
})
}
/**
* 积分商城商品列表
*/
export const getShopItemList = (data: ShopSearchParams) => {
return service.request<BackendServicePageResult<BackendShopItemDto>>({
url: '/api/culture/shop/item/pageList',
method: 'POST',
data,
})
}
/**
* 兑换商品
*/
export const exchangeGoods = (data: ExchangeGoodsParams) => {
return service.request({
url: '/api/culture/shop/item/exchange',
method: 'POST',
data,
})
}
/**
* 商品领取列表
*/
export const getExchangeGoodsRecordList = (data: ExchangeGoodsRecordSearchParams) => {
return service.request<BackendServicePageResult<ExchangeGoodsRecordItemDto>>({
url: '/api/culture/shop/order/exchangeList',
method: 'POST',
data,
})
}
/**
* 获取用户YA币兑换记录
*/
export const getExchangeYabiRecordList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<ExchangeYabiRecordItemDto>>({
url: '/api/culture/action/record/yabiList',
method: 'POST',
data,
})
}
import type { PageSearchParams } from '@/utils/request/types'
import { BooleanFlag, ShopGoodsTypeEnum } from '@/constants'
/**
* 商品列表搜索参数
*/
export interface ShopSearchParams extends PageSearchParams {
region: string
itemType: ShopGoodsTypeEnum
}
/**
* 积分商城商品类型
*/
export interface BackendShopItemDto {
createTime: number
description: string
enable: BooleanFlag
id: number
imageUrl: string
itemType: ShopGoodsTypeEnum
itemTypeName: string
name: string
price: number
region: string
soldOut: boolean
sortOrder: number
stock: number
}
/**
* 商品领取列表搜索参数
*/
export interface ExchangeGoodsRecordSearchParams extends PageSearchParams {
receiveTimeStart: number
receiveTimeEnd: number
itemType: ShopGoodsTypeEnum
}
/**
* yabi信息对象类型
*/
export interface YaBiData {
currentValue: number
}
/**
* 兑换商品请求参数类型
*/
export interface ExchangeGoodsParams {
itemId: number
num: number
deliveryInfo?: string
}
/**
* 商品领取列表item Dto
*/
export interface ExchangeGoodsRecordItemDto {
createTime: number
deliveryInfo: string
id: number
isDelete: BooleanFlag
itemId: number
itemName: string
itemType: ShopGoodsTypeEnum
num: number
price: number
status: number
userId: number
}
/**
* 获取用户YA币收支记录
*/
export interface ExchangeYabiRecordItemDto {
actionType: string
actionTypeText: string
createdAt: number
createdTimeText: string
currentValue: number
id: number
incrText: string
isDelete: BooleanFlag
isIncr: BooleanFlag
isSign: BooleanFlag
relationId: number
remark: string
scoreAyabi: number
scoreExp: number
subType: string
userId: number
}
import service from '@/utils/request/index'
/**
* 每日签到
*/
export const dailySign = () => {
return service.request({
url: '/api/culture/action/record/finishTask',
method: 'POST',
data: {
taskKey: 'SIGN_IN_NORMAL',
},
})
}
import service from '@/utils/request/index'
import type { TagItemDto } from './types'
/**
* 获取标签 不分页
*/
export const getTagList = () => {
return service.request<TagItemDto[]>({
url: '/api/cultureTag/listNoPage',
method: 'POST',
data: {
type: 'culture',
},
})
}
export interface TagItemDto {
color: string
createId: string
createTime: number
description: string
id: number
imgUrl: string
style: number
title: string
type: string
}
import service from '@/utils/request/index'
import type { TaskItemDto } from './types'
/**
* 获取任务列表
*/
export const getTaskList = () => {
return service.request<TaskItemDto[]>({
url: '/api/culture/task/config/getList',
method: 'POST',
data: {},
})
}
import { TaskTypeEnum, TaskDateLimitTypeEnum } from '@/constants'
export interface TaskItemDto {
completedTasks: number
currentCount: number
description: string
id: number
jumpUrl: string
limitCount: number
limitType: TaskDateLimitTypeEnum
periodKey: string
rewardType: number
rewardValue: number
status: number
svgName: string
taskKey: string
taskType: TaskTypeEnum
title: string
totalTasks: number
}
import service from '@/utils/request/index'
import type {
UpdateUserInfoDto,
SelfPublishDetailDto,
AuditListSearchParams,
AuditListItemDto,
AuditArticleDto,
SelfPublishSearchParams,
SelfCollectSearchParams,
SelfCollectDetailDto,
SelfPraiseSearchParams,
SelfPraiseDetailDto,
} from './types'
import type { BackendServicePageResult, PageSearchParams } from '@/utils/request/types'
/**
* 更新用户信息
*/
export const updateUserInfo = (data: UpdateUserInfoDto) => {
return service.request({
url: '/api/personalCenter/updateUser',
method: 'POST',
data,
})
}
/**
* 是否有官方账号权限
*/
export const hasOfficialAccount = () => {
return service.request<[]>({
url: '/api/personalCenter/getIsOfficial',
method: 'POST',
data: {},
})
}
/**
* 获取我的发布列表
*/
export const getSelfPublishList = (data: SelfPublishSearchParams) => {
return service.request<BackendServicePageResult<SelfPublishDetailDto>>({
url: '/api/personalCenter/selfPublish',
method: 'POST',
data,
})
}
/**
* 获取我的草稿列表
*/
export const getSelfDraftList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<SelfPublishDetailDto>>({
url: '/api/personalCenter/selfDraft',
method: 'POST',
data,
})
}
/**
* 获取我的收藏列表
*/
export const getSelfCollectList = (data: SelfCollectSearchParams) => {
return service.request<BackendServicePageResult<SelfCollectDetailDto>>({
url: '/api/personalCenter/selfCollect',
method: 'POST',
data,
})
}
/**
* 获取我的点赞列表
*/
export const getSelfPraiseList = (data: SelfPraiseSearchParams) => {
return service.request<BackendServicePageResult<SelfPraiseDetailDto>>({
url: '/api/personalCenter/selfPraise',
method: 'POST',
data,
})
}
/**
* 获取我的案例库列表
*/
export const getSelfCaseList = (data: PageSearchParams) => {
return service.request<
BackendServicePageResult<{
id: number
title: string
content: string
createTime: string
updateTime: string
}>
>({
url: '/api/personalCenter/selfCase',
method: 'POST',
data,
})
}
/**
* 获取我的任务列表
*/
export const getSelfTaskList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<SelfPublishDetailDto>>({
url: '/api/personalCenter/selfTaskConfig',
method: 'POST',
data,
})
}
/**
* 评论回复
*/
export const getSelfCommentList = (data: PageSearchParams) => {
return service.request({
url: '/api/personalCenter/selfComment',
method: 'POST',
data,
})
}
/**
* 待审核列表
*/
export const getAuditList = (data: AuditListSearchParams) => {
return service.request<BackendServicePageResult<AuditListItemDto>>({
url: '/api/personalCenter/getAuditList',
method: 'POST',
data,
})
}
/**
* 审核推文
*/
export const auditArticle = (data: AuditArticleDto) => {
return service.request({
url: '/api/cultureArticle/auditArticle',
method: 'POST',
data,
})
}
import { ArticleTypeEnum, AuditStatusEnum } from '@/constants'
import type { PageSearchParams } from '@/utils/request/types'
export interface SelfPublishSearchParams extends PageSearchParams {
type: ArticleTypeEnum
}
// 我的发布
export interface SelfPublishDetailDto {
collectionCount: number
content: string
createTime: number
createUserId: number
description: string
faceUrl: string
hasPraised: boolean
id: number
isRecommend: boolean
isRelateColleague: boolean
praiseCount: number
releaseStatus: number
replyCount: number
tagNameList: string[]
title: string
type: string
videoUrl: string
viewCount: number
}
// 我的收藏
export interface SelfCollectSearchParams extends PageSearchParams {
type: ArticleTypeEnum
}
export interface SelfCollectDetailDto {
collectionCount: number
content: string
createTime: number
createUserId: number
description: string
faceUrl: string
hasPraised: boolean
id: number
isRecommend: boolean
praiseCount: number
replyCount: number
tagNameList: string[]
title: string
type: string
viewCount: number
}
// 我的点赞
export interface SelfPraiseSearchParams extends PageSearchParams {
type: ArticleTypeEnum
}
export interface SelfPraiseDetailDto {
collectionCount: number
content: string
createTime: number
createUserId: number
description: string
faceUrl: string
hasPraised: boolean
id: number
isRecommend: boolean
isRelateColleague: boolean
praiseCount: number
releaseStatus: number
replyCount: number
tagNameList: string[]
title: string
type: string
videoUrl: string
viewCount: number
}
// 我的草稿 详情
export interface SelfDraftDetailDto {
id: number
title: string
content: string
createTime: string
updateTime: string
}
export interface UpdateUserInfoDto {
hiddenAvatar: string
hiddenName: string
signature: string
}
// 审核列表查询参数
export interface AuditListSearchParams extends PageSearchParams {
isAudit: AuditStatusEnum
}
// 待审核列表item
export interface AuditListItemDto {
collectionCount: number
content: string
createTime: number
createUserId: number
description: string
faceUrl: string
hasPraised: boolean
id: number
imgUrl: string
isRecommend: boolean
isRelateColleague: boolean
playCount: number
praiseCount: number
relateColumnId: string
releaseStatus: AuditStatusEnum
replyCount: number
showAvatar: string
showName: string
tagNameList: string[]
title: string
type: string
viewCount: number
}
/**
* 审核推文参数
*/
export interface AuditArticleDto {
articleId: number
auditResult: Exclude<AuditStatusEnum, AuditStatusEnum.UNAUDITED>
auditRemark?: string
}
This source diff could not be displayed because it is too large. You can view the blob instead.
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5888_1385)">
<path d="M18.8509 2H30.1198C31.8313 2 33.2188 3.372 33.2188 5.06445V14.2578C33.2188 15.9502 31.8313 17.3222 30.1198 17.3222H29.4155L28.8211 19.0856C28.6985 19.4494 28.2495 19.5848 27.9424 19.3505L25.2836 17.3222H18.8509C17.1394 17.3222 15.752 15.9502 15.752 14.2578V5.06445C15.752 3.372 17.1394 2 18.8509 2Z" fill="url(#paint0_linear_5888_1385)"/>
<foreignObject x="-21.5" y="-18.0352" width="76" height="76"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(12px);clip-path:url(#bgblur_1_5888_1385_clip_path);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="24" d="M25.21 6.21484C27.9909 6.215 30.25 8.44537 30.25 11.2021V25.4131C30.2498 28.1697 27.9908 30.3993 25.21 30.3994C24.6667 30.3994 24.1833 30.7469 24.0098 31.2617L23.4395 32.9551C23.1945 33.6819 22.3012 33.9478 21.6924 33.4834L18.1191 30.7588C17.8143 30.5263 17.4419 30.3995 17.0586 30.3994H7.79004C5.00922 30.3993 2.75015 28.1697 2.75 25.4131V11.2021C2.75 8.44537 5.00912 6.215 7.79004 6.21484H25.21Z" fill="#FFCBB9" fill-opacity="0.35" stroke="url(#paint1_linear_5888_1385)" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<g filter="url(#filter1_d_5888_1385)">
<path d="M26.1504 14.0986L29.4072 22.1543H28.0615L27.3721 20.3535L27.3232 20.2256H23.7539L23.7061 20.3555L23.0381 22.1543H21.7959L24.9541 14.0986H26.1504ZM25.3232 15.9551L24.2256 18.9238L24.126 19.1934H26.9326L26.8291 18.9219L25.6973 15.9531L25.5078 15.4551L25.3232 15.9551Z" fill="url(#paint2_linear_5888_1385)" stroke="url(#paint3_linear_5888_1385)" stroke-width="0.4"/>
<path d="M16.834 13.6143C17.2948 13.6143 17.6871 13.6981 18.0166 13.8594C18.3475 14.0213 18.5947 14.2431 18.7656 14.5234L18.7666 14.5254C18.9376 14.7991 19.0254 15.1123 19.0254 15.4707C19.0253 15.948 18.8755 16.3267 18.585 16.624C18.2824 16.9338 17.8417 17.2378 17.2529 17.5322L17.0127 17.6523L17.1963 17.8486L18.6406 19.3848L18.8428 19.6006L18.9678 19.332C19.2174 18.7959 19.3902 18.1986 19.4902 17.542H20.6846C20.4999 18.5688 20.162 19.4664 19.6689 20.2373L19.584 20.3701L19.6924 20.4834L21.2881 22.1553H19.8418L19.0234 21.2939L18.8896 21.1523L18.7451 21.2832C17.9971 21.9579 17.1128 22.2939 16.083 22.2939C15.2477 22.2939 14.5916 22.0881 14.0967 21.6934C13.6175 21.2975 13.3721 20.7483 13.3721 20.0215C13.3721 19.4191 13.5311 18.9486 13.833 18.5918C14.1506 18.2165 14.5929 17.8812 15.168 17.5898L15.3965 17.4736L15.2295 17.2803C14.9356 16.9412 14.7362 16.6488 14.624 16.4033C14.5206 16.1525 14.4697 15.8916 14.4697 15.6201C14.4698 15.193 14.576 14.8377 14.7803 14.5449C14.9894 14.2453 15.2683 14.0185 15.6211 13.8633L15.6221 13.8623C15.9867 13.6978 16.39 13.6143 16.834 13.6143ZM15.8799 18.1963C15.5041 18.4122 15.2019 18.6495 14.9824 18.9111C14.7508 19.1872 14.6358 19.5145 14.6357 19.8828C14.6357 20.3101 14.7821 20.6642 15.0898 20.916L15.0918 20.918C15.3976 21.1608 15.7886 21.2734 16.2451 21.2734C16.8933 21.2733 17.4892 21.0273 18.0283 20.5537L18.1846 20.417L18.042 20.2656L16.125 18.2324L16.0166 18.1182L15.8799 18.1963ZM16.7764 14.5889C16.4751 14.5889 16.2098 14.6735 15.998 14.8564L15.9951 14.8604C15.7821 15.0532 15.6866 15.3203 15.6865 15.6318C15.6865 15.8291 15.7382 16.0282 15.833 16.2266L15.835 16.2295C15.9303 16.4201 16.0921 16.6466 16.3115 16.9053L16.4111 17.0225L16.5508 16.9561C16.9304 16.7741 17.2325 16.5822 17.4463 16.377L17.4492 16.374C17.6775 16.1457 17.7959 15.8679 17.7959 15.5508C17.7958 15.2759 17.6986 15.039 17.499 14.8594H17.498C17.3082 14.6741 17.0608 14.5889 16.7764 14.5889Z" fill="url(#paint4_linear_5888_1385)" stroke="url(#paint5_linear_5888_1385)" stroke-width="0.4"/>
<path d="M8.25977 13.9482C8.9817 13.9482 9.6285 14.117 10.2041 14.4521L10.207 14.4541C10.7876 14.7807 11.2466 15.256 11.584 15.8867L11.585 15.8887C11.9283 16.5169 12.1035 17.2573 12.1035 18.1152C12.1035 18.951 11.939 19.6772 11.6172 20.2988C11.2936 20.916 10.8595 21.3928 10.3145 21.7344L10.0869 21.877L10.2891 22.0547L12.6045 24.084H10.8877L8.81738 22.3174L8.75391 22.2637L8.6709 22.2705C8.48785 22.2858 8.3515 22.293 8.25977 22.293C7.53715 22.2929 6.88579 22.1273 6.30176 21.7988C5.72883 21.4645 5.27399 20.9844 4.93652 20.3535C4.60062 19.7254 4.42877 18.9816 4.42871 18.1152C4.42871 17.2566 4.60034 16.5152 4.93652 15.8867C5.27431 15.2554 5.72965 14.7796 6.30273 14.4531H6.30371C6.88748 14.1175 7.53795 13.9483 8.25977 13.9482ZM8.25977 15.085C7.83583 15.085 7.44863 15.1845 7.10254 15.3857L6.95703 15.4775C6.58338 15.7325 6.29604 16.095 6.09082 16.5547L6.08984 16.5557C5.89189 17.0093 5.7959 17.5313 5.7959 18.1152C5.79595 18.6991 5.8925 19.2231 6.08984 19.6836L6.09082 19.6865C6.29594 20.146 6.58291 20.5112 6.95508 20.7734L6.95898 20.7764C7.34069 21.0308 7.77678 21.1562 8.25977 21.1562C8.74275 21.1562 9.17582 21.0309 9.55078 20.7754L9.55176 20.7744C9.93291 20.5118 10.222 20.1455 10.4199 19.6836L10.4189 19.6826C10.6238 19.2222 10.7246 18.6986 10.7246 18.1152C10.7246 17.5308 10.6241 17.0088 10.418 16.5547C10.2199 16.0939 9.93166 15.7313 9.5498 15.4766H9.54883C9.17462 15.2143 8.74239 15.085 8.25977 15.085Z" fill="url(#paint6_linear_5888_1385)" stroke="url(#paint7_linear_5888_1385)" stroke-width="0.4"/>
</g>
</g>
<defs>
<clipPath id="bgblur_1_5888_1385_clip_path" transform="translate(21.5 18.0352)"><path d="M25.21 6.21484C27.9909 6.215 30.25 8.44537 30.25 11.2021V25.4131C30.2498 28.1697 27.9908 30.3993 25.21 30.3994C24.6667 30.3994 24.1833 30.7469 24.0098 31.2617L23.4395 32.9551C23.1945 33.6819 22.3012 33.9478 21.6924 33.4834L18.1191 30.7588C17.8143 30.5263 17.4419 30.3995 17.0586 30.3994H7.79004C5.00922 30.3993 2.75015 28.1697 2.75 25.4131V11.2021C2.75 8.44537 5.00912 6.215 7.79004 6.21484H25.21Z"/>
</clipPath><filter id="filter1_d_5888_1385" x="-10.7715" y="-1.58594" width="55.4746" height="40.8691" 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 dx="3"/>
<feGaussianBlur stdDeviation="5"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.521569 0 0 0 0 0.133333 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5888_1385"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5888_1385" result="shape"/>
</filter>
<linearGradient id="paint0_linear_5888_1385" x1="35.2774" y1="12.3708" x2="24.4854" y2="19.4668" gradientUnits="userSpaceOnUse">
<stop stop-color="#FD7A0D"/>
<stop offset="1" stop-color="#FF9F58"/>
</linearGradient>
<linearGradient id="paint1_linear_5888_1385" x1="7.29796" y1="9.61038" x2="27.4516" y2="33.9467" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.25"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear_5888_1385" x1="28.666" y1="22.1874" x2="26.9631" y2="13.6602" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.264434" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint3_linear_5888_1385" x1="36.1813" y1="28.6783" x2="33.8122" y2="13.0178" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.399023" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint4_linear_5888_1385" x1="20.6689" y1="22.3145" x2="18.7965" y2="13.1768" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.264434" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint5_linear_5888_1385" x1="28.5335" y1="29.2842" x2="25.9263" y2="12.4883" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.399023" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint6_linear_5888_1385" x1="12.0081" y1="24.0753" x2="9.60302" y2="13.5786" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.264434" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint7_linear_5888_1385" x1="20.169" y1="32.1622" x2="16.8063" y2="12.788" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.399023" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<clipPath id="clip0_5888_1385">
<rect width="36" height="36" fill="white"/>
</clipPath>
</defs>
</svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1662518356637" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5502" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M284.458667 941.397333c-36.437333 15.637333-68.48-7.68-64.896-47.168l22.613333-248.917333-164.394667-188.053333c-26.069333-29.824-13.653333-67.562667 24.789334-76.309334l243.370666-55.381333 127.786667-214.677333c20.288-34.090667 59.946667-34.069333 80.213333 0l127.786667 214.677333 243.370667 55.381333c38.656 8.789333 50.858667 46.485333 24.789333 76.309334l-164.394667 188.053333 22.741334 249.002667c3.605333 39.509333-28.458667 62.805333-64.896 47.146666l-229.504-98.517333-229.376 98.453333z" p-id="5503"></path></svg>
<svg width="1440" height="52" viewBox="0 0 1440 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4018_4776)">
<rect width="1440" height="52" fill="white"/>
<g opacity="0.4">
<g opacity="0.9" filter="url(#filter0_f_4018_4776)">
<path d="M772.018 297.727C638.936 279.177 564.987 211.476 490.277 150.855C420.344 94.1114 352.005 34.5528 364.844 -32.66C378.486 -104.063 452.013 -168.512 559.749 -212.809C672.877 -259.322 814.042 -285.149 956.779 -276.038C1105.97 -266.516 1256.16 -230.95 1333.5 -163.423C1407.16 -99.1186 1360.33 -20.677 1333.66 53.0078C1307.82 124.405 1291.35 201.192 1184.72 247.653C1071.85 296.834 913.88 317.501 772.018 297.727Z" fill="url(#paint0_linear_4018_4776)" fill-opacity="0.4"/>
</g>
<g opacity="0.7" filter="url(#filter1_f_4018_4776)">
<path opacity="0.8" d="M857.48 195.132C762.64 167.448 681.215 111.153 668.027 52.5871C655.959 -0.994975 738.162 -34.9614 797.598 -70.8048C838.132 -95.2444 887.653 -112.753 948.772 -116.704C1003.95 -120.271 1055.98 -102.542 1111.6 -91.1024C1198.17 -73.2947 1307.77 -78.3568 1358.21 -32.4827C1412.26 16.6699 1393.96 79.8839 1347.86 123.779C1303.9 165.632 1212.07 174.564 1127.87 186.817C1039.07 199.734 947.063 221.283 857.48 195.132Z" fill="#0500FF"/>
</g>
<g filter="url(#filter2_f_4018_4776)">
<path d="M350.157 200.737C213.928 187.476 77.4713 132.294 25.7152 56.7774C-21.6292 -12.3138 61.6784 -78.8585 115.165 -142.166C151.635 -185.339 203.601 -221.451 278.326 -242.246C345.782 -261.018 422.039 -250.322 499.052 -248.999C618.921 -246.942 754.141 -281.511 845.269 -232.516C942.908 -180.017 957.736 -90.2717 925.9 -19.4962C895.544 47.9861 785.074 83.2629 686.216 121.078C581.963 160.955 478.828 213.263 350.157 200.737Z" fill="url(#paint1_linear_4018_4776)"/>
</g>
</g>
</g>
<defs>
<filter id="filter0_f_4018_4776" x="-61.5882" y="-702.721" width="1859.51" height="1432.75" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="212.434" result="effect1_foregroundBlur_4018_4776"/>
</filter>
<filter id="filter1_f_4018_4776" x="356.946" y="-427.06" width="1344.29" height="944.428" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="154.945" result="effect1_foregroundBlur_4018_4776"/>
</filter>
<filter id="filter2_f_4018_4776" x="-283.184" y="-553.803" width="1519.04" height="1051.3" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="147.435" result="effect1_foregroundBlur_4018_4776"/>
</filter>
<linearGradient id="paint0_linear_4018_4776" x1="787.426" y1="-33.9855" x2="1583.56" y2="-454.532" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF0099"/>
<stop offset="1" stop-color="#FFD600"/>
</linearGradient>
<linearGradient id="paint1_linear_4018_4776" x1="301.626" y1="167.422" x2="911.841" y2="-246.846" gradientUnits="userSpaceOnUse">
<stop stop-color="#16CDEA"/>
<stop offset="1" stop-color="#D680FF"/>
</linearGradient>
<clipPath id="clip0_4018_4776">
<rect width="1440" height="52" fill="white"/>
</clipPath>
</defs>
</svg>
<svg width="63" height="66" viewBox="0 0 63 66" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_f_4018_4946)">
<path d="M45.922 47.8398C44.836 48.918 43.3624 49.5217 41.8263 49.5177H21.5527C18.3512 49.5177 15.7523 46.9381 15.7479 43.7561L15.7274 29.1431C15.7551 27.89 16.3492 26.7167 17.3449 25.9488L28.2654 17.187C30.1512 15.5607 32.9396 15.5104 34.8879 17.0676L46.0884 25.9488C47.0413 26.74 47.5929 27.9098 47.5946 29.1431L47.6151 43.771C47.6172 45.2977 47.008 46.7617 45.922 47.8398Z" fill="url(#paint0_linear_4018_4946)"/>
</g>
<path d="M45.922 47.8398C44.836 48.918 43.3624 49.5217 41.8263 49.5177H21.5527C18.3512 49.5177 15.7523 46.9381 15.7479 43.7561L15.7274 29.1431C15.7551 27.89 16.3492 26.7167 17.3449 25.9488L28.2654 17.187C30.1512 15.5607 32.9396 15.5104 34.8879 17.0676L46.0884 25.9488C47.0413 26.74 47.5929 27.9098 47.5946 29.1431L47.6151 43.771C47.6172 45.2977 47.008 46.7617 45.922 47.8398Z" fill="url(#paint1_linear_4018_4946)"/>
<g opacity="0.5" filter="url(#filter1_f_4018_4946)">
<path d="M37.671 43.8949C36.858 44.36 35.8919 44.4839 34.9859 44.2393L23.0193 41.0414C21.1296 40.5364 20.007 38.6039 20.5118 36.725L22.83 28.0964C23.0462 27.3611 23.584 26.7623 24.2941 26.466L32.1372 23.0169C33.5097 22.3544 35.1635 22.7645 36.0653 23.991L41.2601 30.9999C41.6963 31.6172 41.8354 32.3947 41.6398 33.1229L39.3191 41.7603C39.0769 42.6618 38.4839 43.4298 37.671 43.8949Z" fill="#0062FF"/>
</g>
<foreignObject x="-20.8184" y="-7.61719" width="85.3125" height="87.0713"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(12px);clip-path:url(#bgblur_0_4018_4946_clip_path);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="24" d="M17.7686 18.3877C20.0426 16.4369 23.3992 16.376 25.7432 18.2432L38.1406 28.0469L38.1494 28.0537C39.3179 29.0214 39.9941 30.4537 39.9941 31.9648V48.1113C39.9941 49.9309 39.2626 51.6746 37.9629 52.958C36.6638 54.2408 34.9039 54.9574 33.0713 54.9531V54.9541H10.6055C6.78645 54.9541 3.68168 51.8879 3.68164 48.0947V31.9531L3.69629 31.666C3.81092 30.2427 4.52424 28.9282 5.66699 28.0479L17.7686 18.3877Z" fill="url(#paint2_linear_4018_4946)" stroke="url(#paint3_linear_4018_4946)" stroke-linecap="round" stroke-linejoin="round"/>
<foreignObject x="-1.25977" y="30.0732" width="47.4746" height="35.6318"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(7.5px);clip-path:url(#bgblur_1_4018_4946_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter3_d_4018_4946)" data-figma-bg-blur-radius="15">
<rect x="13.7402" y="45.0732" width="15.4754" height="3.6315" rx="1.81575" fill="url(#paint4_linear_4018_4946)"/>
<rect x="13.8402" y="45.1732" width="15.2754" height="3.4315" rx="1.71575" stroke="url(#paint5_linear_4018_4946)" stroke-opacity="0.5" stroke-width="0.2"/>
</g>
<defs>
<filter id="filter0_f_4018_4946" x="0.726562" y="0.932617" width="61.8887" height="63.585" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="7.5" result="effect1_foregroundBlur_4018_4946"/>
</filter>
<filter id="filter1_f_4018_4946" x="2.39062" y="4.70215" width="57.3359" height="57.6602" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="9" result="effect1_foregroundBlur_4018_4946"/>
</filter>
<clipPath id="bgblur_0_4018_4946_clip_path" transform="translate(20.8184 7.61719)"><path d="M17.7686 18.3877C20.0426 16.4369 23.3992 16.376 25.7432 18.2432L38.1406 28.0469L38.1494 28.0537C39.3179 29.0214 39.9941 30.4537 39.9941 31.9648V48.1113C39.9941 49.9309 39.2626 51.6746 37.9629 52.958C36.6638 54.2408 34.9039 54.9574 33.0713 54.9531V54.9541H10.6055C6.78645 54.9541 3.68168 51.8879 3.68164 48.0947V31.9531L3.69629 31.666C3.81092 30.2427 4.52424 28.9282 5.66699 28.0479L17.7686 18.3877Z"/>
</clipPath><filter id="filter3_d_4018_4946" x="-1.25977" y="30.0732" width="47.4746" height="35.6318" 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 dx="5" dy="5"/>
<feGaussianBlur stdDeviation="6"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.15375 0 0 0 0 0.522433 0 0 0 0 0.9 0 0 0 0.28 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4018_4946"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4018_4946" result="shape"/>
</filter>
<clipPath id="bgblur_1_4018_4946_clip_path" transform="translate(1.25977 -30.0732)"><rect x="13.7402" y="45.0732" width="15.4754" height="3.6315" rx="1.81575"/>
</clipPath><linearGradient id="paint0_linear_4018_4946" x1="47.7062" y1="15.9326" x2="11.3318" y2="44.0154" gradientUnits="userSpaceOnUse">
<stop stop-color="#39AFFD"/>
<stop offset="1" stop-color="#477FFF"/>
</linearGradient>
<linearGradient id="paint1_linear_4018_4946" x1="47.7062" y1="15.9326" x2="11.3318" y2="44.0154" gradientUnits="userSpaceOnUse">
<stop stop-color="#39AFFD"/>
<stop offset="1" stop-color="#477FFF"/>
</linearGradient>
<linearGradient id="paint2_linear_4018_4946" x1="21.8381" y1="17.3828" x2="21.8381" y2="54.4544" gradientUnits="userSpaceOnUse">
<stop stop-color="#91C9FF"/>
<stop offset="1" stop-color="#3B9FFE"/>
</linearGradient>
<linearGradient id="paint3_linear_4018_4946" x1="9.80287" y1="21.7017" x2="40.8699" y2="50.5891" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.25"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear_4018_4946" x1="28.1185" y1="45.729" x2="11.1888" y2="48.0813" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.2"/>
</linearGradient>
<linearGradient id="paint5_linear_4018_4946" x1="15.4073" y1="46.2914" x2="28.5412" y2="46.6004" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1665994642562" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8290" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M778.94 659.1c-1.3-13.86-4.78-33.96-13.12-61.31-16.49-54.08-63.83-89.67-74.29-97a3.825 3.825 0 0 1-1.56-3.87c3.04-15.68 17.79-104.94-12.94-225.32C646.65 152.62 541 97.64 519.31 87.48c-2.48-1.16-7-2.55-9.45-1.44-10.39 4.73-128.49 58.66-160.88 185.58-30.73 120.37-15.98 209.63-12.94 225.31 0.29 1.48-0.32 3-1.56 3.87-10.47 7.34-57.81 42.92-74.29 97-8.15 26.74-11.66 46.56-13.03 60.38-1.36 13.68 9.38 25.55 23.14 25.55h123.48c0.99 0 1.94 0.38 2.66 1.07l0.19 0.18c0.74 0.71 1.16 1.7 1.17 2.73 0.86 62.99 52.2 113.79 115.39 113.79 63.26 0 114.63-50.9 115.39-113.97 0.01-0.92 0.34-1.81 0.96-2.5 0.73-0.83 1.78-1.3 2.88-1.3h124.03c13.27-0.01 23.73-11.41 22.49-24.63z m-265.9-224.97c-36.52 0-66.12-29.6-66.12-66.12s29.6-66.12 66.12-66.12 66.12 29.6 66.12 66.12-29.6 66.12-66.12 66.12z" p-id="8291" ></path><path d="M513.04 368.01m-44.14 0a44.14 44.14 0 1 0 88.28 0 44.14 44.14 0 1 0-88.28 0Z" p-id="8292"></path><path d="M353.12 830.01c-13.29 0-24.16-10.87-24.16-24.16v-84.77c0-13.29 10.87-24.16 24.16-24.16 13.29 0 24.16 10.87 24.16 24.16v84.77c0 13.29-10.87 24.16-24.16 24.16zM675.68 830.86c-13.29 0-24.16-10.87-24.16-24.16v-84.77c0-13.29 10.87-24.16 24.16-24.16 13.29 0 24.16 10.87 24.16 24.16v84.77c0 13.29-10.88 24.16-24.16 24.16zM513.04 948.69c-13.29 0-24.16-10.87-24.16-24.16v-84.77c0-13.29 10.87-24.16 24.16-24.16 13.29 0 24.16 10.87 24.16 24.16v84.77c0 13.29-10.87 24.16-24.16 24.16z" p-id="8293"></path></svg>
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="44" height="44" fill="url(#pattern0_3339_802)"/>
<defs>
<pattern id="pattern0_3339_802" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3339_802" transform="scale(0.005)"/>
</pattern>
<image id="image0_3339_802" width="200" height="200" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763374682921" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5607" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M747.008 257.536h-18.432c-12.288-35.84-46.08-61.44-86.016-61.44H247.296c-50.176 0-91.136 40.96-91.136 91.136v263.168c0 46.08 37.376 83.456 83.456 83.456 24.064 0 43.52 19.456 43.52 43.52v21.504c0 29.696 35.328 45.056 56.832 25.088l85.504-78.336c8.192-7.168 18.432-11.264 29.184-11.264h187.904c50.176 0 91.136-40.96 91.136-91.136V328.704h13.312c27.136 0 49.152 22.016 49.152 49.152v244.736c0 23.04-18.944 42.496-42.496 42.496-41.984 0-76.288 34.304-76.288 76.288v10.752l-72.704-66.56c-13.824-12.8-32.256-19.968-51.712-19.968H460.288c-19.456 0-35.84 15.872-35.84 35.84 0 19.456 15.872 35.84 35.84 35.84h93.184c1.024 0 2.56 0.512 3.072 1.024l79.36 73.216c12.8 11.264 28.672 17.92 45.056 17.92 9.216 0 18.432-2.048 27.136-5.632 25.088-10.752 40.448-34.304 40.448-61.44V742.4c0-2.56 2.048-4.608 4.608-4.608 62.464 0 113.664-51.2 113.664-113.664V377.856c0.512-66.56-53.248-120.32-119.808-120.32z m-233.984 260.608H311.808c-18.432 0-33.28-14.848-33.28-33.28s14.848-33.28 33.28-33.28h201.216c18.432 0 33.28 14.848 33.28 33.28s-14.848 33.28-33.28 33.28z m67.584-130.048H311.808c-8.704 0-17.408-3.584-23.552-9.728-6.144-6.144-9.728-14.848-9.728-23.552 0-8.704 3.584-17.408 9.728-23.552 6.144-6.144 14.848-9.728 23.552-9.728h268.8c18.432 0 33.28 14.848 33.28 33.28s-14.848 33.28-33.28 33.28z m0 0" fill="#ffffff" p-id="5608"></path><path d="M605.184 685.056c-13.824-12.8-32.256-19.968-51.712-19.968H460.288c-19.456 0-35.84 15.872-35.84 35.84 0 17.92 12.8 32.256 30.208 35.328 52.736-9.216 103.424-26.112 150.528-51.2z m0 0" fill="#ffffff" p-id="5609"></path><path d="M859.136 333.824c-17.408-44.544-61.44-76.288-112.128-76.288h-18.432c-12.288-35.84-46.08-61.44-86.016-61.44H247.296c-50.176 0-91.136 40.96-91.136 91.136v263.168c0 46.08 37.376 83.456 83.456 83.456 24.064 0 43.52 19.456 43.52 43.52v21.504c0 29.696 35.328 45.056 56.832 25.088l85.504-78.336c8.192-7.168 18.432-11.264 29.184-11.264h187.904c50.176 0 91.136-40.96 91.136-91.136V328.704h13.312c27.136 0 49.152 22.016 49.152 49.152v126.464c30.72-51.2 52.736-109.056 62.976-170.496z m-346.112 184.32H311.808c-18.432 0-33.28-14.848-33.28-33.28s14.848-33.28 33.28-33.28h201.216c18.432 0 33.28 14.848 33.28 33.28s-14.848 33.28-33.28 33.28z m67.584-130.048H311.808c-8.704 0-17.408-3.584-23.552-9.728-6.144-6.144-9.728-14.848-9.728-23.552 0-8.704 3.584-17.408 9.728-23.552 6.144-6.144 14.848-9.728 23.552-9.728h268.8c18.432 0 33.28 14.848 33.28 33.28s-14.848 33.28-33.28 33.28z m0 0" fill="#ffffff" p-id="5610"></path><path d="M457.728 518.144H311.808c-18.432 0-33.28-14.848-33.28-33.28s14.848-33.28 33.28-33.28h201.216c11.776 0 22.016 6.144 27.648 14.848 78.336-60.416 138.24-144.384 168.448-241.664-16.384-17.92-40.448-29.696-67.072-29.696H247.296c-50.176 0-91.136 40.96-91.136 91.136v263.168c0 3.584 0.512 7.68 1.024 11.264 27.648 5.12 56.32 7.168 85.504 7.168 76.8 0.512 150.016-17.408 215.04-49.664zM311.808 321.536h268.8c18.432 0 33.28 14.848 33.28 33.28s-14.848 33.28-33.28 33.28H311.808c-8.704 0-17.408-3.584-23.552-9.728-6.144-6.144-9.728-14.848-9.728-23.552 0-8.704 3.584-17.408 9.728-23.552 6.144-6.144 14.336-9.728 23.552-9.728z m0 0" fill="#ffffff" p-id="5611"></path><path d="M280.576 367.616c-1.536-4.096-2.56-8.192-2.56-12.8 0-8.704 3.584-17.408 9.728-23.552 6.144-6.144 14.848-9.728 23.552-9.728h65.536C430.08 288.768 476.16 245.76 513.536 195.584h-266.24c-50.176 0-91.136 40.96-91.136 91.136v107.008c43.52-3.584 84.992-12.288 124.416-26.112z m0 0" fill="#ffffff" p-id="5612"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1712731651220"
class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5138"
xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<path
d="M624.500364 925.184a204.8 204.8 0 0 0 233.192727-333.312l-233.192727 333.265455z m-45.149091-32.954182l232.029091-331.543273a204.8 204.8 0 0 0-231.982546 331.543273zM426.356364 571.485091c-181.434182 37.469091-280.994909 152.389818-302.638546 350.533818a27.927273 27.927273 0 0 1-55.528727-6.050909c21.504-196.654545 115.898182-325.026909 280.389818-381.207273a283.927273 283.927273 0 1 1 419.374546-109.149091 27.927273 27.927273 0 0 1-50.269091-24.203636 228.072727 228.072727 0 1 0-282.670546 115.898182 27.927273 27.927273 0 0 1-8.657454 54.178909zM721.454545 1005.381818a260.654545 260.654545 0 1 1 0-521.309091 260.654545 260.654545 0 0 1 0 521.309091z"
p-id="5139"></path>
</svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2.0957" y="8.7959" width="27.8093" height="7.58436" rx="3.79218" fill="white"/>
<rect opacity="0.3" x="12.8398" y="3" width="6.95233" height="3.57373" rx="1.78686" transform="rotate(45 12.8398 3)" fill="white"/>
<rect opacity="0.3" width="6.95233" height="3.57373" rx="1.78686" transform="matrix(-0.707107 0.707107 0.707107 0.707107 19.0195 3)" fill="white"/>
<path d="M21.376 11.9561C24.6897 11.9561 27.376 14.6423 27.376 17.9561V22.3887C27.376 25.7024 24.6897 28.3887 21.376 28.3887H10.623C7.30934 28.3887 4.62305 25.7024 4.62305 22.3887V17.9561C4.62305 14.6423 7.30934 11.9561 10.623 11.9561H21.376ZM16 18.3975C15.488 18.3975 15.0723 18.8123 15.0723 19.3242V22.1045C15.0723 22.6164 15.488 23.0322 16 23.0322C16.5118 23.032 16.9268 22.6163 16.9268 22.1045V19.3242C16.9268 18.8124 16.5118 18.3977 16 18.3975Z" fill="white"/>
</svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763611281052" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7362" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M190.193225 471.411583c14.446014 0 26.139334-11.718903 26.139334-26.13831 0-14.44499-11.69332-26.164916-26.139334-26.164916-0.271176 0-0.490164 0.149403-0.73678 0.149403l-62.496379 0.146333c-1.425466-0.195451-2.90005-0.295735-4.373611-0.295735-19.677155 0-35.621289 16.141632-35.621289 36.114522L86.622358 888.550075c0 19.949354 15.96767 35.597753 35.670407 35.597753 1.916653 0 3.808746 0.292666 5.649674 0l61.022819 0.022513c0.099261 0 0.148379 0.048095 0.24764 0.048095 0.097214 0 0.146333-0.048095 0.24457-0.048095l0.73678 0 0-0.148379c13.413498-0.540306 24.174586-11.422144 24.174586-24.960485 0-13.55983-10.760065-24.441669-24.174586-24.981974l0-0.393973-50.949392 0 1.450025-402.275993L190.193225 471.409536z" fill="#606266" p-id="7363"></path><path d="M926.52241 433.948343c-19.283182-31.445176-47.339168-44.172035-81.289398-45.546336-1.77032-0.246617-3.536546-0.39295-5.380544-0.39295l-205.447139-0.688685c13.462616-39.059598 22.698978-85.58933 22.698978-129.317251 0-28.349675-3.193739-55.962569-9.041934-82.542948l-0.490164 0.049119c-10.638291-46.578852-51.736315-81.31498-100.966553-81.31498-57.264215 0-95.466282 48.15065-95.466282 106.126063 0 3.241834-0.294712 6.387477 0 9.532097-2.996241 108.386546-91.240027 195.548698-196.23636 207.513194l0 54.881958-0.785899 222.227314 0 229.744521 10.709923 0 500.025271 0.222057 8.746198-0.243547c19.35686 0.049119 30.239721-4.817726 47.803749-16.116049 16.682961-10.761088 29.236881-25.50079 37.490869-42.156122 2.260483-3.341095 4.028757-7.075139 5.106298-11.20111l77.018118-344.324116c1.056052-4.053316 1.348718-8.181333 1.056052-12.160971C943.643346 476.446249 938.781618 453.944769 926.52241 433.948343zM893.82573 486.837924l-82.983993 367.783411-0.099261-0.049119c-2.555196 6.141884-6.879688 11.596106-12.872169 15.427364-4.177136 2.727111-8.773827 4.351098-13.414521 4.964058-1.49812-0.195451-3.046383 0-4.620227 0l-477.028511-0.540306-0.171915-407.408897c89.323375-40.266076 154.841577-79.670527 188.596356-173.661202 0.072655 0.024559 0.124843 0.049119 0.195451 0.072655 2.99931-9.137101 6.313799-20.73423 8.697079-33.164331 5.551436-29.185716 5.258771-58.123792 5.258771-58.123792-4.937452-37.98001 25.940812-52.965306 44.364417-52.965306 25.304316 0.860601 50.263777 33.656541 50.263777 52.326762 0 0 5.600555 27.563776 5.649674 57.190537 0.048095 37.366026-4.6673 56.847729-4.6673 56.847729l-0.466628 0c-5.872754 30.879288-16.214287 60.138682-30.464849 86.964654l0.36839 0.342808c-2.358721 4.815679-3.709485 10.220782-3.709485 15.943111 0 19.922748 19.088754 21.742187 38.765909 21.742187l238.761895 0.270153c0 0 14.666024 0.465604 14.690584 0.465604l0 0.100284c12.132318-0.638543 24.221658 5.207605 31.100322 16.409738 5.504364 9.016351 6.437619 19.6045 3.486404 28.988218L893.82573 486.837924z" fill="#606266" p-id="7364"></path><path d="M264.827039 924.31872c0.319272 0.024559 0.441045 0.024559 0.295735-0.024559 0.243547-0.048095 0.367367-0.074701-0.295735-0.074701s-0.539282 0.026606-0.271176 0.074701C264.43409 924.343279 264.532327 924.343279 264.827039 924.31872z" fill="#606266" p-id="7365"></path></svg>
\ No newline at end of file
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.4" x="7.13672" y="4" width="3.54545" height="8.86364" rx="1.77273" fill="#FFFEEB"/>
<rect opacity="0.4" x="21.3184" y="4" width="3.54545" height="8.86364" rx="1.77273" fill="#FFFEEB"/>
<path d="M27 8.72754C28.1046 8.72754 29 9.62297 29 10.7275V24.4551C28.9999 25.5595 28.1045 26.4551 27 26.4551H5C3.89552 26.4551 3.00014 25.5595 3 24.4551V10.7275C3 9.62297 3.89543 8.72754 5 8.72754H27ZM20.8428 14.2246C20.4522 13.8346 19.8191 13.8343 19.4287 14.2246L15.4082 18.2441L13.1611 15.9971C12.7706 15.6072 12.1374 15.6069 11.7471 15.9971C11.3567 16.3874 11.3571 17.0206 11.7471 17.4111L14.7012 20.3662C15.0917 20.7567 15.7257 20.7567 16.1162 20.3662L20.8428 15.6387C21.2333 15.2481 21.2333 14.6151 20.8428 14.2246Z" fill="white"/>
</svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.3" x="11.1074" y="10.1885" width="13.0886" height="2.37974" rx="1.18987" fill="white"/>
<rect opacity="0.3" x="11.1074" y="14.3545" width="9.51895" height="2.37974" rx="1.18987" fill="white"/>
<path d="M20.3623 3.64453C24.7806 3.64453 28.3623 7.22625 28.3623 11.6445V25.3594C28.3621 27.0161 27.0191 28.3594 25.3623 28.3594H9.94336C8.2866 28.3594 6.94351 27.0161 6.94336 25.3594V6.64453C6.94336 4.98768 8.28651 3.64453 9.94336 3.64453H20.3623ZM20.6289 22.6836C19.9718 22.6836 19.4385 23.2169 19.4385 23.874C19.4386 24.5311 19.9718 25.0635 20.6289 25.0635H23.0088C23.6657 25.0633 24.1982 24.531 24.1982 23.874C24.1982 23.217 23.6658 22.6838 23.0088 22.6836H20.6289ZM12.2979 14.3545C11.641 14.3547 11.1086 14.8871 11.1084 15.5439C11.1084 16.201 11.6409 16.7342 12.2979 16.7344H19.4375C20.0946 16.7344 20.627 16.2011 20.627 15.5439C20.6268 14.887 20.0945 14.3545 19.4375 14.3545H12.2979ZM12.2979 10.1885C11.6409 10.1887 11.1084 10.7219 11.1084 11.3789C11.1086 12.0358 11.641 12.5681 12.2979 12.5684H23.0068C23.6639 12.5684 24.1971 12.0359 24.1973 11.3789C24.1973 10.7218 23.664 10.1885 23.0068 10.1885H12.2979Z" fill="white"/>
<path opacity="0.3" d="M2 17.6494H11.0613V28.3582H6C3.79086 28.3582 2 26.5674 2 24.3582V17.6494Z" fill="white"/>
</svg>
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5888_1374)">
<path d="M33.3521 8.97001C33.3521 12.7555 30.2841 15.8228 26.4993 15.8228C22.7145 15.8228 19.6465 12.7555 19.6465 8.97001C19.6465 5.18588 22.7145 2.11719 26.4993 2.11719C30.2841 2.11719 33.3521 5.18588 33.3521 8.97001Z" fill="url(#paint0_linear_5888_1374)"/>
<foreignObject x="-23.0215" y="-20.4922" width="80.6074" height="79.2891"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(12px);clip-path:url(#bgblur_1_5888_1374_clip_path);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="24" d="M16.623 3.75781C8.12062 3.75781 1.22855 10.6515 1.22852 19.1523C1.22852 27.6563 8.12063 34.5469 16.623 34.5469C19.7388 34.5469 22.6379 33.6197 25.0615 32.0283L33.0342 33.7109C33.124 33.7299 33.217 33.6979 33.2764 33.6279C33.3356 33.558 33.3525 33.4614 33.3193 33.376L30.4355 25.9561C31.4479 23.9043 32.0176 21.5947 32.0176 19.1523C32.0175 10.6515 25.1254 3.75787 16.623 3.75781Z" fill="#FFCBB9" fill-opacity="0.35" stroke="url(#paint1_linear_5888_1374)" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<foreignObject x="-8.64648" y="-6.16309" width="49.0586" height="48.5293"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(7.5px);clip-path:url(#bgblur_2_5888_1374_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter2_d_5888_1374)" data-figma-bg-blur-radius="15">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3615 20.7137L11.9795 15.9656H8.44534C7.80352 15.9656 7.28321 15.4342 7.28321 14.7786C7.28321 14.1231 7.80351 13.5916 8.44534 13.5916H12.2885L12.7732 9.86763C12.8578 9.21778 13.4421 8.76101 14.0783 8.8474C14.7145 8.9338 15.1617 9.53064 15.0772 10.1805L14.6332 13.5916H18.6109L19.0757 10.0203C19.1603 9.37042 19.7446 8.91365 20.3809 9.00004C21.0171 9.08643 21.4643 9.68328 21.3797 10.3331L20.9556 13.5916H24.2502C24.892 13.5916 25.4123 14.1231 25.4123 14.7786C25.4123 15.4342 24.892 15.9656 24.2502 15.9656H20.6466L20.0286 20.7137H23.3205C23.9623 20.7137 24.4826 21.2451 24.4826 21.9007C24.4826 22.5562 23.9623 23.0877 23.3205 23.0877H19.7196L19.2968 26.3356C19.2123 26.9855 18.6279 27.4422 17.9917 27.3558C17.3555 27.2694 16.9083 26.6726 16.9929 26.0228L17.3749 23.0877H13.3972L12.9943 26.183C12.9097 26.8328 12.3254 27.2896 11.6892 27.2032C11.0529 27.1168 10.6058 26.52 10.6903 25.8701L11.0525 23.0877H7.51564C6.87382 23.0877 6.35352 22.5562 6.35352 21.9007C6.35352 21.2451 6.87382 20.7137 7.51564 20.7137H11.3615ZM13.7062 20.7137H17.6839L18.3019 15.9656H14.3242L13.7062 20.7137Z" fill="url(#paint2_linear_5888_1374)"/>
<path d="M14.0518 9.0459C14.5749 9.11715 14.9495 9.61002 14.8789 10.1543L14.4346 13.5654L14.4053 13.792H18.7861L18.8096 13.6172L19.2744 10.0459C19.3453 9.5024 19.8312 9.12756 20.3535 9.19824C20.8769 9.26932 21.2525 9.76306 21.1816 10.3076L20.7568 13.5654L20.7275 13.792H24.25C24.7773 13.792 25.2117 14.2293 25.2119 14.7783C25.2119 15.3274 24.7774 15.7656 24.25 15.7656H20.4707L20.4482 15.9395L19.8301 20.6875L19.8008 20.9141H23.3203C23.8476 20.9141 24.2821 21.3514 24.2822 21.9004C24.2822 22.4495 23.8477 22.8877 23.3203 22.8877H19.5439L19.5215 23.0615L19.0986 26.3096C19.0279 26.8533 18.5411 27.2282 18.0186 27.1572C17.4953 27.0861 17.1207 26.5933 17.1914 26.0488L17.5732 23.1133L17.6025 22.8877H13.2217L13.1992 23.0615L12.7959 26.1572C12.7251 26.7009 12.2383 27.0758 11.7158 27.0049C11.1925 26.9337 10.8178 26.44 10.8887 25.8955L11.251 23.1133L11.2803 22.8877H7.51562C6.98824 22.8877 6.55371 22.4495 6.55371 21.9004C6.55386 21.3514 6.98833 20.9141 7.51562 20.9141H11.5371L11.5596 20.7393L12.1777 15.9912L12.207 15.7656H8.44531C7.91793 15.7656 7.4834 15.3274 7.4834 14.7783C7.48357 14.2294 7.91803 13.792 8.44531 13.792H12.4639L12.4863 13.6172L12.9717 9.89355C13.0425 9.34979 13.5292 8.97495 14.0518 9.0459ZM14.126 15.9395L13.5078 20.6875L13.4785 20.9141H17.8594L17.8818 20.7393L18.5 15.9912L18.5293 15.7656H14.1484L14.126 15.9395Z" stroke="url(#paint3_linear_5888_1374)" stroke-width="0.4"/>
</g>
</g>
<defs>
<clipPath id="bgblur_1_5888_1374_clip_path" transform="translate(23.0215 20.4922)"><path d="M16.623 3.75781C8.12062 3.75781 1.22855 10.6515 1.22852 19.1523C1.22852 27.6563 8.12063 34.5469 16.623 34.5469C19.7388 34.5469 22.6379 33.6197 25.0615 32.0283L33.0342 33.7109C33.124 33.7299 33.217 33.6979 33.2764 33.6279C33.3356 33.558 33.3525 33.4614 33.3193 33.376L30.4355 25.9561C31.4479 23.9043 32.0176 21.5947 32.0176 19.1523C32.0175 10.6515 25.1254 3.75787 16.623 3.75781Z"/>
</clipPath><filter id="filter2_d_5888_1374" x="-8.64648" y="-6.16309" width="49.0586" height="48.5293" 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 dx="5" dy="5"/>
<feGaussianBlur stdDeviation="5"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.521569 0 0 0 0 0.133333 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5888_1374"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5888_1374" result="shape"/>
</filter>
<clipPath id="bgblur_2_5888_1374_clip_path" transform="translate(8.64648 6.16309)"><path fill-rule="evenodd" clip-rule="evenodd" d="M11.3615 20.7137L11.9795 15.9656H8.44534C7.80352 15.9656 7.28321 15.4342 7.28321 14.7786C7.28321 14.1231 7.80351 13.5916 8.44534 13.5916H12.2885L12.7732 9.86763C12.8578 9.21778 13.4421 8.76101 14.0783 8.8474C14.7145 8.9338 15.1617 9.53064 15.0772 10.1805L14.6332 13.5916H18.6109L19.0757 10.0203C19.1603 9.37042 19.7446 8.91365 20.3809 9.00004C21.0171 9.08643 21.4643 9.68328 21.3797 10.3331L20.9556 13.5916H24.2502C24.892 13.5916 25.4123 14.1231 25.4123 14.7786C25.4123 15.4342 24.892 15.9656 24.2502 15.9656H20.6466L20.0286 20.7137H23.3205C23.9623 20.7137 24.4826 21.2451 24.4826 21.9007C24.4826 22.5562 23.9623 23.0877 23.3205 23.0877H19.7196L19.2968 26.3356C19.2123 26.9855 18.6279 27.4422 17.9917 27.3558C17.3555 27.2694 16.9083 26.6726 16.9929 26.0228L17.3749 23.0877H13.3972L12.9943 26.183C12.9097 26.8328 12.3254 27.2896 11.6892 27.2032C11.0529 27.1168 10.6058 26.52 10.6903 25.8701L11.0525 23.0877H7.51564C6.87382 23.0877 6.35352 22.5562 6.35352 21.9007C6.35352 21.2451 6.87382 20.7137 7.51564 20.7137H11.3615ZM13.7062 20.7137H17.6839L18.3019 15.9656H14.3242L13.7062 20.7137Z"/>
</clipPath><linearGradient id="paint0_linear_5888_1374" x1="14.7508" y1="11.1493" x2="30.5186" y2="5.245" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFB37F"/>
<stop offset="1" stop-color="#FF7B0D"/>
</linearGradient>
<linearGradient id="paint1_linear_5888_1374" x1="28.0545" y1="7.53654" x2="5.64413" y2="35.776" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.25"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear_5888_1374" x1="23.0005" y1="27" x2="19.4667" y2="8.23516" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.264434" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint3_linear_5888_1374" x1="40.4635" y1="41.2229" x2="35.5559" y2="6.82202" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.399023" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<clipPath id="clip0_5888_1374">
<rect width="36" height="36" fill="white"/>
</clipPath>
</defs>
</svg>
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5888_1397)">
<g filter="url(#filter0_f_5888_1397)">
<path d="M25.6699 11.7781C26.1007 11.5549 26.6061 11.5782 27.0173 11.842C27.4285 12.1059 27.6738 12.5614 27.6738 13.0607V20.7336C27.6738 21.2328 27.4285 21.6884 27.0173 21.9512C26.7926 22.0943 26.5414 22.1673 26.2882 22.1673C26.0772 22.1673 25.8662 22.1176 25.669 22.0141L14.4219 16.9885L25.6699 11.7781Z" fill="#FC5D00"/>
</g>
<path d="M6 11C6 8.79086 7.79086 7 10 7H20C22.2091 7 24 8.79086 24 11V23C24 25.2091 22.2091 27 20 27H10C7.79086 27 6 25.2091 6 23V11Z" fill="url(#paint0_linear_5888_1397)"/>
<path d="M29.9967 8.55256C30.6625 8.20177 31.4435 8.23845 32.079 8.65301C32.7144 9.06758 33.0936 9.7835 33.0936 10.568V22.6254C33.0936 23.4099 32.7144 24.1258 32.079 24.5388C31.7317 24.7636 31.3434 24.8784 30.9521 24.8784C30.6261 24.8784 30.3 24.8003 29.9951 24.6377L12.6133 16.7403L29.9967 8.55256Z" fill="url(#paint1_linear_5888_1397)"/>
<foreignObject x="-20.9219" y="-19.5" width="70.3848" height="71.8896"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(12px);clip-path:url(#bgblur_1_5888_1397_clip_path);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="24" d="M18.6875 4.75C20.5935 4.75 22.2275 5.40798 23.3857 6.55176C24.5442 7.69587 25.2119 9.31161 25.2119 11.1973V21.6924C25.2119 23.5773 24.5442 25.1926 23.3857 26.3369C22.2275 27.481 20.5936 28.1396 18.6875 28.1396H9.85254C7.94642 28.1396 6.31255 27.481 5.1543 26.3369C3.99587 25.1926 3.32812 23.5772 3.32812 21.6924V11.1973L3.33594 10.8467C3.41348 9.11124 4.06817 7.62439 5.1543 6.55176C6.31256 5.40796 7.94654 4.75 9.85254 4.75H18.6875Z" fill="url(#paint2_linear_5888_1397)" fill-opacity="0.3" stroke="url(#paint3_linear_5888_1397)" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<foreignObject x="-3.75977" y="-3.13281" width="36.9512" height="39.1562"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(7.5px);clip-path:url(#bgblur_2_5888_1397_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter2_d_5888_1397)" data-figma-bg-blur-radius="15">
<path d="M11.2402 20.0215V12.8685C11.2402 12.0698 12.1304 11.5934 12.7949 12.0365L17.747 15.3379C18.3149 15.7165 18.345 16.5402 17.8063 16.9593L12.8542 20.8109C12.1973 21.3218 11.2402 20.8537 11.2402 20.0215Z" fill="url(#paint4_linear_5888_1397)"/>
<path d="M11.4404 12.8682C11.4407 12.2295 12.1521 11.8489 12.6836 12.2031L17.6357 15.5039C18.0901 15.8068 18.1146 16.4665 17.6836 16.8018L12.7314 20.6533C12.206 21.0619 11.4404 20.6872 11.4404 20.0215V12.8682Z" stroke="url(#paint5_linear_5888_1397)" stroke-width="0.4"/>
</g>
<foreignObject x="-18.5" y="5.5" width="65" height="51"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(12px);clip-path:url(#bgblur_3_5888_1397_clip_path);height:100%;width:100%"></div></foreignObject><rect data-figma-bg-blur-radius="24" x="5.75" y="29.75" width="16.5" height="2.5" rx="1.25" fill="url(#paint6_linear_5888_1397)" stroke="url(#paint7_linear_5888_1397)" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<filter id="filter0_f_5888_1397" x="5.42188" y="2.62598" width="31.252" height="28.541" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="4.5" result="effect1_foregroundBlur_5888_1397"/>
</filter>
<clipPath id="bgblur_1_5888_1397_clip_path" transform="translate(20.9219 19.5)"><path d="M18.6875 4.75C20.5935 4.75 22.2275 5.40798 23.3857 6.55176C24.5442 7.69587 25.2119 9.31161 25.2119 11.1973V21.6924C25.2119 23.5773 24.5442 25.1926 23.3857 26.3369C22.2275 27.481 20.5936 28.1396 18.6875 28.1396H9.85254C7.94642 28.1396 6.31255 27.481 5.1543 26.3369C3.99587 25.1926 3.32812 23.5772 3.32812 21.6924V11.1973L3.33594 10.8467C3.41348 9.11124 4.06817 7.62439 5.1543 6.55176C6.31256 5.40796 7.94654 4.75 9.85254 4.75H18.6875Z"/>
</clipPath><filter id="filter2_d_5888_1397" x="-3.75977" y="-3.13281" width="36.9512" height="39.1562" 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 dx="5" dy="5"/>
<feGaussianBlur stdDeviation="5"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.521569 0 0 0 0 0.133333 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5888_1397"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5888_1397" result="shape"/>
</filter>
<clipPath id="bgblur_2_5888_1397_clip_path" transform="translate(3.75977 3.13281)"><path d="M11.2402 20.0215V12.8685C11.2402 12.0698 12.1304 11.5934 12.7949 12.0365L17.747 15.3379C18.3149 15.7165 18.345 16.5402 17.8063 16.9593L12.8542 20.8109C12.1973 21.3218 11.2402 20.8537 11.2402 20.0215Z"/>
</clipPath><clipPath id="bgblur_3_5888_1397_clip_path" transform="translate(18.5 -5.5)"><rect x="5.75" y="29.75" width="16.5" height="2.5" rx="1.25"/>
</clipPath><linearGradient id="paint0_linear_5888_1397" x1="31.8262" y1="24.3684" x2="5.20647" y2="24.957" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF7B0D"/>
<stop offset="1" stop-color="#FFF3EB"/>
</linearGradient>
<linearGradient id="paint1_linear_5888_1397" x1="24.0401" y1="8.31348" x2="24.0401" y2="24.8784" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFB37F"/>
<stop offset="1" stop-color="#FF7B0D"/>
</linearGradient>
<linearGradient id="paint2_linear_5888_1397" x1="22.4732" y1="26.6853" x2="14.6333" y2="4.78791" gradientUnits="userSpaceOnUse">
<stop offset="0.00365973" stop-color="#FFF0E8"/>
<stop offset="1" stop-color="#FFE1B9"/>
</linearGradient>
<linearGradient id="paint3_linear_5888_1397" x1="5.88823" y1="8.48513" x2="30.9004" y2="26.0759" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.25"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear_5888_1397" x1="25.7827" y1="30.946" x2="21.1147" y2="10.0502" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.399023" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint5_linear_5888_1397" x1="24.9516" y1="30.3416" x2="20.6995" y2="10.2798" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.2"/>
<stop offset="0.399023" stop-color="white" stop-opacity="0.723067"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint6_linear_5888_1397" x1="20.1378" y1="31.8948" x2="20.0477" y2="29.74" gradientUnits="userSpaceOnUse">
<stop offset="0.00365973" stop-color="#FFDCC0"/>
<stop offset="1" stop-color="#FFC697"/>
</linearGradient>
<linearGradient id="paint7_linear_5888_1397" x1="7.72849" y1="30.3045" x2="8.47898" y2="34.8244" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.25"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<clipPath id="clip0_5888_1397">
<rect width="36" height="36" fill="white"/>
</clipPath>
</defs>
</svg>
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
You’ve successfully created a project with
<a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
const openReadmeInEditor = () => fetch('/__open-in-editor?file=README.md')
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vue’s
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vite.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a>
+
<a href="https://github.com/vuejs/language-tools" target="_blank" rel="noopener"
>Vue - Official</a
>. If you need to test your components and web pages, check out
<a href="https://vitest.dev/" target="_blank" rel="noopener">Vitest</a>
and
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a>
/
<a href="https://playwright.dev/" target="_blank" rel="noopener">Playwright</a>.
<br />
More instructions are available in
<a href="javascript:void(0)" @click="openReadmeInEditor"><code>README.md</code></a
>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>
(our official Discord server), or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also follow the official
<a href="https://bsky.app/profile/vuejs.org" target="_blank" rel="noopener">@vuejs.org</a>
Bluesky account or the
<a href="https://x.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
X account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>
<template>
<div
class="bg-white backdrop-blur-sm rounded-lg shadow-sm border border-white/50 overflow-hidden"
>
<!-- 发布者信息 -->
<div class="p-6 border-b border-gray-100">
<div class="flex items-center gap-4">
<div class="relative">
<img
:src="articleDetail?.createUserAvatar"
alt=""
class="w-12 h-12 rounded-full object-cover"
/>
<div
class="absolute -bottom-1 -right-1 w-6 h-6 bg-gradient-to-r from-yellow-400 to-orange-400 rounded-full flex items-center justify-center text-xs font-bold text-white"
>
8
</div>
</div>
<div class="flex-1">
<div class="flex items-center gap-2">
<h3 class="font-semibold text-gray-800">{{ articleDetail?.createUserName }}</h3>
<span
class="px-2 py-0.5 text-xs bg-gradient-to-r from-purple-100 to-blue-100 text-purple-600 rounded-full"
>
资深前端工程师
</span>
</div>
<p class="text-sm text-gray-500 mt-1">
{{ dayjs((articleDetail?.createTime || 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}
· {{ articleDetail?.viewCount || 0 }} 阅读
</p>
</div>
<el-button type="primary" link>{{ articleType }}</el-button>
</div>
</div>
<!-- 帖子内容 -->
<div class="p-6">
<h1 class="text-2xl font-bold text-gray-900 mb-4 leading-tight">
{{ articleDetail?.title }}
</h1>
<!-- 文章内容 -->
<div class="prose prose-lg max-w-none">
<div class="text-gray-700 leading-relaxed space-y-4">
{{ articleDetail?.content }}
</div>
<!-- 图片内容 -->
<div
v-if="articleDetail.imgUrl && articleDetail.type !== ArticleTypeEnum.VIDEO"
class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6"
>
<el-image
v-for="item in articleDetail.imgUrl.split(',')"
:key="item"
:src="item"
fit="cover"
class="rounded-lg w-full h-64 hover:scale-105 transition-transform cursor-pointer"
:preview-src-list="articleDetail.imgUrl.split(',')"
:preview-teleported="true"
/>
</div>
<div v-if="articleDetail.type === ArticleTypeEnum.VIDEO">
<video :src="articleDetail.videoUrl" controls />
</div>
</div>
<!-- 标签 -->
<div class="flex flex-wrap gap-2 mt-6">
<span
v-for="item in articleDetail?.tagNameList"
:key="item"
class="px-3 py-1 text-sm bg-gradient-to-r from-cyan-100 to-blue-100 text-cyan-600 rounded-full hover:shadow-md transition-all cursor-pointer"
>
#{{ item }}
</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
import { type ArticleItemDto } from '@/api'
import { articleTypeListOptions, ArticleTypeEnum } from '@/constants'
const { articleDetail } = defineProps<{
articleDetail: ArticleItemDto
}>()
const articleType = computed(() => {
return articleTypeListOptions.find((item) => item.value === articleDetail.type)?.label
})
</script>
<template>
<button class="btn" v-bind="attrs">测试111</button>
</template>
<script setup lang="ts">
// 二次封装el-button 除去el-button的默认样式 然后保留其他功能
import type { ButtonProps } from 'element-plus'
// const props = defineProps<ButtonProps>()
const attrs = useAttrs()
</script>
<style scoped>
.el-button {
/* 会作用到根元素 */
all: unset !important;
}
</style>
<!-- LoadingComponent.vue -->
<template>
<div class="loading-container">
<div class="loading-spinner"></div>
<span v-if="showText" class="loading-text">{{ text }}</span>
</div>
</template>
<script setup lang="ts">
interface Props {
text?: string
showText?: boolean
}
withDefaults(defineProps<Props>(), {
text: '加载中...',
showText: true,
})
</script>
<style scoped>
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
gap: 12px;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f0f0f0;
border-top-color: #409eff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.loading-text {
font-size: 14px;
color: #909399;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
<template>
<el-dialog v-model="dialogVisible" title="选择标签" width="500px" :close-on-click-modal="false">
<div class="space-y-6 px-2">
<div class="flex items-start gap-4">
<div class="text-sm text-gray-700 w-16 flex-shrink-0">主标签</div>
<div class="flex-1">
<SelectTags v-model="mainTagId" />
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-sm text-gray-700 w-16 flex-shrink-0">副标签</div>
<div class="flex-1">
<SelectTags
v-model="subTagIdList"
:max-selected-tags="3"
:filter-tags-fn="filterTagsFn"
/>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import SelectTags from '@/components/common/SelectTags/index.vue'
import type { TagItemDto } from '@/api'
const dialogVisible = ref(false)
const mainTagId = defineModel<string>('mainTagId', { required: true })
const subTagIdList = defineModel<number[]>('tagList', { required: true })
const open = () => {
dialogVisible.value = true
}
const filterTagsFn = (allTags: TagItemDto[]) => {
return allTags.filter((tag) => tag.id !== Number(mainTagId.value))
}
defineExpose({
open,
})
</script>
<style scoped></style>
<template>
<div class="bg-white p-6 mb-6 rounded-lg shadow-sm">
<div class="flex-1 bg-white rounded-lg border border-gray-200">
<!-- 主输入区域 -->
<div class="flex gap-3 mb-4 items-start">
<!-- 用户头像 -->
<el-avatar :size="48" :src="userInfo.avatar" class="flex-shrink-0">
<el-icon><User /></el-icon>
</el-avatar>
<!-- 输入区域 -->
<div class="flex-1">
<!-- 话题标签输入 -->
<div class="mb-4">
<el-input
v-model="form.title"
:placeholder="textMap[type].title"
class="tag-input"
clearable
/>
</div>
<!-- 主要内容输入 -->
<div class="relative mb-3">
<el-input
type="textarea"
:placeholder="textMap[type].content"
:rows="3"
:maxlength="500"
resize="none"
class="main-textarea"
v-model="form.content"
/>
<!-- 字符计数 -->
<div class="absolute bottom-3 right-3 text-xs text-gray-400">1/30</div>
</div>
<!-- 标签内容 -->
<div class="mb-2">
<!-- 选择的标签内容 -->
<div class="flex items-center gap-2">
<span v-if="mainTagText" class="text-sm text-gray-500"
>主标签:
<el-tag>{{ mainTagText }}</el-tag>
</span>
<span v-if="subTagTextList.length > 0" class="text-sm text-gray-500"
>副标签:
<el-tag class="mr-2" v-for="tag in subTagTextList" :key="tag">{{ tag }}</el-tag>
</span>
</div>
</div>
<!-- 图片相关 -->
<div v-if="form.imgUrl.length" class="flex flex-wrap gap-2">
<!-- 删除图片 -->
<div
class="relative w-20 h-20 rounded-lg overflow-hidden group"
v-for="img in form.imgUrl"
:key="img"
>
<div
class="absolute top-1 right-1 z-10 w-5 h-5 flex items-center justify-center bg-black/60 rounded-full cursor-pointer opacity-0 group-hover:opacity-100 transition-all duration-200 hover:bg-black/80 hover:scale-110"
@click="handleDeleteImg(img)"
>
<el-icon class="text-white text-xs">
<Close />
</el-icon>
</div>
<el-image
:src="img"
class="w-full h-full rounded-lg border border-gray-200"
fit="cover"
/>
</div>
</div>
</div>
</div>
<!-- 工具栏 -->
<div class="flex items-center justify-between pl-15">
<!-- 左侧工具按钮 -->
<div class="flex items-center gap-1">
<el-tooltip content="添加标签" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
@click="handleAddTag"
>
<el-icon size="18"><CollectionTag /></el-icon>
</el-button>
</el-tooltip>
<!-- 隐藏上传文件的input -->
<input type="file" class="hidden" ref="fileInputRef" @change="handleFileChange" />
<el-tooltip content="添加图片" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
@click="fileInputRef?.click()"
>
<el-icon size="18"><Picture /></el-icon>
</el-button>
</el-tooltip>
<!-- <el-tooltip content="添加视频" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
>
<el-icon size="18"><VideoPlay /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="添加附件" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
>
<el-icon size="18"><Paperclip /></el-icon>
</el-button>
</el-tooltip> -->
</div>
<!-- 右侧操作按钮 -->
<div class="flex items-center gap-3">
<el-button
class="px-4 py-2 text-gray-600 hover:text-gray-800 hover:bg-gray-50 rounded-lg border border-gray-200 text-sm"
@click="handlePublish(ReleaseStatusTypeEnum.DRAFT)"
>
存草稿
</el-button>
<el-button
type="primary"
class="px-6 py-2 bg-blue-500 hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200"
@click="handlePublish(ReleaseStatusTypeEnum.PUBLISH)"
>
发布 {{ type === ArticleTypeEnum.QUESTION ? '问题' : '实践' }}
</el-button>
</div>
</div>
</div>
<SelectTagsDialog
v-model:mainTagId="form.mainTagId"
v-model:tagList="form.tagList"
ref="selectTagsDialogRef"
/>
</div>
</template>
<!-- 发布实践 或者 问吧 的 发布框 -->
<script setup lang="ts">
import { useUserStore } from '@/stores'
import { storeToRefs } from 'pinia'
import SelectTagsDialog from './components/selectTagsDialog.vue'
import { useResetData } from '@/hooks'
import { ArticleTypeEnum, ReleaseStatusTypeEnum, SendTypeEnum } from '@/constants'
import { useTagsStore } from '@/stores'
import { uploadFile } from '@/api'
import { Close } from '@element-plus/icons-vue'
import { addOrUpdatePractice, addOrUpdateArticle } from '@/api'
import type { AddOrUpdatePracticeDto } from '@/api/practice/types'
type ArticleType = ArticleTypeEnum.QUESTION | ArticleTypeEnum.PRACTICE
const { type } = defineProps<{
type: ArticleType
}>()
const textMap: Record<
ArticleType,
{ title: string; content: string; api: (data: any) => Promise<any> }
> = {
[ArticleTypeEnum.QUESTION]: {
title: '问题标题',
content: '请输入问题内容',
api: addOrUpdateArticle,
},
[ArticleTypeEnum.PRACTICE]: {
title: '实践标题',
content: '请输入实践内容',
api: addOrUpdatePractice,
},
}
const tagsStore = useTagsStore()
const { tagList } = storeToRefs(tagsStore)
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
const selectTagsDialogRef =
useTemplateRef<InstanceType<typeof SelectTagsDialog>>('selectTagsDialogRef')
const fileInputRef = useTemplateRef<HTMLInputElement>('fileInputRef')
const [form, resetForm] = useResetData({
title: '',
content: '',
imgUrl: [],
releaseStatus: ReleaseStatusTypeEnum.PUBLISH,
mainTagId: '',
tagList: [],
sendType: SendTypeEnum.IMMEDIATE,
sendTime: '',
})
const mainTagText = computed(() => {
return tagList.value.find((tag) => tag.id === Number(form.value.mainTagId))?.title
})
const subTagTextList = computed(() => {
return form.value.tagList.map((tag) => tagList.value.find((t) => t.id === tag)?.title)
})
const handleAddTag = () => {
selectTagsDialogRef.value?.open()
}
const handleFileChange = async (e: Event) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (file) {
const { data } = await uploadFile(file)
form.value.imgUrl.push(data.data[0].filePath)
}
}
const handleDeleteImg = (img: string) => {
form.value.imgUrl = form.value.imgUrl.filter((item) => item !== img)
}
const validateForm = () => {
if (!form.value.title) {
ElMessage.error('请输入实践标题')
return false
}
if (!form.value.content) {
ElMessage.error('请输入实践内容')
return false
}
if (!form.value.mainTagId) {
ElMessage.error('请选择主标签')
return false
}
return true
}
const transformForm = (releaseStatus: ReleaseStatusTypeEnum): AddOrUpdatePracticeDto => {
return {
...form.value,
releaseStatus,
faceUrl: form.value.imgUrl[0] || '',
imgUrl: form.value.imgUrl.join(','),
tagList: [form.value.mainTagId, ...form.value.tagList].map((item, index) => ({
sort: index,
tagId: Number(item),
})),
type: type,
}
}
const handlePublish = async (releaseStatus: ReleaseStatusTypeEnum) => {
if (!validateForm()) return
await textMap[type].api(transformForm(releaseStatus))
ElMessage.success(releaseStatus === ReleaseStatusTypeEnum.PUBLISH ? '发布成功' : '存草稿成功')
resetForm()
}
</script>
<style scoped></style>
<template>
<div class="tag-selector">
<!-- 已选标签区域 -->
<div class="selected-tags">
<!-- 添加标签按钮 -->
<el-popover placement="top" :width="300" trigger="click">
<template #reference>
<el-button class="button-new-tag" size="small"> + 添加标签 </el-button>
</template>
<!-- 暂时不加搜索输入框 -->
<!-- <div class="search-section mb-4">
<el-input
v-model="searchKeyword"
placeholder="Enter键添加标签"
class="search-input"
clearable
@keyup.enter="addTagFromSearch"
>
<template #suffix>
<span class="text-gray-400 text-sm"
>{{ searchKeyword.length }} / {{ maxTagLength }}</span
>
</template>
</el-input>
</div> -->
<!-- 推荐标签区域 -->
<div class="recommended-section">
<div class="section-title mb-3 text-gray-600 text-sm">官方标签列表</div>
<div class="tags-grid">
<el-tag
v-for="tag in filteredRecommendedTags"
:key="tag.id"
:type="arryrOfModelValue.includes(tag.id) ? 'primary' : 'info'"
:effect="arryrOfModelValue.includes(tag.id) ? 'dark' : 'plain'"
class="tag-item cursor-pointer mr-2 mb-2"
@click="toggleTag(tag.id)"
>
{{ tag.title }}
</el-tag>
</div>
</div>
</el-popover>
<div class="flex flex-wrap gap-2 mt-2">
<el-tag
v-for="tag in selectedTags"
:key="tag.id"
closable
type="primary"
@close="removeTag(tag.id)"
>
{{ tag.title }}
</el-tag>
</div>
</div>
</div>
</template>
// 选中的可能是 可能是 多选 [id1,id2] 或者 'id1,id2' 或者 (单选 number这种不考虑处理了
可以在父组件自己处理)
<script setup lang="ts" generic="T extends string | number[] | number">
import { useTagsStore } from '@/stores/tags'
import { storeToRefs } from 'pinia'
import type { TagItemDto } from '@/api/tag/types'
import type { SelectTagProps } from './types'
const { maxSelectedTags = 1, filterTagsFn } = defineProps<SelectTagProps>()
const emit = defineEmits<{
selected: [tag?: TagItemDto]
}>()
const tagsStore = useTagsStore()
const { tagList } = storeToRefs(tagsStore)
const filterTags = computed(() => {
if (filterTagsFn) {
return filterTagsFn(tagList.value)
}
return tagList.value
})
const modelValue = defineModel<T>({ required: true })
const isArrayOfModelValue = Array.isArray(modelValue.value)
// 把v-model转成一个 array类型的 方便后续操作的操作
const arryrOfModelValue = computed({
get() {
if (isArrayOfModelValue) {
return (modelValue.value as number[]).filter((tagId) => {
return filterTags.value.some((tag) => tag.id === tagId)
})
}
return (modelValue.value as string)
.split(',')
.filter(Boolean)
.map(Number)
.filter((tagId) => filterTags.value.some((tag) => tag.id === tagId))
},
set(value) {
if (isArrayOfModelValue) {
modelValue.value = value as T
} else {
modelValue.value = value.join(',') as T
}
},
})
// 选中的标签
const selectedTags = computed(() => {
const selectedTags: TagItemDto[] = []
arryrOfModelValue.value.forEach((tagId) => {
const tag = filterTags.value.find((tag) => tag.id === tagId)
if (tag) {
selectedTags.push(tag)
}
})
return selectedTags
})
const searchKeyword = ref('')
const filteredRecommendedTags = computed(() => {
if (!searchKeyword.value) {
return filterTags.value
}
return filterTags.value.filter((tag) =>
tag.title.toLowerCase().includes(searchKeyword.value.toLowerCase()),
)
})
const addTag = (tagId: number) => {
if (arryrOfModelValue.value.length >= maxSelectedTags)
return ElMessage.warning(`最多只能选择 ${maxSelectedTags} 个标签`)
// 不能直接push 触发不了computed 的 set
arryrOfModelValue.value = [...arryrOfModelValue.value, tagId]
ElMessage.success('标签添加成功')
emit('selected', filterTags.value.find((tag) => tag.id === tagId)!)
}
const removeTag = (tagId: number) => {
const index = arryrOfModelValue.value.indexOf(tagId)
if (index > -1) {
// 不能直接splice 触发不了computed 的 set
arryrOfModelValue.value = [...arryrOfModelValue.value.filter((_, i) => i !== index)]
}
emit('selected', undefined)
}
const toggleTag = (tagId: number) => {
if (arryrOfModelValue.value.includes(tagId)) {
removeTag(tagId)
} else {
addTag(tagId)
}
}
// onMounted(() => {
// if (tagList.value.length === 0) {
// tagsStore.fetchTagList()
// }
// })
</script>
import type { TagItemDto } from '@/api/tag/types'
export type SelectTagProps = {
maxSelectedTags?: number
filterTagsFn?: (tags: TagItemDto[]) => TagItemDto[]
}
<template>
<svg :class="svgClass" aria-hidden="true" :style="{ width: size, height: size }">
<use :xlink:href="iconName" />
</svg>
</template>
<script setup lang="ts">
interface IProp {
name: string
size?: string
className?: string
}
const props = withDefaults(defineProps<IProp>(), {
size: '1em',
color: '',
className: '',
})
const iconName = computed(() => {
return `#icon-${props.name}`
})
const svgClass = computed(() => {
if (props.className) {
return `svg-icon ${props.className}`
}
return 'svg-icon'
})
</script>
<style lang="scss" scoped>
.svg-icon {
vertical-align: -0.1em;
fill: currentColor;
overflow: hidden;
}
</style>
import type { SetupContext } from 'vue'
type TypeOfValue = string | number
interface TabsProps<T> {
tabs: {
label: string
value: T
}[]
modelValue: T
}
interface TabsEmits<T> {
'update:modelValue': [T]
change: [T]
}
const BASE_TAB_CALASSES =
'tab-item cursor-pointer bg-#d9effd/80 rounded-lg flex items-center justify-center px-4 py-2 min-w-20 h-8 transition-all duration-300 hover:shadow-md hover:bg-blue-100 hover:text-blue-700 hover:-translate-y-0.5 active:scale-95 font-medium text-14px text-gray-700 shadow-sm border border-blue-100'
const ACTIVE_TAB_CLASSES =
' !bg-gradient-to-r !from-[#3b82f6]/90 !to-[#60a5fa]/90 !shadow-lg transform -translate-y-1 !text-white !border-transparent'
// <div class="left flex gap-3"> 未设置排列方式 需要给父组件设置 flex布局
export default function Tabs<T extends TypeOfValue>(
{ tabs, modelValue }: TabsProps<T>,
{ emit }: SetupContext<TabsEmits<T>>,
) {
return tabs.map((tab) => (
<div
key={tab.value}
class={[
BASE_TAB_CALASSES,
{
[ACTIVE_TAB_CLASSES]: modelValue === tab.value,
},
]}
onClick={() => {
emit('update:modelValue', tab.value)
emit('change', tab.value)
}}
>
{tab.label}
</div>
))
}
<template>
<el-upload
v-model:file-list="fileList"
action="#"
ref="uploadRef"
:auto-upload="false"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:on-change="handleChange"
:before-remove="handleBeforeRemove"
:on-exceed="handleExceed"
:multiple="multiple"
:limit="limit"
class="custom-upload"
>
<el-icon><Plus /></el-icon>
</el-upload>
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
<script lang="ts" setup generic="T extends string | string[]">
import { Plus } from '@element-plus/icons-vue'
import { uploadFile as uploadFileApi } from '@/api'
import type { UploadProps, UploadUserFile } from 'element-plus'
import type { UploadFileProps } from './types'
const props = withDefaults(defineProps<UploadFileProps>(), {
limit: 2,
multiple: false,
})
const modelValue = defineModel<T>({
required: true,
})
const fileList = ref<UploadUserFile[]>([])
const uploadRef = useTemplateRef('uploadRef')
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
const isArrayType = computed(() => Array.isArray(modelValue.value))
const hasReachedLimit = computed(() => fileList.value.length >= props.limit)
const showUploadBtn = computed(() => (hasReachedLimit.value ? 'none' : 'flex'))
const isInternalUpdate = ref(false)
const uploadProgress = ref(0)
const parseModelValueToUrls = (value: T): string[] => {
if (!value) return []
return Array.isArray(value) ? value.filter(Boolean) : (value as string).split(',').filter(Boolean)
}
const formatUrlsToModelValue = (urls: string[]): T => {
return (isArrayType.value ? urls : urls.join(',')) as T
}
const syncFileListToModel = () => {
if (isInternalUpdate.value) return
const urls = fileList.value
.filter((file) => file.status === 'success' && file.url)
.map((file) => file.url!)
isInternalUpdate.value = true
modelValue.value = formatUrlsToModelValue(urls)
nextTick(() => {
isInternalUpdate.value = false
})
}
const syncModelToFileList = () => {
const urls = parseModelValueToUrls(modelValue.value)
fileList.value = urls.map((url, index) => ({
uid: Date.now() + index,
name: url.split('/').pop() || `file-${index}`,
url,
status: 'success' as const,
}))
}
watch(
() => modelValue.value,
(newVal) => {
if (isInternalUpdate.value) return
if (newVal !== undefined && newVal !== null) {
syncModelToFileList()
}
},
{ immediate: true },
)
watch(
fileList,
() => {
syncFileListToModel()
},
{ deep: true },
)
const handleExceed: UploadProps['onExceed'] = (uploadFiles) => {
console.log('uploadFiles', uploadFiles)
if (uploadFiles.length > props.limit) {
ElMessage.error(`最多上传 ${props.limit} 个文件`)
return
}
}
const handleChange: UploadProps['onChange'] = async (uploadFile, uploadFiles) => {
console.log('uploadFiles', uploadFiles)
if (uploadFiles.length > props.limit) {
debugger
ElMessage.error(`最多上传 ${props.limit} 个文件`)
const index = fileList.value.findIndex((file) => file.uid === uploadFile.uid)
if (index !== -1) {
fileList.value.splice(index, 1)
}
return
}
if (uploadFile.raw && uploadFile.status === 'ready') {
const uid = uploadFile.uid
try {
let fileIndex = fileList.value.findIndex((file) => file.uid === uid)
if (fileIndex !== -1) {
fileList.value[fileIndex].status = 'uploading'
}
const { data } = await uploadFileApi(uploadFile.raw, (progress) => {
console.log('progress', progress)
})
console.log('data', data)
const url = data.fileUrl || data.data[0].filePath
const name = data.fileName || data.data[0].finalName
fileIndex = fileList.value.findIndex((file) => file.uid === uid)
if (fileIndex !== -1) {
fileList.value[fileIndex] = {
...fileList.value[fileIndex],
url,
name,
status: 'success',
}
ElMessage.success('上传成功')
} else {
console.warn('找不到对应的文件,uid:', uid)
}
} catch (error) {
console.error('上传失败:', error)
ElMessage.error('上传失败,请重试')
const fileIndex = fileList.value.findIndex((file) => file.uid === uid)
if (fileIndex !== -1) {
fileList.value.splice(fileIndex, 1)
}
}
}
}
const handleBeforeRemove: UploadProps['beforeRemove'] = (uploadFile) => {
return ElMessageBox.confirm('确定要删除这个文件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => true)
.catch(() => false)
}
const handleRemove: UploadProps['onRemove'] = (uploadFile) => {
console.log('文件已删除:', uploadFile.name)
}
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
dialogImageUrl.value = uploadFile.url!
dialogVisible.value = true
}
const clearFiles = () => {
fileList.value = []
}
const submit = () => {
uploadRef.value?.submit()
}
defineExpose({
clearFiles,
submit,
})
</script>
<style scoped>
.custom-upload :deep(.el-upload--picture-card) {
display: v-bind(showUploadBtn);
}
/* 方案1: 适中尺寸(推荐) */
.custom-upload :deep(.el-upload--picture-card) {
width: 100px;
height: 100px;
line-height: 100px;
}
.custom-upload :deep(.el-upload-list--picture-card .el-upload-list__item) {
width: 100px;
height: 100px;
}
/* 方案2: 更小尺寸 */
/* .custom-upload :deep(.el-upload--picture-card) {
width: 80px;
height: 80px;
line-height: 80px;
}
.custom-upload :deep(.el-upload-list--picture-card .el-upload-list__item) {
width: 80px;
height: 80px;
}
.custom-upload :deep(.el-icon) {
font-size: 20px;
} */
/* 方案3: 迷你尺寸 */
/* .custom-upload :deep(.el-upload--picture-card) {
width: 60px;
height: 60px;
line-height: 60px;
}
.custom-upload :deep(.el-upload-list--picture-card .el-upload-list__item) {
width: 60px;
height: 60px;
}
.custom-upload :deep(.el-icon) {
font-size: 16px;
} */
</style>
export interface UploadFileProps {
multiple?: boolean
limit?: number
}
export interface UploadVideoProps {
maxSize?: number // MB
acceptFormats?: string[]
}
interface IConfig {
/** 网络请求url */
baseUrl: string
/** 用户登录 type */
loginType: number
/** 微信登录跳转路径 */
wxRedirect: string
}
export const app_config: { [key: string]: IConfig } = {
// 正式环境
production: {
baseUrl: 'http://oa.yswg.com.cn:8082',
loginType: 3,
wxRedirect: 'oa3.yswg.com.cn',
},
// 测试环境
test: {
baseUrl: 'http://47.119.149.50:8082',
loginType: 1,
wxRedirect: 'oatest.yswg.com.cn:3457',
},
// 开发环境
development: {
// baseUrl: 'http://oa.yswg.com.cn:8082', // 正式环境
baseUrl: '/api1', // 线上测试机
// baseUrl: 'http://192.168.2.110:8082', // 线上测试机
// baseUrl: 'http://192.168.2.85:8080', // 洋倍
// baseUrl: 'http://192.168.2.12:8084', // 立鹏
// baseUrl: 'http://192.168.2.55:8089', // 首拥
loginType: 1,
wxRedirect: 'oatest.yswg.com.cn:3457',
},
}
// 文章类型枚举
export enum ArticleTypeEnum {
VIDEO = 'video', // 视频
POST = 'post', // 帖子
QUESTION = 'question', // 问题
COLUMN = 'column', // 专栏
PRACTICE = 'practice', // 实践
INTERVIEW = 'interview', // 专访
}
// 发布状态枚举
export enum ReleaseStatusTypeEnum {
DRAFT = 1, // 草稿
PUBLISH = 2, // 发布
}
// 任务类型枚举
export enum TaskTypeEnum {
// 常规任务
REGULAR_TASK = 1,
// 特殊任务
SPECIAL_TASK = 2,
}
// 商品类型枚举
export enum ShopGoodsTypeEnum {
// 实物
REAL_GOODS = 1,
// 虚拟
VIRTUAL_GOODS = 2,
}
// 布尔值枚举
export enum BooleanFlag {
NO = 0, // 否
YES = 1, // 是
}
// 发送类型枚举
export enum SendTypeEnum {
// 立即发送
IMMEDIATE = 1,
// 定时发送
SCHEDULED = 2,
}
// 任务日期限制类型枚举
export enum TaskDateLimitTypeEnum {
// 每日
DAY = 'DAY',
// 每周
WEEK = 'WEEK',
// 每月
MONTH = 'MONTH',
}
// 标签类型枚举
export enum TagTypeEnum {
// 文化关键词
CULTURE_TAG = 0,
// 年度关键词
YEAR_TAG = 1,
// 关联场景
SCENE_TAG = 2,
}
// 标签级别
export enum TagLevelEnum {
// 副标签
SUB_TAG = 0,
// 主标签
MAIN_TAG = 1,
}
// 回复类型
export enum CommentTypeEnum {
// 我评论别人的
COMMNET_TO_OTHER = 1,
// 别人评论我的
COMMNET_TO_SELF = 2,
}
// 审核状态枚举
export enum AuditStatusEnum {
// 未审核
UNAUDITED = 0,
// 通过审核
AGREED = 1,
// 驳回审核
REJECTED = 2,
}
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment