update
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
<title>Freeleaps</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"pdfjs-dist": "^4.3.136",
|
||||
"pinia": "^2.1.6",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.2.4",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 16 KiB |
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="app-body">
|
||||
<img src="@/assets/images/home-bg-left.png" alt="freeleaps" class="app-bg-left">
|
||||
<img src="@/assets/images/home-bg-right.png" alt="freeleaps" class="app-bg-right">
|
||||
<header class="body-header">
|
||||
<router-view name="header"></router-view>
|
||||
</header>
|
||||
@ -34,6 +36,22 @@ export default {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.app-bg-left {
|
||||
width: 300px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.app-bg-right {
|
||||
width: 300px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.body-header {
|
||||
width: 100%;
|
||||
height: $header-height;
|
||||
|
||||
BIN
frontend/src/assets/images/home-bg-left.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
frontend/src/assets/images/home-bg-right.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
frontend/src/assets/lang/us.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
frontend/src/assets/lang/zh.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
@ -1 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
<svg width="12" height="19" viewBox="0 0 12 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.56464 6.68937L0 9.35254L2.56464 12.0157L5.12928 9.35254L2.56464 6.68937Z" fill="#A4CBFA"/>
|
||||
<path d="M5.78339 3.34465L3.21875 6.00781L5.78339 8.67098L8.34803 6.00781L5.78339 3.34465Z" fill="#5E8AF9"/>
|
||||
<path d="M5.78729 10.0321L3.22266 12.6953L5.78729 15.3585L8.35193 12.6953L5.78729 10.0321Z" fill="#5E8AF9"/>
|
||||
<path d="M9.00995 13.3798L6.44531 16.043L9.00995 18.7061L11.5746 16.043L9.00995 13.3798Z" fill="#1748F8"/>
|
||||
<path d="M9.00214 6.68742L6.4375 9.35059L9.00214 12.0138L11.5668 9.35059L9.00214 6.68742Z" fill="#1748F8"/>
|
||||
<path d="M9.00604 -7.86142e-05L6.44141 2.66309L9.00604 5.32625L11.5707 2.66309L9.00604 -7.86142e-05Z" fill="#1748F8"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 276 B After Width: | Height: | Size: 757 B |
@ -44,6 +44,10 @@ $header-height: 88px;
|
||||
$footer-height: 110px;
|
||||
$body-height: calc(100vh - $header-height - $footer-height);
|
||||
$body-width: 1288px;
|
||||
|
||||
$accordion-icon-width: 1rem;
|
||||
$accordion-button-icon: url("data:image/svg+xml,<svg viewBox='0 0 1331 1024' xmlns='http://www.w3.org/2000/svg' width='64' height='64'><path d='M552.5504 896.512L45.1584 244.1216A102.4 102.4 0 0 1 125.952 78.848h1014.784a102.4 102.4 0 0 1 80.896 165.2736l-507.4944 652.288a102.4 102.4 0 0 1-161.6896 0z'></path></svg>");
|
||||
$accordion-button-active-icon: url("data:image/svg+xml,<svg viewBox='0 0 1331 1024' xmlns='http://www.w3.org/2000/svg' width='64' height='64'><path d='M552.5504 896.512L45.1584 244.1216A102.4 102.4 0 0 1 125.952 78.848h1014.784a102.4 102.4 0 0 1 80.896 165.2736l-507.4944 652.288a102.4 102.4 0 0 1-161.6896 0z'></path></svg>");
|
||||
// $default-font: "Lato", Verdana, Arial, sans-serif;
|
||||
|
||||
// $link-color: #ef899e;
|
||||
|
||||
@ -179,6 +179,16 @@ p {
|
||||
padding: 12px 28px 12px 12px;
|
||||
outline: none;
|
||||
box-shadow: none !important;
|
||||
&::before {
|
||||
content: '';
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
background-color: #dee2e6;
|
||||
transition: all 300ms ease-in-out;
|
||||
}
|
||||
.dashed-container {
|
||||
flex: 1;
|
||||
margin-right: 28px;
|
||||
@ -191,7 +201,11 @@ p {
|
||||
color: black;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
// border-bottom: 1px solid #dee2e6;
|
||||
&::before {
|
||||
left: 0;
|
||||
width: 100%;;
|
||||
}
|
||||
.dashed-container {
|
||||
background-color: #f3f6ff;
|
||||
}
|
||||
@ -232,3 +246,11 @@ p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
input:-webkit-autofill {
|
||||
box-shadow:0 0 0 1000px white inset !important;
|
||||
}
|
||||
input:-internal-autofill-previewed,
|
||||
input:-internal-autofill-selected {
|
||||
transition: background-color 5000s ease-in-out 0s !important;
|
||||
}
|
||||
@ -237,7 +237,6 @@ export default {
|
||||
getSelect() {
|
||||
if (window.getSelection) {
|
||||
let sel = window.getSelection()
|
||||
console.log('this.selectedRange', sel)
|
||||
if (sel.rangeCount > 0) {
|
||||
return sel.getRangeAt(0)
|
||||
}
|
||||
@ -290,6 +289,7 @@ export default {
|
||||
default:
|
||||
console.log('none')
|
||||
}
|
||||
this.$refs.editor.blur()
|
||||
// window.getSelection().removeAllRanges()
|
||||
},
|
||||
restoreSelection() {
|
||||
@ -307,7 +307,9 @@ export default {
|
||||
|
||||
updateAction($event) {
|
||||
let html = $event.target.innerHTML || ''
|
||||
setTimeout(() => {
|
||||
this.$emit('update:content', html)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
66
frontend/src/components/LaguageSwitch.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="laguage-switch-container">
|
||||
<button class="btn btn-link btn-link-switch" data-bs-toggle="dropdown" aria-expanded="false" id="language-switch-button">
|
||||
<img :src="languageMapper[currentLanguage]" alt="freeleap language">
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="language-switch-button">
|
||||
<template v-for="item in languageList" :key="item">
|
||||
<li v-if="item != currentLanguage">
|
||||
<button class="btn btn-link btn-link-switch" @click="changeLanguage(item)">
|
||||
<img :src="languageMapper[item]" alt="freeleap zh">
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import zh_icon from '@/assets/lang/zh.png'
|
||||
import en_icon from '@/assets/lang/us.png'
|
||||
export default {
|
||||
name: 'LaguageSwitch',
|
||||
data() {
|
||||
return {
|
||||
languageList: ["zh", "en"],
|
||||
languageMapper: {
|
||||
"zh": zh_icon,
|
||||
"en": en_icon
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentLanguage() {
|
||||
return this.$store.getters['basic/language']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeLanguage(l) {
|
||||
this.$store.dispatch('basic/setLanguage', l)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.laguage-switch-container {
|
||||
display: inline;
|
||||
position: relative;
|
||||
|
||||
.btn-link-switch {
|
||||
padding: 0;
|
||||
width: 35px;
|
||||
line-height: 1;
|
||||
|
||||
img {width: 100%;}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
min-width: auto;
|
||||
margin-left: -5px !important;
|
||||
|
||||
.btn-link-switch {
|
||||
padding: 0 5px;
|
||||
width: 45px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -87,5 +87,8 @@ export default {
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
canvas {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -13,20 +13,22 @@
|
||||
>
|
||||
</div> -->
|
||||
<div class="public-sites">
|
||||
<button class="public-site-button" @click="gotoAbout()">About</button>
|
||||
<button class="public-site-button" @click="gotoBlogs()">Blogs</button>
|
||||
<button class="public-site-button" @click="gotoCareer()">Career</button>
|
||||
<button class="public-site-button" @click="gotoContact()">
|
||||
{{ getContactText }}
|
||||
</button>
|
||||
<button class="public-site-button" @click="gotoAbout()">{{ $t('About') }}</button>
|
||||
<button class="public-site-button" @click="gotoBlogs()">{{ $t('Blogs') }}</button>
|
||||
<button class="public-site-button" @click="gotoCareer()">{{ $t('Career') }}</button>
|
||||
<button class="public-site-button" @click="gotoContact()">{{ $t('Contact') }}</button>
|
||||
<laguage-switch />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import LaguageSwitch from '@/components/LaguageSwitch.vue'
|
||||
// import { i18n } from '@/lang'
|
||||
// const { t } = i18n.global
|
||||
export default {
|
||||
name: 'HeaderGuest',
|
||||
components: {},
|
||||
components: { LaguageSwitch },
|
||||
computed: {
|
||||
getContactText() {
|
||||
if (this.getRegion === 'CN') {
|
||||
@ -44,6 +46,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// translate(content) {
|
||||
// return i18n.global.t(content)
|
||||
// },
|
||||
gotoFrontdoor() {
|
||||
this.mnx_navToFrontDoor()
|
||||
},
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<div
|
||||
class="information-bar"
|
||||
@click="gotoMessages"
|
||||
:class="activePath == 'message' ? 'active' : ''"
|
||||
:class="{'active' : activePath == 'message', 'unread' : unreadCount > 0}"
|
||||
>
|
||||
<img alt="freeleaps logo" src="@/assets/message.png" />
|
||||
</div>
|
||||
@ -56,6 +56,7 @@
|
||||
Please go to profile page to add money receiving method
|
||||
</div>
|
||||
</div>
|
||||
<laguage-switch class="laguage-switch" />
|
||||
</div>
|
||||
<div class="profile-container">
|
||||
<img
|
||||
@ -87,15 +88,21 @@
|
||||
</template>
|
||||
<script>
|
||||
import { UserAuthApi } from '@/utils/backend/index'
|
||||
import LaguageSwitch from '@/components/LaguageSwitch.vue'
|
||||
|
||||
export default {
|
||||
name: 'HeaderGuest',
|
||||
components: {},
|
||||
computed: {},
|
||||
components: { LaguageSwitch },
|
||||
computed: {
|
||||
unreadCount() {
|
||||
return this.$store.getters['basic/unreadCount']
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.userIdentityNote.length > 8) {
|
||||
this.userIdentityNote = this.userIdentityNote.slice(0, 5) + '...'
|
||||
}
|
||||
this.$store.dispatch('basic/initWebsocket', this.mnx_getUserAuthToken())
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -157,6 +164,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.laguage-switch {
|
||||
margin-left: 30px;
|
||||
}
|
||||
.header-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -191,7 +201,7 @@ export default {
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
display: none;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
@ -201,6 +211,10 @@ export default {
|
||||
background-color: #f44837;
|
||||
}
|
||||
|
||||
&.unread::after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border: 1px solid $primary;
|
||||
}
|
||||
@ -244,7 +258,7 @@ export default {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
left: 0;
|
||||
bottom: -20px;
|
||||
bottom: -24px;
|
||||
font-size: 12px;
|
||||
color: #3d455f;
|
||||
white-space: nowrap;
|
||||
|
||||
6
frontend/src/lang/en.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
"About": "About",
|
||||
"Blogs": "Blogs",
|
||||
"Career": "Career",
|
||||
"Contact": "Contact",
|
||||
}
|
||||
32
frontend/src/lang/index.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import en from './en'
|
||||
import zh from './zh'
|
||||
const messages = { en, zh }
|
||||
|
||||
const getDefaultLocale = () => {
|
||||
let defaultLanguage = localStorage.getItem('language')
|
||||
if (defaultLanguage) {
|
||||
return defaultLanguage
|
||||
}
|
||||
const lan = (navigator.browserLanguage || navigator.language).toLowerCase()
|
||||
if (lan.indexOf('zh') > -1) {
|
||||
defaultLanguage = 'zh'
|
||||
} else if (lan.indexOf('en') > -1) {
|
||||
defaultLanguage = 'en'
|
||||
}
|
||||
return defaultLanguage || 'zh'
|
||||
}
|
||||
|
||||
const localeData = {
|
||||
globalInjection: true,
|
||||
legacy: false,
|
||||
locale: getDefaultLocale(),
|
||||
messages
|
||||
}
|
||||
|
||||
export const i18n = createI18n(localeData)
|
||||
export const setupI18n = {
|
||||
install(app) {
|
||||
app.use(i18n)
|
||||
}
|
||||
}
|
||||
3
frontend/src/lang/zh.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
"Contact": "联系我们"
|
||||
}
|
||||
@ -5,6 +5,7 @@ import { store, router } from './plugins/index'
|
||||
import { navigatorMixin, userIdentityMixin, errorHanlderMixin, userAuthMixin } from './mixins/index'
|
||||
import { tooltip } from './utils/index'
|
||||
|
||||
import { setupI18n } from '@/lang'
|
||||
/* import the fontawesome core */
|
||||
// import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
/* import font awesome icon component */
|
||||
@ -31,6 +32,7 @@ import { tooltip } from './utils/index'
|
||||
const app = createApp(App)
|
||||
app.use(store)
|
||||
app.use(router)
|
||||
app.use(setupI18n)
|
||||
app.mixin(userIdentityMixin)
|
||||
app.mixin(navigatorMixin)
|
||||
app.mixin(errorHanlderMixin)
|
||||
|
||||
@ -40,6 +40,7 @@ import {
|
||||
import { signinActionEnum } from '@/types/index'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
name: 'FrontDoor',
|
||||
props: {},
|
||||
data() {
|
||||
|
||||
@ -6,9 +6,10 @@
|
||||
:key="index"
|
||||
@click="view_link(directory)"
|
||||
>
|
||||
<p>{{ directory.title_text }}</p>
|
||||
<img class="directory_cover_image" :src="directory.cover_picture" />
|
||||
<p>{{ directory.summary_text }}</p>
|
||||
<p class="directory-title">{{ directory.title_text }}</p>
|
||||
<p class="directory-subtitle">{{ directory.summary_text }}</p>
|
||||
<button class="btn btn-link">Read More</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -46,13 +47,34 @@ export default {
|
||||
<style scoped lang="scss">
|
||||
.directories_containter {
|
||||
@extend .container;
|
||||
padding: 56px 40px;
|
||||
}
|
||||
|
||||
.directory_container {
|
||||
@extend .container;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
.btn-link {
|
||||
margin-bottom: 24px;
|
||||
text-decoration: underline;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.directory_cover_image {
|
||||
height: 20vh;
|
||||
width: 100%;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.directory-title {
|
||||
font-size: 48px;
|
||||
// font-weight: bold;
|
||||
color: #18181A;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.directory-subtitle {
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -48,14 +48,20 @@ export default {
|
||||
<style scoped lang="scss">
|
||||
.blogs_containter {
|
||||
@extend .container;
|
||||
padding: 56px 40px;
|
||||
}
|
||||
|
||||
.blog_containter {
|
||||
@extend .container;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
|
||||
h2, p {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.blog_cover_image {
|
||||
height: 20vh;
|
||||
height: 311px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
<template>
|
||||
<div class="directories_containter">
|
||||
<div class="career_containter">
|
||||
<div
|
||||
class="directory_container"
|
||||
class="career-item"
|
||||
v-for="(directory, index) in directories"
|
||||
:key="index"
|
||||
@click="view_link(directory)"
|
||||
>
|
||||
<p
|
||||
class="directory_title_txt"
|
||||
v-tooltip
|
||||
:title="directory.summary_text"
|
||||
delay='{"show":"500", "hide":"100"}'
|
||||
>
|
||||
<u>{{ directory.title_text }}</u>
|
||||
<p class="career-title">
|
||||
{{ directory.title_text }}
|
||||
</p>
|
||||
<p>{{ directory.summary_text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -48,15 +44,25 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.directories_containter {
|
||||
.career_containter {
|
||||
@extend .container;
|
||||
}
|
||||
max-width: 1000px;
|
||||
|
||||
.directory_container {
|
||||
@extend .container;
|
||||
.career-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
box-shadow: 0px 0px 24px 0px #D4D3E380;
|
||||
padding: 32px;
|
||||
color: #18181A;
|
||||
font-size: 14px;
|
||||
background-color: white;
|
||||
margin-bottom: 32px;
|
||||
text-align: left;
|
||||
|
||||
.directory_title_txt {
|
||||
p {margin: 0;}
|
||||
.career-title {
|
||||
font-size: 32px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
<template>
|
||||
<div class="directories_containter">
|
||||
<div class="directory_container" v-for="(directory, index) in directories" :key="index">
|
||||
<img class="directory_cover_image" :src="directory.cover_picture" />
|
||||
<div v-html="directory.content_html"></div>
|
||||
<div class="contact-container">
|
||||
<div class="contact-item" v-for="(directory, index) in directories" :key="index">
|
||||
<div class="contact-left">
|
||||
<img :src="directory.cover_picture" />
|
||||
<div class="contact-left-content">
|
||||
<p class="contact-title-text">{{ directory.title_text }}</p>
|
||||
<p>{{ directory.summary_text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-right" v-html="directory.content_html"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -34,6 +40,61 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.contact-container {
|
||||
display: flex;
|
||||
padding: 42px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 62px;
|
||||
|
||||
.contact-left {
|
||||
border-radius: 12px;
|
||||
background-color: $primary;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
width: 368px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
.contact-left-content {
|
||||
padding: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
color: white;
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
justify-content: center;
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.contact-title-text {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.contact-right {
|
||||
margin-left: 24px;
|
||||
box-shadow: 0px 0px 24px 0px #D4D3E380;
|
||||
background: white;
|
||||
padding: 32px;
|
||||
color: #18181A;
|
||||
border-radius: 12px;
|
||||
width: 368px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.directories_containter {
|
||||
@extend .container;
|
||||
}
|
||||
|
||||
@ -1,6 +1,148 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>History</p>
|
||||
<div class="history-container">
|
||||
<div class="history-content">
|
||||
<div class="accordion accordion-list" id="accordion-history-total">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="collapse-history-total" aria-expanded="false" aria-controls="collapse-history-total">
|
||||
<div class="history-bar dashed-container">
|
||||
<div class="history-bar-container">
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Total earning</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Paid</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Payable</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Total spending</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Deposit</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse-history-total" class="accordion-collapse collapse" data-bs-parent="#accordion-history-total">
|
||||
<div class="accordion-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion accordion-list" id="accordion-history-total">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="collapse-history-total" aria-expanded="false" aria-controls="collapse-history-total">
|
||||
<div class="history-bar dashed-container">
|
||||
<div class="history-bar-container">
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Summary</label>
|
||||
<p class="history-item-text">Need a python programmer</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Spending</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Updated</label>
|
||||
<p class="history-item-text">2023-10-19</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse-history-total" class="accordion-collapse collapse" data-bs-parent="#accordion-history-total">
|
||||
<div class="accordion-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="collapse-history-total" aria-expanded="false" aria-controls="collapse-history-total">
|
||||
<div class="history-bar dashed-container">
|
||||
<div class="history-bar-container">
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Summary</label>
|
||||
<p class="history-item-text">Need a python programmer</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Spending</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Updated</label>
|
||||
<p class="history-item-text">2023-10-19</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse-history-total" class="accordion-collapse collapse" data-bs-parent="#accordion-history-total">
|
||||
<div class="accordion-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="accordion accordion-list" id="accordion-history-earning">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-history-earning" aria-expanded="false" aria-controls="collapse-history-earning">
|
||||
<div class="history-bar dashed-container">
|
||||
<div class="history-bar-container">
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Summary</label>
|
||||
<p class="history-item-text">Need a python programmer</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Earining</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Updated</label>
|
||||
<p class="history-item-text">2023-10-19</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse-history-earning" class="accordion-collapse collapse" data-bs-parent="#accordion-history-earning">
|
||||
<div class="accordion-body">
|
||||
<div class="history-bar dashed-container">
|
||||
<div class="history-bar-container">
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Summary</label>
|
||||
<p class="history-item-text">Need a python programmer</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Earining</label>
|
||||
<p class="history-item-text">2000USD</p>
|
||||
</div>
|
||||
<div class="history-bar-item">
|
||||
<label class="history-item-label">Updated</label>
|
||||
<p class="history-item-text">2023-10-19</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -30,3 +172,43 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.history-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.history-content {
|
||||
width: 100%;
|
||||
max-width: $body-width;
|
||||
padding: 24px 0;
|
||||
|
||||
.history-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.history-bar-container {
|
||||
@extend .flex-row-container;
|
||||
@extend .flex-grow-1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.history-bar-item {
|
||||
@extend .text-start;
|
||||
@extend .flex-grow-1;
|
||||
|
||||
.history-item-label {
|
||||
@extend .label-text-light;
|
||||
}
|
||||
|
||||
.history-item-text {
|
||||
@extend .text-start;
|
||||
margin-bottom: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1008,8 +1008,8 @@ export default {
|
||||
|
||||
.project-item-text {
|
||||
@extend .text-start;
|
||||
@extend .mx-1;
|
||||
margin-bottom: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.project-request-container {
|
||||
@ -1067,7 +1067,6 @@ export default {
|
||||
|
||||
.request-proposal-content-container {
|
||||
@extend .container;
|
||||
@extend .border;
|
||||
}
|
||||
|
||||
.request-proposal-content {
|
||||
@ -1076,7 +1075,10 @@ export default {
|
||||
|
||||
.request-proposal-payment-plan {
|
||||
@extend .container;
|
||||
@extend .border;
|
||||
border: 1px dashed #9CB0F6;
|
||||
border-radius: 3px;
|
||||
padding: 24px;
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
.request-proposal-payment-plan-stage-container {
|
||||
|
||||
@ -147,21 +147,8 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="description-container">
|
||||
<!-- <div
|
||||
class="input-description"
|
||||
id="inputDescription"
|
||||
placeholder="Fill the request's description here"
|
||||
v-html="content"
|
||||
@keyup="textAreaAdjust($event)"
|
||||
@blur="descriptionDone($event)"
|
||||
contenteditable="true"
|
||||
/> -->
|
||||
<freeleaps-editor v-model:content="content" />
|
||||
</div>
|
||||
<!-- <div class="file-upload-container">
|
||||
<label for="file-upload" class="file-upload-label">Upload File:</label>
|
||||
<input type="file" id="file-upload" class="file-upload-input" @change="handleFileUpload" />
|
||||
</div> -->
|
||||
<div class="action-footer">
|
||||
<button class="cancel-button" @click="back">Cancel</button>
|
||||
<button class="submit-button" @click="submit">(Re)Submit</button>
|
||||
@ -347,9 +334,9 @@ export default {
|
||||
.request-issue-container {
|
||||
@extend .flex-colum-container;
|
||||
box-shadow: 0px 0px 24px 0px rgba(212, 211, 227, 0.5);
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: white
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
|
||||
75
frontend/src/plugins/store/basic.js
Normal file
@ -0,0 +1,75 @@
|
||||
/* eslint-disable no-prototype-builtins */
|
||||
import { i18n } from '@/lang'
|
||||
import { WsConnectionFactory } from '@/utils/backend/websocket'
|
||||
|
||||
const basicStore = {
|
||||
namespaced: true,
|
||||
state() {
|
||||
return {
|
||||
language: 'zh',
|
||||
unreadCountMapper: [],
|
||||
downstream_web_socket: null,
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
setLanguage(state, language) {
|
||||
state.language = language
|
||||
i18n.global.locale.value = language
|
||||
localStorage.setItem('language', language)
|
||||
},
|
||||
initWebsocket(state, token) {
|
||||
state.downstream_web_socket = WsConnectionFactory.CreateWebSocket(
|
||||
token,
|
||||
() => {
|
||||
// keep
|
||||
setInterval(() => {
|
||||
state.downstream_web_socket.send(1)
|
||||
}, 1000 * 60)
|
||||
console.log('downstream_web_socket open')
|
||||
},
|
||||
(e) => {
|
||||
const data = JSON.parse(e.data)
|
||||
let unread = state.unreadCountMapper[data.sender_id]
|
||||
if (unread) {
|
||||
unread ++
|
||||
} else {
|
||||
unread = 1
|
||||
}
|
||||
state.unreadCountMapper[data.sender_id] = unread
|
||||
console.log('downstream_web_socket onmessage: ', data, state.unreadCountMapper)
|
||||
},
|
||||
() => {
|
||||
console.log('downstream_web_socket error')
|
||||
},
|
||||
() => {
|
||||
console.log('downstream_web_socket closed')
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setLanguage(context, language) {
|
||||
context.commit('setLanguage', language)
|
||||
},
|
||||
initWebsocket(context, token) {
|
||||
context.commit('initWebsocket', token)
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
language(state) {
|
||||
return state.language
|
||||
},
|
||||
unreadCount(state) {
|
||||
let count = 0
|
||||
for (let key in state.unreadCountMapper) {
|
||||
if (state.unreadCountMapper.hasOwnProperty(key)) {
|
||||
count += state.unreadCountMapper[key]
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { basicStore }
|
||||
|
||||
@ -2,11 +2,13 @@
|
||||
import { createStore } from 'vuex'
|
||||
import { userAuthStore } from './userAuth'
|
||||
import { userRoleEnum, userProfileStore } from './userProfile'
|
||||
import { basicStore } from './basic'
|
||||
|
||||
const store = createStore({
|
||||
modules: {
|
||||
userAuth: userAuthStore,
|
||||
userProfile: userProfileStore
|
||||
userProfile: userProfileStore,
|
||||
basic: basicStore
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { userUtils } from '../store/index'
|
||||
// import { userUtils } from '../store/index'
|
||||
|
||||
class WsConnectionFactory {
|
||||
static CreateWebSocket(onOpen, onMessage, onError, onClose) {
|
||||
let jwt = userUtils.getJwtToken()
|
||||
static CreateWebSocket(jwt, onOpen, onMessage, onError, onClose) {
|
||||
// let jwt = userUtils.getJwtToken()
|
||||
this.socket = new WebSocket(`/ws/downstream/online_platform_notification?token=${jwt}`)
|
||||
this.socket.onopen = onOpen
|
||||
this.socket.onmessage = onMessage
|
||||
|
||||