This commit is contained in:
min.jiang 2024-06-25 06:03:11 +08:00
parent 97709a8e50
commit f868969bde
31 changed files with 595 additions and 66 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<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>Freeleaps</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@ -17,6 +17,7 @@
"pdfjs-dist": "^4.3.136", "pdfjs-dist": "^4.3.136",
"pinia": "^2.1.6", "pinia": "^2.1.6",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-i18n": "^9.13.1",
"vue-router": "^4.2.4", "vue-router": "^4.2.4",
"vuex": "^4.1.0" "vuex": "^4.1.0"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,5 +1,7 @@
<template> <template>
<div class="app-body"> <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"> <header class="body-header">
<router-view name="header"></router-view> <router-view name="header"></router-view>
</header> </header>
@ -34,6 +36,22 @@ export default {
margin-top: 0px; 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 { .body-header {
width: 100%; width: 100%;
height: $header-height; height: $header-height;

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -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

View File

@ -44,6 +44,10 @@ $header-height: 88px;
$footer-height: 110px; $footer-height: 110px;
$body-height: calc(100vh - $header-height - $footer-height); $body-height: calc(100vh - $header-height - $footer-height);
$body-width: 1288px; $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; // $default-font: "Lato", Verdana, Arial, sans-serif;
// $link-color: #ef899e; // $link-color: #ef899e;

View File

@ -179,6 +179,16 @@ p {
padding: 12px 28px 12px 12px; padding: 12px 28px 12px 12px;
outline: none; outline: none;
box-shadow: none !important; 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 { .dashed-container {
flex: 1; flex: 1;
margin-right: 28px; margin-right: 28px;
@ -191,7 +201,11 @@ p {
color: black; color: black;
background-color: transparent; background-color: transparent;
box-shadow: none; box-shadow: none;
border-bottom: 1px solid #dee2e6; // border-bottom: 1px solid #dee2e6;
&::before {
left: 0;
width: 100%;;
}
.dashed-container { .dashed-container {
background-color: #f3f6ff; background-color: #f3f6ff;
} }
@ -232,3 +246,11 @@ p {
display: flex; display: flex;
align-items: center; 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;
}

View File

@ -237,7 +237,6 @@ export default {
getSelect() { getSelect() {
if (window.getSelection) { if (window.getSelection) {
let sel = window.getSelection() let sel = window.getSelection()
console.log('this.selectedRange', sel)
if (sel.rangeCount > 0) { if (sel.rangeCount > 0) {
return sel.getRangeAt(0) return sel.getRangeAt(0)
} }
@ -290,6 +289,7 @@ export default {
default: default:
console.log('none') console.log('none')
} }
this.$refs.editor.blur()
// window.getSelection().removeAllRanges() // window.getSelection().removeAllRanges()
}, },
restoreSelection() { restoreSelection() {
@ -307,7 +307,9 @@ export default {
updateAction($event) { updateAction($event) {
let html = $event.target.innerHTML || '' let html = $event.target.innerHTML || ''
setTimeout(() => {
this.$emit('update:content', html) this.$emit('update:content', html)
});
} }
} }
} }

View 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>

View File

@ -87,5 +87,8 @@ export default {
right: 0; right: 0;
top: 0; top: 0;
} }
canvas {
width: 100%;
}
} }
</style> </style>

View File

@ -13,20 +13,22 @@
> >
</div> --> </div> -->
<div class="public-sites"> <div class="public-sites">
<button class="public-site-button" @click="gotoAbout()">About</button> <button class="public-site-button" @click="gotoAbout()">{{ $t('About') }}</button>
<button class="public-site-button" @click="gotoBlogs()">Blogs</button> <button class="public-site-button" @click="gotoBlogs()">{{ $t('Blogs') }}</button>
<button class="public-site-button" @click="gotoCareer()">Career</button> <button class="public-site-button" @click="gotoCareer()">{{ $t('Career') }}</button>
<button class="public-site-button" @click="gotoContact()"> <button class="public-site-button" @click="gotoContact()">{{ $t('Contact') }}</button>
{{ getContactText }} <laguage-switch />
</button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import LaguageSwitch from '@/components/LaguageSwitch.vue'
// import { i18n } from '@/lang'
// const { t } = i18n.global
export default { export default {
name: 'HeaderGuest', name: 'HeaderGuest',
components: {}, components: { LaguageSwitch },
computed: { computed: {
getContactText() { getContactText() {
if (this.getRegion === 'CN') { if (this.getRegion === 'CN') {
@ -44,6 +46,9 @@ export default {
} }
}, },
methods: { methods: {
// translate(content) {
// return i18n.global.t(content)
// },
gotoFrontdoor() { gotoFrontdoor() {
this.mnx_navToFrontDoor() this.mnx_navToFrontDoor()
}, },

View File

@ -4,7 +4,7 @@
<div <div
class="information-bar" class="information-bar"
@click="gotoMessages" @click="gotoMessages"
:class="activePath == 'message' ? 'active' : ''" :class="{'active' : activePath == 'message', 'unread' : unreadCount > 0}"
> >
<img alt="freeleaps logo" src="@/assets/message.png" /> <img alt="freeleaps logo" src="@/assets/message.png" />
</div> </div>
@ -56,6 +56,7 @@
Please go to profile page to add money receiving method Please go to profile page to add money receiving method
</div> </div>
</div> </div>
<laguage-switch class="laguage-switch" />
</div> </div>
<div class="profile-container"> <div class="profile-container">
<img <img
@ -87,15 +88,21 @@
</template> </template>
<script> <script>
import { UserAuthApi } from '@/utils/backend/index' import { UserAuthApi } from '@/utils/backend/index'
import LaguageSwitch from '@/components/LaguageSwitch.vue'
export default { export default {
name: 'HeaderGuest', name: 'HeaderGuest',
components: {}, components: { LaguageSwitch },
computed: {}, computed: {
unreadCount() {
return this.$store.getters['basic/unreadCount']
}
},
created() { created() {
if (this.userIdentityNote.length > 8) { if (this.userIdentityNote.length > 8) {
this.userIdentityNote = this.userIdentityNote.slice(0, 5) + '...' this.userIdentityNote = this.userIdentityNote.slice(0, 5) + '...'
} }
this.$store.dispatch('basic/initWebsocket', this.mnx_getUserAuthToken())
}, },
data() { data() {
return { return {
@ -157,6 +164,9 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.laguage-switch {
margin-left: 30px;
}
.header-container { .header-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -191,7 +201,7 @@ export default {
&::after { &::after {
content: ''; content: '';
display: block; display: none;
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 4px; border-radius: 4px;
@ -201,6 +211,10 @@ export default {
background-color: #f44837; background-color: #f44837;
} }
&.unread::after {
display: block;
}
&.active { &.active {
border: 1px solid $primary; border: 1px solid $primary;
} }
@ -244,7 +258,7 @@ export default {
position: absolute; position: absolute;
background-color: white; background-color: white;
left: 0; left: 0;
bottom: -20px; bottom: -24px;
font-size: 12px; font-size: 12px;
color: #3d455f; color: #3d455f;
white-space: nowrap; white-space: nowrap;

6
frontend/src/lang/en.js Normal file
View File

@ -0,0 +1,6 @@
export default {
"About": "About",
"Blogs": "Blogs",
"Career": "Career",
"Contact": "Contact",
}

View 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
View File

@ -0,0 +1,3 @@
export default {
"Contact": "联系我们"
}

View File

@ -5,6 +5,7 @@ import { store, router } from './plugins/index'
import { navigatorMixin, userIdentityMixin, errorHanlderMixin, userAuthMixin } from './mixins/index' import { navigatorMixin, userIdentityMixin, errorHanlderMixin, userAuthMixin } from './mixins/index'
import { tooltip } from './utils/index' import { tooltip } from './utils/index'
import { setupI18n } from '@/lang'
/* import the fontawesome core */ /* import the fontawesome core */
// import { library } from '@fortawesome/fontawesome-svg-core' // import { library } from '@fortawesome/fontawesome-svg-core'
/* import font awesome icon component */ /* import font awesome icon component */
@ -31,6 +32,7 @@ import { tooltip } from './utils/index'
const app = createApp(App) const app = createApp(App)
app.use(store) app.use(store)
app.use(router) app.use(router)
app.use(setupI18n)
app.mixin(userIdentityMixin) app.mixin(userIdentityMixin)
app.mixin(navigatorMixin) app.mixin(navigatorMixin)
app.mixin(errorHanlderMixin) app.mixin(errorHanlderMixin)

View File

@ -40,6 +40,7 @@ import {
import { signinActionEnum } from '@/types/index' import { signinActionEnum } from '@/types/index'
export default { export default {
components: {},
name: 'FrontDoor', name: 'FrontDoor',
props: {}, props: {},
data() { data() {

View File

@ -6,9 +6,10 @@
:key="index" :key="index"
@click="view_link(directory)" @click="view_link(directory)"
> >
<p>{{ directory.title_text }}</p>
<img class="directory_cover_image" :src="directory.cover_picture" /> <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>
</div> </div>
</template> </template>
@ -46,13 +47,34 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
.directories_containter { .directories_containter {
@extend .container; @extend .container;
padding: 56px 40px;
} }
.directory_container { .directory_container {
@extend .container; @extend .container;
padding: 0;
cursor: pointer; cursor: pointer;
text-align: left;
.btn-link {
margin-bottom: 24px;
text-decoration: underline;
padding: 0;
}
} }
.directory_cover_image { .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> </style>

View File

@ -48,14 +48,20 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
.blogs_containter { .blogs_containter {
@extend .container; @extend .container;
padding: 56px 40px;
} }
.blog_containter { .blog_containter {
@extend .container; @extend .container;
cursor: pointer; cursor: pointer;
padding: 0;
h2, p {
text-align: left;
}
} }
.blog_cover_image { .blog_cover_image {
height: 20vh; height: 311px;
} }
</style> </style>

View File

@ -1,19 +1,15 @@
<template> <template>
<div class="directories_containter"> <div class="career_containter">
<div <div
class="directory_container" class="career-item"
v-for="(directory, index) in directories" v-for="(directory, index) in directories"
:key="index" :key="index"
@click="view_link(directory)" @click="view_link(directory)"
> >
<p <p class="career-title">
class="directory_title_txt" {{ directory.title_text }}
v-tooltip
:title="directory.summary_text"
delay='{"show":"500", "hide":"100"}'
>
<u>{{ directory.title_text }}</u>
</p> </p>
<p>{{ directory.summary_text }}</p>
</div> </div>
</div> </div>
</template> </template>
@ -48,15 +44,25 @@ export default {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.directories_containter { .career_containter {
@extend .container; @extend .container;
} max-width: 1000px;
.directory_container { .career-item {
@extend .container;
cursor: pointer; 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> </style>

View File

@ -1,8 +1,14 @@
<template> <template>
<div class="directories_containter"> <div class="contact-container">
<div class="directory_container" v-for="(directory, index) in directories" :key="index"> <div class="contact-item" v-for="(directory, index) in directories" :key="index">
<img class="directory_cover_image" :src="directory.cover_picture" /> <div class="contact-left">
<div v-html="directory.content_html"></div> <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>
</div> </div>
</template> </template>
@ -34,6 +40,61 @@ export default {
} }
</script> </script>
<style scoped lang="scss"> <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 { .directories_containter {
@extend .container; @extend .container;
} }

View File

@ -1,6 +1,148 @@
<template> <template>
<div> <div class="history-container">
<p>History</p> <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> </div>
</template> </template>
@ -30,3 +172,43 @@ export default {
} }
} }
</script> </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>

View File

@ -1008,8 +1008,8 @@ export default {
.project-item-text { .project-item-text {
@extend .text-start; @extend .text-start;
@extend .mx-1;
margin-bottom: 0; margin-bottom: 0;
font-weight: bold;
} }
.project-request-container { .project-request-container {
@ -1067,7 +1067,6 @@ export default {
.request-proposal-content-container { .request-proposal-content-container {
@extend .container; @extend .container;
@extend .border;
} }
.request-proposal-content { .request-proposal-content {
@ -1076,7 +1075,10 @@ export default {
.request-proposal-payment-plan { .request-proposal-payment-plan {
@extend .container; @extend .container;
@extend .border; border: 1px dashed #9CB0F6;
border-radius: 3px;
padding: 24px;
margin: 32px 0;
} }
.request-proposal-payment-plan-stage-container { .request-proposal-payment-plan-stage-container {

View File

@ -147,21 +147,8 @@
</button> </button>
</div> </div>
<div class="description-container"> <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" /> <freeleaps-editor v-model:content="content" />
</div> </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"> <div class="action-footer">
<button class="cancel-button" @click="back">Cancel</button> <button class="cancel-button" @click="back">Cancel</button>
<button class="submit-button" @click="submit">(Re)Submit</button> <button class="submit-button" @click="submit">(Re)Submit</button>
@ -347,9 +334,9 @@ export default {
.request-issue-container { .request-issue-container {
@extend .flex-colum-container; @extend .flex-colum-container;
box-shadow: 0px 0px 24px 0px rgba(212, 211, 227, 0.5); box-shadow: 0px 0px 24px 0px rgba(212, 211, 227, 0.5);
border-top-left-radius: 12px; border-radius: 12px;
border-top-right-radius: 12px;
overflow: hidden; overflow: hidden;
background: white
} }
.action-bar { .action-bar {

View 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 }

View File

@ -2,11 +2,13 @@
import { createStore } from 'vuex' import { createStore } from 'vuex'
import { userAuthStore } from './userAuth' import { userAuthStore } from './userAuth'
import { userRoleEnum, userProfileStore } from './userProfile' import { userRoleEnum, userProfileStore } from './userProfile'
import { basicStore } from './basic'
const store = createStore({ const store = createStore({
modules: { modules: {
userAuth: userAuthStore, userAuth: userAuthStore,
userProfile: userProfileStore userProfile: userProfileStore,
basic: basicStore
} }
}) })

View File

@ -1,8 +1,8 @@
import { userUtils } from '../store/index' // import { userUtils } from '../store/index'
class WsConnectionFactory { class WsConnectionFactory {
static CreateWebSocket(onOpen, onMessage, onError, onClose) { static CreateWebSocket(jwt, onOpen, onMessage, onError, onClose) {
let jwt = userUtils.getJwtToken() // let jwt = userUtils.getJwtToken()
this.socket = new WebSocket(`/ws/downstream/online_platform_notification?token=${jwt}`) this.socket = new WebSocket(`/ws/downstream/online_platform_notification?token=${jwt}`)
this.socket.onopen = onOpen this.socket.onopen = onOpen
this.socket.onmessage = onMessage this.socket.onmessage = onMessage